-
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.
-
The Icons Behind The Notch
MenuTidy was the app I'd quietly filed away as 'done' — collapses your menu bar, cleans up clutter, ships. Today I added a feature I'd been trying to ignore for months: a way to actually click the status icons that the notch on MacBook Pros has politely hidden behind itself. I also fixed a position-memory bug that had been quietly lying to users for months.
-
Default ON Was The Wrong Default
I changed the rendering format of the standard “background pill” option of our menu-bar apps. I set the default to “on”, propagated deployment across eleven apps in a sweep, and then realised it was silently changing settings for upgraders who'd never touched the toggle. The pill that came shipping with everyone's next update was a small surprise nobody asked for. A short lesson in `UserDefaults.register`, the difference between “fresh install” and “user with no preference set”, and why reversing a convention across eleven repos in one go is sometimes the only honest move.