I build small apps, games, and tools.

I'm a former Apple engineer building independent apps, AI tools, and the occasional hardware experiment. I write about what works, what fails, and the code behind it.

Active Projects all projects →

The Documentation (Notes & Tactics) read all →

Sep 7, 2026

Dots N Bars (post-1.2.1 build) — I changed the plank to start at 2 segments and grow to 3 then 4 as score crosses fixed thresholds, instead of always being 4. I also added the project’s first test target — 31 unit tests covering the new scoring and matching rules, written as plain functions so they run without SpriteKit or a simulator.

Sep 7, 2026

Canopy build 9 (unreleased, pending merge) — Fixed a data-loss bug where an involuntarily interrupted recording (permission revoked mid-flight) could be silently wiped by the next tap of “Record,” and a trim-tool bug that let both handles land on the same point and save, collapsing the flight to a single point with no recovery path in the UI.

Sep 6, 2026

Dots N Bars (post-1.2.1 build) — Added a drop type that matches no color currently on the plank — touching it now ends the run, and letting it fall through is the correct play. While building this I found the existing plank-drag clamp was mathematically too narrow to ever dodge a drop (its radius was smaller than the plank’s own width), so I widened it.

Sep 5, 2026

Pixel Edit Lite v1.4.0 (build 12) — Sharing a sprite saved it as .com.apple.Foundation.NSItemProvider.P48iCO.png — the generic fallback name iOS assigns when a shared item has no filename of its own. I fixed it by switching from DataRepresentation to FileRepresentation, writing a real temp file first, so shares now save as PixelEdit-Sprite-8x8-2026-09-05-091600.png.

Sep 4, 2026

Pixel Edit Lite v1.4.0 (build 12) — New Sprite Sheet used to wipe the sprite sheet outright behind a confirm dialog. I changed it to silently save the outgoing sheet to disk via a security-scoped bookmark before clearing, so the action needs no confirmation now and nothing is lost — the same check runs before Import and before reopening a past file too.