Side project · Ballpark hot dogs, ranked
Gelbs Dogs
Steve Gelbs calls Mets games on SNY, and at every ballpark he visits, he rates the hot dog. Gelbs Dogs is the fan-made leaderboard those verdicts deserved.

01 · The elevator pitch
What it is
Gelbs Dogs keeps the running scoreboard of Steve Gelbs' ballpark hot dog ratings: a Top Dogs podium for the current leaders, a list that flips between most recent and highest scored, a fan vote on every entry, and the video clip to go with it when there's footage. Every rating is his; the site just gives them the leaderboard treatment they were always begging for.
It is a fan project and says so everywhere: the footer disclaims any affiliation with SNY or the Mets, and the same disclaimer is baked into the share image the site draws for social posts, so even the pixels carry it. One person runs it, and every score is entered by hand.
02 · The scoreboard
Two scores per dog
Every entry carries two numbers that never blend. The Gelbs score is his verdict, entered once and never recomputed. The Fans score is the crowd's running answer to a single question: do you agree? Agree and disagree tallies live in their own columns, so the crowd can push back all it wants without ever moving his number.
The crowd's number also plays hard to get: an entry shows a placeholder until you have cast your own vote, and only then reveals what everyone else thought. No peeking at the consensus before you commit. It is a small nudge that turns lurkers into voters.
Fans86% agree
The crowd's number unlocks after your ballot, not before
03 · The ballot box
Votes without accounts
There are no user accounts, and the schema has no votes table. Your ballot lives in your own browser: a cookie holding which way you voted on each dog, signed by the server with a secret key, the signature riding in a second cookie the page's scripts can't read. Edit the payload by hand and the signature stops matching, so the server treats the whole ballot as blank.
A repeat vote flips your ballot instead of stuffing it: the server subtracts the old direction and counts the new one in the same request. One browser, one standing vote per dog, with a soft rate limit behind it, sized honestly for the stakes: these are hot dog opinions.
04 · The postcard
The share card
Paste gelbsdogs.com into a chat and the link preview does something most links don't: it draws itself. The share image is a live route, not a file. Each request pulls the current top three from the same query the homepage uses, lays the standings over the artwork as JSX, and rasterizes the result. Share it today and it shows today's podium.
Two quiet tricks keep it fast and small: the background photo is read off disk and inlined as data, so no render ever waits on a network fetch, and the finished PNG is re-encoded to JPEG at quality 80 (the one job the native sharp library does here, and what keeps the route pinned to Node instead of the edge). The fan-made disclaimer ships inside the pixels, so the image can't travel without it.

05 · The film room
The clips
Each review carries its clip, and the clips dictated the architecture. Video files blow straight past the 4.5 MB request limit on Vercel's serverless functions, so uploads skip the server entirely: the browser pushes the file to storage itself, and the API only ever touches the resulting URL. Playback is a plain HTML5 video tag pointed at that file.
The poster frame is a trick of the URL: appending #t=0.001 asks the browser to seek a hair past zero, so the paused player shows a real first frame instead of a black box, and no thumbnail pipeline exists at all. Desktop expands the clip in place; a phone gets a bottom sheet that tracks your finger 1:1 and dismisses once you've dragged it far enough.
06 · War stories
Production stories
The portrait video that locked the exits
Three months in, a clip arrived shot vertically. The mobile player is a bottom sheet that slides up over the page, and the tall video stretched it past the screen, pushing the drag handle and the close button out of reach: anyone who opened that clip on a phone had no way to put it away. The fix was a height budget: the sheet caps at 90 percent of the viewport, the handle and header are pinned, and only the video itself scrolls.
The database that broke the build
Version one was a static site reading a JSON file, and the founding spec said exactly that: no backend, no database. The database arrived before lunch on day one, and the build promptly failed: static prerendering runs pages at build time, where no database exists. The fix (forcing the page to render per request) was added, reverted seven minutes later over worries about per-request queries, and re-added nineteen seconds after that when the build broke again. The commit log keeps the whole argument.
07 · The product
Screenshots
All of it is live at gelbsdogs.com: the podium, the tape, and the ballot. These captures landed hours after a new number one took the podium.


08 · The toolbox
Stack
- Frontend
- Next.js 16 (App Router), React 19, TypeScript, Tailwind CSS 4
- Data
- Prisma 7, Supabase Postgres
- Media
- Supabase Storage, plain HTML5 video
- Share card
- next/og (Satori), sharp
- Deploy
- Vercel (a standalone Docker build waits in the repo)
A one-day build that kept growing: the founding spec promised no backend and no database, and the site outgrew both before lunch (the original JSON file is still in the repo, the fossil record of version one). Even the lone recurring chore is scripted: adding a new dog is a short conversation with a coding agent that collects the stadium, the score, and the date, then seeds the database.
Postgame
See it live
The podium, the tape, and your ballot: fan-made hot dog justice, updated with every review.