-
Announcing Citadel: My Latest Native Mac Game
My second native game is out. Six weeks, forty-eight floors, one flamethrower, and a rule that quietly reorganises everything else: killing the thing that holds a floor is only half the job. Here's what Citadel is, what makes it strange, and every promise I broke to finish it.
-
Changelog: MenuTidy and ActiveSpace
A clutch of small, careful updates to two of my menu-bar tools. MenuTidy learned to tidy itself and made its peace with macOS Tahoe; ActiveSpace's space-aware app switcher finally makes it obvious which app you're about to land on.
-
When a Broken Link Is Not a Broken Link
I built a small dashboard to keep an eye on this website, and one morning it told me one of my own posts had a dead link — to Shadertoy, in the very post where I’d gushed about Shadertoy. Except the link works perfectly. Shadertoy just won’t talk to robots, and my robot took it personally. Here’s the small, careful feature I built so a tool can tell the difference between ‘broken’ and ‘I’ve checked, and it’s fine.’
-
The Volume Knob Was Winning
I’ve spent thirty years building a music library — pub bands, street buskers, support-act CDs that never reached a single streaming service, an almost-complete collection of a band who deleted themselves from history. All of it recorded at wildly different volumes, all of it a chore to actually listen to, because one hand was forever on the dial. So I built a small app that learns how loud each track really is and quietly evens them out — no audio driver, nothing to install. This is also the project the dancer was hiding in.
-
Nobody Dances Like That
A little app I’ve been building sprouted a music visualiser, and I decided it needed a dancer in the spotlight. So I drew one — head, arms, a swirling skirt — and she looked wrong in a way I couldn’t explain. Here’s how a black blob, a smoke machine, and a hard lesson about the human form taught me to make something feel alive by refusing to say what it is.
-
The Furniture Was a Lie
I filled my next game's rooms with bunks, lockers, oil drums and dead terminals to make the place feel lived-in. It looked wonderful. There was just one problem: you could walk straight through all of it. Here's how I made the set-dressing solid — without costing a single frame.
-
Every Level Is Winnable, and I Can Prove It
My next game builds its levels fresh every time you play — rooms, corridors, locked doors, hidden keys. Which raises a terrifying question: what if it builds one you physically can't finish? Here's how I stopped blindly trusting the generator and started proving it wrong.
-
I Tore Out the Renderer. Again.
When I built my first game I ripped out a renderer I loved and rebuilt the world from scratch. For the second one I did it again — only this time there are no models, no meshes, no geometry in the usual sense at all. Every wall and every enemy is an equation. Here's why I keep doing this to myself.
-
The Sound I Could Not Fake
I wrote a whole post bragging that my games ship no asset files — every pixel and every note generated in code, the lot small enough to fit on a floppy. Then I started the second one, and a monster in the dark made me break my own rule. Here's why sound was the one thing I couldn't synthesise, and how I broke the rule without betraying it.
-
One Television, One Kilobyte
Something set me thinking about the Sinclair ZX81 today — the first computer I ever touched, when I was twelve. The sublime manual, the housework I did to earn telly time, the tape that never loaded, the RAM pack that lost everything, and the unbridled joy of the first program that was properly mine.
-
Better Late Than Never
An afternoon with no business case whatsoever: I opened the dead issue tracker of a PHP library I’d kept frozen since 2014 — eighty-five issues, the newest a decade old — and cleared the lot. Not by mass-closing, but by reading every one, fixing the two that were genuinely still broken, and replying by hand to all eighty-five people who’d once filed into a void. This is the whole story: the humane method, the gnarliest bug (a one-character CSS mistake that survived ten years), and what a dead repo taught me about the actual shape of software maintenance.
-
My AI Had Amnesia, So I Built It a Memory
Claude can refactor a Metal renderer at 2am and forget the entire estate by morning. Here is how I gave it a permanent, self-maintaining memory of all of Jorvik Software — three layers, one local LLM running on my own machine, zero cloud, and a fistful of bugs that lied to my face along the way.
-
The Shortcut I Never Meant to Press
There is a key combination on every Mac that toggles display mirroring, it sits one slip away from shortcuts you use all day, and I kept hitting it by accident at exactly the wrong moment. So I built a tiny app to swallow it. The build had a twist: the key I was trying to block turned out not to be the key I thought it was.
-
Strataris: The Screensaver That Became a Game
Five months, a lot of swearing, the occasional actual tear, and a stubborn rule that the whole thing had to fit on a floppy disk. My first native game is finished. Here's the messy story of how it got here.
-
The Details Are the Software
Tugboat is an internal-only tool, used by exactly one person, and almost nobody will ever see its menu-bar icon. I reworked that icon anyway, so it shows — in real time — which edge of the screen the Dock is actually living on. This is a post about the smallest of details: why they matter, why they're the line between hand-crafted code and AI slop, and the other invisible little things scattered across the Jorvik suite.
-
The OS Upgrade Tax
Two apps, three Tahoe regressions, one afternoon — the price of shipping a macOS suite through an OS upgrade.
-
The Column I Couldn't Copy
Last week I needed to copy a single column out of an HTML table in an email, and discovered that the boring solution — copy the whole table, paste it somewhere, trim it back — is a fifteen-second tax on a one-second intention. So I built CopyLens. Draw a rectangle anywhere on screen; if there's text inside, it lands on the clipboard as text. If not, the cropped image does. Same gesture, two outcomes. Plus a full-disclosure note about how the OCR engine actually got built, which wasn't by me.
-
Rainy Day Isn't a Screensaver
Rainy Day shipped this past weekend. The product page calls it a screensaver. The bundle on disk is a regular .app — no .saver in sight. This is the story of why. A run-down of the macOS frictions that ended my attempt to build it the orthodox way: WebContent suspended within seconds of activation, an Apple SPI that no longer works in macOS 26, a CARenderer that returns blank frames, ScreenCaptureKit edge cases when the saver occludes the screen it's trying to read, multi-instance lifecycle thrash from System Settings previews, and the way ad-hoc signing burns TCC grants on every rebuild. And then the architecture I ended up with — LSUIElement app, SMAppService login item, idle-driven fullscreen NSWindow at screenSaver level — which side-stepped every one of those frictions in an afternoon.
-
Putting The House In Order
Two weeks of housekeeping on the Jorvik estate. It started with Spotlight launching the wrong version of an app, ended with a 1,100-line refactor of Release Manager, and surfaced about ten gigabytes of accumulated nonsense in between. This is the diary of the fortnight: what the audit found, what `ps` told me about my own apps, and why a clean filesystem is a clean countertop.
-
I Deleted Most Of My Release Manager
My Release Manager was a 1,600-line Swift pipeline that built, signed, notarised, packaged, and shipped every Jorvik app. Today it's a 770-line dispatcher, and the work it used to do happens in shell. This is the story of why I stopped reaching for Swift, and what I learned about treating the right tool as the obvious one even when it isn't the prestigious one.