[BingWow](https://bingwow.com) is a free multiplayer bingo platform. You type a topic, AI generates a card, and up to 20 people play together in the browser. No downloads, no accounts. Teachers use it for classroom review games, event planners use it for baby showers and team building, and watch party hosts use it for live TV events.
Here's how the real-time multiplayer works under the hood.
## The Stack
- **Next.js 16** (App Router) on Vercel
- **Supabase** (PostgreSQL + Auth + Storage)
- **Ably** for real-time pub/sub
- **Tailwind v4** for styling
## The Hard Problem: Simultaneous Bingo Claims
Most of the game is simple. A player taps a cell, the client marks it optimistically, and a fire-and-forget POST goes to the server. No waiting, no blocking.
The moment it gets complicated is bingo. When a claim completes a line, you need to atomically determine who won. Two players might complete their lines within milliseconds of each other. The server has to pick one winner and broadcast the result to everyone.
We solve this with a Supabase RPC function called `claim_and_process`. It runs in a single database transaction: insert the claim, check if it completes a line, and if so mark the round winner. The atomicity of the transaction means two simultaneous bingo claims can't both win.
## Board Generation: Deterministic Randomness
Every player needs a unique board, but late joiners need to reconstruct the exact same board a player would have gotten had they joined at the start. We solve this with seeded PRNGs.
The room gets a random seed at creation. Each player's board is derived from `(roomSeed XOR hash(playerId))`. The hash function is FNV-1a, the PRNG is Mulberry32. Both are deterministic: same inputs, same board, every time.
This means we never store boards in the database. Any client can reconstruct any player's board from the seed and player ID. Late joiners derive their board locally and overlay the current claim state from a single `game/state` API call.
## Wildcard Mode: Shared Clues with Individual Variation
In wildcard mode (always on for online play), players share about 2/3 of their clues with 1/3 unique per player. The room RNG selects a "core" set, and each player's own RNG picks their variable clues from the remaining pool.
This creates the right balance: enough overlap that calling clues is meaningful, enough variation that copying someone else's board doesn't work.
## Real-Time Events via Ably
Every room subscribes to an Ably channel `room:{code}`. Events include `claim`, `bingo`, `new-round`, `player-joined`, `chat`, and `unclaim`. Claims are fire-and-forget from the server -- the DB is the source of truth, and Ably events are convenience notifications.
On reconnect after a disconnect, the client calls `fetchGameState` to reconcile any missed events. A heartbeat POST every 2 minutes keeps `last_active_at` fresh for the expiry cron.
## AI Card Generation
When a user types a topic on [bingwow.com/create](https://bingwow.com/create), we stream clues from Gemini via SSE. The user sees clues appear one by one as the AI generates them. They can edit any clue inline, change the grid size (3x3, 4x4, 5x5), or regenerate with a different tone.
Background images are generated in parallel via Replicate's FLUX Schnell model (~2 seconds), then screened by Claude Haiku for text artifacts before being attached to the card.
## What I'd Do Differently
**Skip the custom PRNG.** Mulberry32 was fun to implement but a simple `crypto.getRandomValues` with a seed would have been simpler. The deterministic requirement is real, but there are libraries for this.
**Use server-sent events for game state instead of polling + Ably.** Ably works great but adds a dependency. SSE from a Next.js route handler could handle the claim/bingo broadcasts with one fewer service.
## Try It
The whole thing is free at [bingwow.com](https://bingwow.com). Teachers can find classroom-specific guides at [bingwow.com/for/teachers](https://bingwow.com/for/teachers). The card creator is at [bingwow.com/create](https://bingwow.com/create).
For further actions, you may consider blocking this person and/or reporting abuse
Top comments (0)