Issue №127 Summer 2026A journal of dev tools, libraries & small ideasUpdated weekly
devops · 4 min read
devopsJune 12, 2026 · 4 min

Gotham Garage

A personal-collection-first PWA for diecast collectors: scan a UPC or photo-match a loose car against your own library — so you never double-buy.

Gotham Garage — diecast collection catalog
01
devops

Gotham Garage: a phone-first catalog for diecast collectors

I have a shoebox of small toy cars and a real problem: I buy duplicates. The official solutions are nice but closed — the big hobby database charges $1,200 to set up an API key, and the visual-classification problem (point a phone at any toy car, get the exact casting/year/series) is genuinely unsolved. There are 10,000+ castings, each released in many color variants under different lighting, and no public dataset exists.

So I reframed the problem in a way that's actually tractable.

The reframe

I don't need to identify against the global 10,000+ casting universe. I need to identify against MY collection. If I've already photographed a car and saved it, the question isn't "which casting is this in the entire diecast universe?" — it's "is this any of the ones I already own?"

That's a retrieval problem, not a classification problem. And retrieval + human pick is something the web does extremely well.

What I built

Gotham Garage is a PWA — installs to your home screen, works offline for browsing. Two add paths:

For packaged cars: scan the UPC on the back of the card. The app calls upcitemdb, caches the response forever (UPCs don't change their metadata), shows you what it found, and one tap saves it.

For loose cars: take a top-down photo. The app removes the background (@imgly/background-removal running ONNX in the browser), embeds the result with DINOv2-small (via transformers.js), and computes cosine similarity against the embeddings of cars already in your library. You see the top 12 as big tappable cards. Tap a match — or add as new.

Both paths run almost entirely in your browser. The model weights are lazy-loaded on first use and cached by the service worker. Photos never leave your device until you save them. Metadata syncs to a personal MongoDB.

Two decisions I'm happy with

Top-K retrieval, not top-1 classification. Existing collector apps that try to be smart about visual ID either get top-1 wrong frequently or refuse to commit. The top-K UX absorbs imperfect ranking gracefully — you're picking from 12 thumbnails of your own photos. The model can be wrong about ranking and the experience still feels great. As you confirm matches, your library accumulates photos taken in your actual lighting, which makes future matches better.

DINOv2 over CLIP. CLIP clusters by concept ("red sports car"). DINOv2 is trained on image-to-image similarity — same object under different lighting and angles — which is exactly the diecast use case. It's also small enough to ship to a phone browser: the small variant is ~30 MB, and the embedding is a 384-dim Float32. For a personal library of a few hundred cars, brute-force cosine similarity in the browser is sub-millisecond.

What the stack looks like

Next.js 14 (App Router) + TypeScript + Tailwind. MongoDB on Railway for the metadata. Vercel Blob for the photos. Auth.js for magic-link email auth. @zxing/browser for the barcode scanner. next-pwa for the service worker.

The whole thing fits comfortably in Vercel's free tier for personal use.

Try it (and fork it)

Gotham Garage is open source, MIT-licensed. The repo is at https://github.com/kea0811/gotham-garage. The README has the deploy steps if you want to run your own instance (you'll need a Mongo URI, a Vercel Blob token, and an email provider for magic links).

What's next

A few things on my list: multi-angle capture (top-down + side → averaged embedding), a Fandom-wiki-backed reference catalog as a fallback when your library has no match, CSV export, and a read-only public share link so you can show your collection without making someone install the app.

If you collect diecast cars — or you're just curious about a small PWA with browser-side ML — I'd love to hear what's missing.


End of essay

About the author
Er An Khoo