All projects
01 / 05 Flagship Case study

CarSpotter

A daily car-spotting game — five cropped details, four answers, ten seconds. Designed, engineered, and shipped solo for iOS.

Role
Sole designer, engineer & founder
Period
2026
Scope
iOS game, durable content pipeline, shared design system, marketing site
Status
iOS · preparing App Store submission
Focus
  • Daily game loop
  • Content-addressed media pipeline
  • End-to-end solo build
Stack
  • Expo / React Native
  • TypeScript
  • Zustand
  • MMKV
  • Turborepo monorepo
  • Astro
  • ImageMagick pipeline
02 Outcomes at a glance
  • Daily 5 Cars a day
  • 12 Detail categories
  • 234 Tests on the engine
  • One person Designed and built by
CarSpotter portfolio poster — tilted iPhone screenshots of the daily car-detail guessing game on a dark garage backdrop, lime accents, the line "Guess the car from the detail."
03

The premise

Every enthusiast has done it. A single headlight in traffic, a quarter of a grille, the shape of a wheel arch — and you already know the car. CarSpotter is that instinct turned into a daily game.

Each day is a Daily 5: five cropped details of real cars, four answers each, ten seconds a clue. Same five for everyone. Score, rank up, hold a streak, and brag without spoiling it. It’s a Wordle-shaped daily for the people who can name a chassis code from a single taillight.

The Daily 5

The whole loop is built to be felt in under a minute. A detail locks into a lime viewfinder, a tachometer drains the ten seconds, four arcade buttons wait in the thumb zone. You pick, the crop blooms out to the whole car, and a one-line fact lands. Five times, then you’re done until tomorrow.

The constraints are the design. Everyone gets the same five, so bragging rights are earned, not bought. Practice is XP-capped, so the leaderboard can’t be farmed. The answers are real model names and real chassis codes — no trick questions, no AI mush, no filler.

Brag without spoiling

Sharing is the growth loop, so it had to be spoiler-free by construction. One tap turns a run into a card of colored squares and a clock — no car names, no photos, nothing that ruins the round for the group chat. It mirrors the in-app result exactly, down to the per-clue letters and the run tier, so the flex is honest.

The reveal — and the bug that almost shipped

The best second in the app is the reveal: the cropped detail zooms out to the whole car. Preparing the first App Store build, I found that second was broken for every authored day — the clue played, then the reveal bloomed to nothing.

The cause was quiet and dangerous. The studio I author puzzles in had stored each reveal photo as an absolute file://…/Application/<container-UUID>/… path. iOS regenerates that container UUID on every reinstall, so every stored path silently dangled. The crops had survived only by accident — the shipping export happened to inline them as base64 — while the reveal originals, stored the exact same way, were never captured anywhere. A whole schedule shipped with working clues and empty reveals.

It was a data-loss class of bug, not a one-off. So I rebuilt the pipeline to make it impossible to repeat.

A content pipeline that can’t lose your work

The rule now: never persist an absolute path. Media is content-addressed — every photo lives under the SHA-256 of its own bytes, the manifest holds stable keys, and the real URI is rebased against the document directory at runtime. The presence of a hash file is the integrity proof. Writes are atomic: download to a temp file, verify size and hash, then move into place.

Around that sits a recovery pipeline and a set of gates:

  • Recovery that runs on any machine. Every reveal can be re-derived by priority — a committed in-repo bake first, then a local raw cache, then the original source fetched by ID and re-verified against its dimensions and hash. Run against the full schedule, it recovered 374 of 375 reveals.
  • A bundler that fails loud. The launch bundler used to silently skip any puzzle whose reveal wouldn’t resolve. Now it ships complete crop-and-reveal pairs only, asserts every scheduled day is a full five, and exits non-zero on anything missing.
  • Referential integrity gates. A pure checker runs over the authored content for dangling photo references, half-built reveal pairs, and orphans — and the same gate guards the studio, the export, and the build.

Exports are self-contained now: every crop and every reveal travels as bytes, so reinstalling a phone or moving to a new laptop can’t orphan a thing. The engine carries 234 tests, and the parts that lose data when they’re wrong carry the most of them.

One person, end to end

The design, the iOS app, the game engine, the content pipeline, and the marketing site are one person’s work, in a Turborepo monorepo. The rules of the game live in a pure-TypeScript game-core package with no React anywhere near them, which makes them trivial to test. The design tokens are a shared package the app and the website both compile from, so the brand can’t drift between the two. Even the App Store screenshots and the poster on this page come out of a scripted ImageMagick pipeline, versioned next to the code.

A small game, built on a serious spine — so the fun part never breaks.

One detail. Ten seconds. Then the whole car.

The moment that hooks you
04 The Daily 5

A clue, a clock, and four ways to be wrong.

The loop is built to be felt in under a minute. A cropped detail locks into a lime viewfinder, the tachometer drains ten seconds, and four arcade buttons wait in the thumb zone. Pick, and the crop blooms out to the whole car — same five for everyone, every day.

CarSpotter clue screen: a tightly cropped car detail inside a lime viewfinder, a draining ten-second tachometer, and four answer buttons — Honda S2000, Toyota Supra, Mazda MX-5, Toyota MR2.
The clue: one cropped detail, a ten-second clock, four chassis to choose from. Tap before the tach hits zero.
CarSpotter reveal screen: the cropped detail has zoomed out to the full car, with the model named and a one-line fact beneath it.
The reveal. The crop zooms out to the whole car and the fact lands — the one second the entire app is built around.
CarSpotter home: today's Daily 5 with the five clue slots and a streak counter.
A fresh Daily 5 every morning. Five clues, the same five for everyone, and a streak to keep alive.
CarSpotter result screen: a run score with a combo multiplier and a rank.
Consecutive hits stack a combo. The run resolves to a score, a tier, and a rank you actually earned.
05 The flex, and the teach

Brag without spoiling. Miss without losing.

Sharing is the growth loop, so it's spoiler-free by construction — colored squares and a clock, never a car name. And every miss teaches the tell, so the detail you blow today is the one you nail on sight tomorrow.

CarSpotter share card: a grid of colored squares and a clock, with no car names — safe to drop in a group chat.
The share card mirrors the in-app result exactly — squares, letters, a clock, a tier — but never a car name. The flex is honest and the round stays unspoiled.
CarSpotter miss state: the car you didn't get, with the detail to look for next time.
Miss one and it shows you the tell. The game is quietly building detail literacy under the arcade.
CarSpotter clue: a different cropped detail — a wheel — framed under the viewfinder with four answers.
Twelve detail categories — headlight, taillight, grille, wheel, badge, and more. The crop is the whole puzzle.
CarSpotter reveal: a second full-car reveal with the model named and a fact.
Real cars, real chassis codes, real facts. No trick questions, no AI mush, no filler.

The clue stays secret, the reveal is the payoff, and the share card protects both. Every surface is designed around one rule: never spoil the round you want someone else to play.

06 The brand surface

One brand, generated end to end.

The marketing poster, the App Store screenshots, and the game all speak the same midnight-arcade language. The design tokens are a shared package the app and the website both compile from — and the promo art comes out of a scripted ImageMagick pipeline, versioned next to the code.

CarSpotter portfolio poster: a fan of tilted iPhone screenshots on a dark garage backdrop with lime accents and the line 'Guess the car from the detail.'
The poster, the screenshots, and the brand are scripted artifacts — reproducible, on-brand, and impossible to drift from the app they sell.