Changelog: ASCII Saver 2.0

A hand ticking items off a checklist.

Photo by Jakub Żerdzicki on Unsplash

ASCII Saver renders your camera feed as live ASCII art. It has done since March. Version 2.0 doesn’t change a single pixel of what it draws — and it’s the biggest release the thing has had, because it changes what it is.

It used to be a screensaver. Now it’s an app. Here’s why that was the only sane direction, and what fell out of the sky when it moved.

It stopped being a screensaver

macOS screensavers are plugins. Your .saver bundle doesn’t run as itself — it’s loaded into a system process called legacyScreenSaver, which owns everything about that process, including its privacy permissions.

Which is fine, right up until your screensaver needs the camera. Then it’s fatal, because the camera permission belongs to Apple’s process, not to yours. There is no API to ask for it. There is no entitlement that helps. You simply cannot have it.

So version 1 did the only thing available: it shipped a second application whose entire purpose in life was to be a normal app that could hold a camera permission. That app opened the camera, converted frames to greyscale, and handed them to the screensaver. The screensaver drew them.

Version 2 is one ordinary app that opens the camera itself. That’s the whole change. Everything below is a consequence of it.

What went away

I want to list this out, because the size of it is the point:

Roughly four hundred lines of inter-process plumbing, plus an entire second build target. None of it did anything a user ever wanted. All of it existed to route around one restriction — and the restriction had no route around it, only a toll.

The renderer, meanwhile, barely moved. It had always taken frames from a struct with a particular shape; the new code hands it the same shape from memory instead of from a file. Two lines changed in nine hundred.

It can update itself now

For the first time ever, ASCII Saver has automatic updates.

This isn’t an oversight I finally got round to. A .saver has no process of its own — nothing runs in the background, nothing is alive to check a feed, nothing can present you with a dialogue. Version 1 could not have had an updater. If you’ve been running it since March, you’ve been running March’s build, and neither of us had any way to change that.

2.0 uses Sparkle, like the rest of the suite, signed so your copy only ever installs genuine releases. It’ll check daily and tell you.

And if you’d rather manage it from the terminal, it’s on Homebrew as of yesterday:

brew install --cask perpetualbeta/jorvik/asciisaver

That got simpler too. The cask used to have to drive the two-component installer; now it just moves an app.

Settings that aren’t hidden in System Settings

Configuration used to mean opening System Settings, finding ASCII Saver in the screensaver list, and right-clicking the preview to reveal an options panel. If you’ve never discovered that you could right-click a screensaver preview, you are not unusual.

There’s now a menu bar icon and a Settings… window, laid out like every other Jorvik app: camera permission status at the top, then activation, then the picture, orientation and effects. All eleven options that were in the old panel are there, plus the new ones. Changes apply to a running saver immediately — no Save button, nothing to restart.

Three things it picked up

Because it’s an app now, a few things became easy that were previously impossible:

Two things to know before you upgrade

I’d rather tell you than have you find out.

You’ll be asked for camera access again. The app’s identifier changed to match the rest of the suite, and macOS ties permissions to the identifier. There is no way to carry a grant across — not a limitation of mine, a deliberate one of Apple’s, and a correct one. Grant it once more and you’re done.

It won’t appear in System Settings → Screen Saver any more. It isn’t a screensaver bundle, so it can’t. Launch the app once instead; it takes over on idle exactly as before, and can start itself at login. That’s the real cost of this release, and I think it’s worth paying.

Your settings do carry over — colour filter, character size, frame rate, rotation, mirroring, all four effects. That very nearly wasn’t true. The screensaver API for saving preferences looks like it writes to an ordinary place, and doesn’t: it buries them inside the system screensaver host’s private container, under a filename containing your Mac’s hardware ID. My first migration looked in the obvious place, found nothing, reported no error, and would have shipped feeling perfectly correct while quietly resetting every single user to factory defaults. I only caught it because I went to check my own settings before deleting the old install, and they weren’t where I’d just written the code to look.

The old files aren’t removed for you. Delete ASCIISaver.saver from your Screen Savers folder and ASCIISaverCameraAgent.app from Applications whenever you like.

One bug worth confessing

There was a stretch where the picture kept breaking up into a strange pattern, a second or two at a time, then coming right again.

Nothing was wrong with the renderer. When the camera hands over a frame it hands over a pointer, valid only for that instant, and I was copying each frame and then scheduling the copy to be freed on the main thread. But the renderer doesn’t run on the main thread — it runs on its own display clock. So the main thread was handing memory back to the system while the renderer was still reading it, and whatever landed there next got drawn as characters. You were watching some other part of the program, rendered as ASCII art.

It didn’t happen every time, which is the tell. A logic error is faithful; it breaks identically on every run. A memory race depends on who gets there first, so it looks like bad luck until you stop believing in bad luck.

The theme, if there is one

Every awkward thing about ASCII Saver — the second app, the shared buffer, the duplicated settings file, the two-part installer, the fact it could never update itself — came from one decision made early: that a screensaver should ship as a screensaver.

That looks obviously right. It was obviously right for Reverie, which is plain, ordinary Mac drawing and was never a problem. It was wrong for ASCII Saver, because that product needs a camera, and the one thing a screensaver bundle can never do is hold a camera permission.

I had even written the rule down for myself, in my own notes, listing this app as a case where the screensaver bundle was the correct choice. The note was right about the rendering and wrong about the app, and I believed it for months because it was nearly right, which is the most expensive kind of wrong there is.

Shipping now, via the download page or Homebrew, whichever you prefer.