← All projects
Personal · Shipped in a weekend · 2026 · … min read

Wedding Picks — Tinder for ~6,000 wedding photos

My family had ~6,000 photos sitting in a Google Drive folder after a wedding and no realistic plan to pick favorites. Importing them all into a desktop tool was painful. So I built the laziest possible thing that worked.

FastAPI Google Drive API Vanilla JS SQLite Tailscale

The constraint

Three constraints pinned the design before I wrote a line of code:

  • No re-uploads. 6,000 photos at ~5 MB each is 30 GB. The photos already live in Drive — copying them anywhere else would be the most expensive step in the whole flow.
  • Resumable. Family members were going to do this in 15-minute chunks across days. Losing progress on a refresh was unacceptable.
  • Phone-first. Everyone was going to use it on a phone, sitting on a couch. Tinder's swipe UX is the right primitive — keep, discard, or "yes please" — because it's literally what people are already doing in their heads.

What I built

  • FastAPI backend with two surfaces: a /photos endpoint that streams the next photo straight from Google Drive (no local copy), and a /verdict endpoint that records keep/discard/favorite into SQLite.
  • Google Drive API with a service-account credential — the backend opens an authenticated stream to the file and pipes it to the client. Drive does the bandwidth, my server does the routing. Bytes never hit my disk.
  • Vanilla JS on the frontend — three swipe gestures (left = discard, right = keep, up = favorite), preloads the next two images so swipes feel instant, persists the cursor in localStorage so refreshing doesn't lose your place.
  • SQLite verdict log, one row per photo per user — supports multiple family members reviewing in parallel without conflicts.
  • Tailscale exposed it inside the family network on port :8002. No public hosting, no ad-blockers, no auth grief.

Architecture highlights

~6,000
photos sorted, zero re-uploads
0 GB
local disk used (Drive streams through)
2 days
spec → working tool, on the couch
3
family members reviewing in parallel

What I learned

The right answer to "I have 30 GB of media and a UX problem" is almost never "build storage." It's "stream the bytes you already have and put a tiny database next to the verdicts." This is the same lesson that shows up everywhere applied AI lives: the model isn't the bottleneck, the data plumbing is.

The other lesson: weekend tools should look like weekend tools, not like products. I spent zero minutes on a logo, no time on settings, no time on accounts. The whole thing fit in three files. It worked the first time someone tried it because there was nothing in the way.

The point of a portfolio isn't only the load-bearing flagship projects. It's also the "I had a problem on Saturday morning and shipped a tool by Sunday night" projects, because that's the actual signal of how someone works.

Stack

Python · FastAPI · Google Drive API · SQLite · Vanilla JS · Tailscale