VeePeenini, Part 1: The Idea and Day One
· Vitor Pontual · 4 min read
The idea showed up on a plane. I was flying back from Las Vegas on May 24 with a few empty hours and nothing to do, and I started sketching out a World Cup app for my friends. The tournament kicks off June 11, so I’d handed myself a little under three weeks. That’s exactly the kind of deadline that forces you to scope hard and skip everything that doesn’t matter.
The name is VeePeenini. It’s my handle stitched onto Panini, the company that made the sticker albums I traded with friends as a kid. That’s the whole idea, really: a private World Cup 2026 companion for my friend group of about a dozen people. Collect stickers, predict matches, trade duplicates, and watch games together in a shared lounge. No public signups, no growth funnel, no ads. Just us.
I own the idea, the scope, the priorities, the UX calls, and the launch decisions. Claude 4.7 did the actual building and wrote most of the code, fast. I directed it, reviewed the architecture, pushed back on choices I didn’t like, and made the calls that had real consequences. I can work through this stuff myself, just a lot slower, so I knew what I was signing off on. The split was simple: it built, I decided what we were building and why, and I stayed paranoid about the things that would quietly go wrong.
The product, before any code
Before writing anything, I fixed the four things the app had to do well:
- Collect. Open packs, get player and team stickers, paste them into an album. This is the nostalgia hook, so it had to feel good, not like filling out a form.
- Predict. Call the scores and the bracket. This is the competition that keeps people coming back match after match.
- Trade. Swap your duplicates with friends. A sticker album is only fun if you can hunt the ones you’re missing.
- Watch together. A live lounge during matches so the group is in the same room even when we’re scattered across cities.
Everything else was negotiable. Those four were not.
Where it ended up: the dashboard, with the daily pack claim, the next-match countdown, and your rank at a glance.
The stack, and the one call that mattered
The build is Next.js 16 on the App Router, Tailwind 4, Drizzle for the database layer, and Postgres 17. Auth through NextAuth. It runs on a small server at home behind a Cloudflare Tunnel, so my friends get a normal https:// link and I get to keep the whole thing on hardware I own.
The decision I cared most about on day one was the live lounge. The easy path is to reach for a hosted realtime service. I didn’t want a third-party dependency, a per-message bill, or my friends’ chat sitting on someone else’s servers for a six-week project. Postgres can already do this: LISTEN/NOTIFY to broadcast events, server-sent events to push them to the browser. So that’s what we built. No Supabase, no Pusher. It’s a slightly more hands-on path, and I signed off on it knowing that. The entire presence broadcast is one line of SQL:
// file: src/app/api/lounge/[matchId]/stream/route.ts
async function broadcastPresence(channel: string, viewers: number) {
const payload = JSON.stringify({ type: "presence", viewers, at: new Date().toISOString() });
await db.execute(sql`SELECT pg_notify(${channel}, ${payload})`);
}
Zero to deployed in fourteen commits
Day one was one long session, phased so each piece stood on its own before the next went in:
- Foundations and the database schema for teams and players.
- The pack drop, the 3D card flip, and a static roster to test against.
- Fixtures, the bracket, predictions, and the grading logic that scores them.
- The trading post: create, accept, decline, cancel, with an atomic settle so two people can’t both walk away with the same card.
- An admin panel so I could enter match results and manage the group.
- The live lounge, running on the
LISTEN/NOTIFYplumbing. - Polish: Giphy in the lounge, PWA icons so it installs on a phone, a PDF export of the album.
- Production deploy to the home server.
By the end of the day the thing was live on the internet, behind a login, with the four pillars all present in rough form. None of it was pretty yet. The album was a plain grid, the stickers were placeholders, and I was the only user. But the shape was real, and the shape is the hard part to get right.
The next two weeks were about closing the distance between “it runs” and “I’d actually show this to my friends.” That’s where it got interesting, and where most of the real product work happened. More on that in Part 2.