I built BingWow, a free multiplayer bingo platform. Players open a link, get a uniquely shuffled board, and play together in real time. No app download, no account creation.
This post covers the interesting technical decisions: real-time sync, optimistic claims, server-side bingo detection, and why each player needs a different board.
The Problem
Traditional bingo generators give everyone the same card. In person that works because a caller draws numbers. Online, there's no caller -- players mark cells when they spot something happening (during a TV show, a meeting, a baby shower). If everyone has the same arrangement, the first person to mark a cell wins every time.
Each player needs the same clues in different positions. And claiming a cell needs to be instant (optimistic) while still being authoritative (server-verified).
Architecture
- Next.js 16 with App Router (React 19)
- Supabase PostgreSQL for rooms, players, boards, claims
- Ably for real-time events (claims, bingo, round transitions)
- Vercel for deployment
Board Generation
Every board is deterministic. Given a room seed and a player ID, the board is reproducible:
playerSeed = (roomSeed XOR hash(playerId)) >>> 0
playerRng = mulberry32(playerSeed)
positions = fisherYatesShuffle(nonFreePositions, playerRng)
The room seed determines which clues appear. The player seed determines where they go. Late joiners regenerate the exact same board by using the same seeds.
Wildcard Mode
In rounds 2+, each player gets ~2/3 shared clues and ~1/3 unique clues. This creates boards that overlap enough for shared moments but diverge enough that bingo timing varies.
Optimistic Claims
Tapping a cell marks it instantly. The server call is fire-and-forget for non-bingo claims:
- Player taps cell
- UI shows claimed immediately (optimistic)
- POST fires to server (no await)
- Server rejects? Next
fetchGameStatereconciles
For bingo-completing claims, the POST is awaited. The server's claim_and_process PostgreSQL function atomically inserts the claim, checks all winning lines, and updates the room status in one transaction.
Real-Time Sync
Ably handles event broadcast. The server publishes; clients subscribe. Events: claim, bingo, new-round, player-joined, chat. If Ably disconnects, clients call fetchGameState on reconnect to reconcile any missed events.
What I'd Do Differently
The claim system works but the fire-and-forget architecture means silent failures. If a claim POST fails (network error), the player sees a claimed cell that the server doesn't know about. It self-heals on the next state fetch, but there's a visible "unclaim" moment that confuses users.
If I rebuilt it, I'd use a write-ahead approach: persist claims locally first, then sync. But for a free tool with casual gameplay, the current approach is good enough.
Try It
The whole thing is free -- no ads, no paid tier, no signup required.
- Create a card: bingwow.com/create
- Browse 1,000+ cards: bingwow.com/cards
- For teachers: bingwow.com/for/teachers


![EasyPollVote [Dev Log #2]](https://media2.dev.to/dynamic/image/width=1000,height=420,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo8l8ulgpe5wqxnyq6pjz.png)








