The devlog
Stories from actually building these apps, written from the build records. Real bugs, real numbers, honest dead ends. No tutorials, no filler.
Deriving racing truth from a free, undocumented Formula 1 data feed.
Building a live World Cup match centre on a public feed full of quirks.
Desktop infrastructure: Electron, process supervision, and the Windows wallpaper layer.
Bugs that earned a full write-up, and what they taught.
Never hash JSON.stringify for identity
My process launcher decided whether a running app was the one it knew by hashing its config. Reordering two keys in that config, with no change in meaning, made it kill a perfectly healthy process on the next boot.
Postmortems11 July 2026 · 3 minThe overlaps a screenshot hides
A layout can look fine in a screenshot while one element sits 121 pixels on top of another. The fix was to stop looking at pictures and start measuring bounding boxes, and to always test the widest and shortest screens where the bugs actually live.
Postmortems11 July 2026 · 3 minThe panel that was empty for all 48 teams
Every team page in my World Cup tracker had a record panel that showed nothing, and nothing complained. The bug was a contract mismatch: the frontend read a field the backend never populated, and a missing field is invisible to almost every guard you have.
Engine Room10 July 2026 · 4 minThe backend a free trial took offline
A free trial ended and took my World Cup tracker's backend down for days during the knockouts. The fix was to stop having a separate backend at all, and fold the data layer into the Next app so there is one host and nothing left to lapse.
Engine Room5 July 2026 · 6 minBuilding a Steam client for my own software
Three overlapping desktop apps became one small kernel with a single manifest schema. Why no runnable type gets special treatment, and the deletion test that decided when the kernel was real.
Telemetry3 July 2026 · 5 minA centreline the cars drew, and lap times within a second of the feed
F1's free feed gives car positions as noisy XY samples and lap times from hardware in the tarmac. I projected every sample onto a derived centreline as arc length plus lateral offset, computed lap times from geometry alone, and checked them against the feed's own clocks.
Postmortems3 July 2026 · 4 minA hydration mismatch hiding in the 14th decimal place
React said my server HTML didn't match the client. The diff was two numbers that agreed to thirteen significant digits. The story of why Math.sqrt is allowed to do that.
Matchday Data3 July 2026 · 6 minA radial knockout bracket, and the draw that refused to pair neighbours
I rebuilt the WC26 knockout bracket as a radial fan chart. The curves were the easy part. The real draw pairs non-adjacent matches, losers deserve a visible line too, and one wrong ARIA role quietly flattened 62 interactive nodes into a single image.
Telemetry3 July 2026 · 5 minDeriving lateral G from a feed that doesn't have it
F1's free timing feed carries five telemetry channels and none of them is cornering force. I derived lateral G from speed and line curvature, added throttle and braking stats, and checked the whole pipeline against the one number the feed also publishes.
Telemetry3 July 2026 · 6 minDrawing an F1 circuit from nothing but where the cars drove
F1Peak ships no hand-drawn track maps. Every circuit outline is computed from the cars' own position data, as a median over many resampled laps, with dirty laps rejected and the start/finish seam parked next to the pit lane.
Telemetry3 July 2026 · 5 minNo curvature threshold can number Monaco
F1Peak derived corner numbers from track curvature, and Monaco broke it in both directions at once - a 25% dead zone or 39 phantom corners, depending on the threshold. The fix was to admit that official turn numbers are a human list, and to snap that list to the real apexes.
Matchday Data3 July 2026 · 5 minQuirks are law: building a World Cup tracker on a free ESPN feed
The free ESPN feed behind WC26 pre-rounds its percentages, counts its clock in seconds, and silently caps a ranged schedule request at 100 events. Every quirk I found became a hard rule in the code, because the feed was never going to change.
Telemetry3 July 2026 · 5 minSmoothing 3.7 Hz GPS into 60 fps car motion
F1's position feed reports each car under four times a second, with up to two metres of noise. How F1Peak turns that into fluid on-track motion with centripetal Catmull-Rom splines and dead reckoning, and how I proved the result with heading statistics instead of a feeling.
Engine Room3 July 2026 · 6 minStop polling. The app already knows it's ready
My desktop engine's HTTP ready probe was killing a healthy server for answering slowly. I replaced pull-based health checks with a push rail - a loopback-only listener, a per-launch token, and a client library that silently no-ops when the engine didn't launch the app.
Telemetry3 July 2026 · 6 minThe free data path behind Formula 1's live timing
F1's official live-timing player is fed by a public static file tree plus a SignalR socket, most of it reachable without a key. The recipe from building F1Peak, including the BOMs, the raw-deflate payloads, the per-topic auth, and the 30-minute publication delay.
Postmortems3 July 2026 · 6 minThe junction that broke every launch, twice
I moved one project to an SSD and left a directory junction behind. Node canonicalised paths through it, every sibling launch in my desktop engine died with ENOENT, and my first fix quietly failed in the packaged exe depending on how you double-clicked it.
Postmortems3 July 2026 · 5 minThe readiness probe that kept killing a healthy app
My desktop engine kept declaring a healthy Next.js app dead and killing it. The probe's hardcoded 1.5 second per-attempt cap sat below the app's real render time, so every attempt failed against a server that was answering 200s.
Matchday Data3 July 2026 · 6 minThe shot map ESPN was handing us for free
My World Cup tracker's plan said shot coordinates were impossible without faking data. Then I read the raw feed and found real pitch coordinates and kick-by-kick shootout data, fetched on every request and thrown away by my own normalizer.
Engine Room3 July 2026 · 5 minThe wallpaper is just another window
The recipe for rendering an Electron app as the live Windows 11 wallpaper, from the WorkerW window layer and click-through widgets to the day the ZQ Engine absorbed the whole trick as a tenant.








