← All posts
Architecture

How a phone becomes a poker seat

Put the table on a TV, scan a QR code, and your phone is your hand. Here's the architecture that keeps it in sync and honest.


Our live gameswork like a real home game: the table lives on a big screen — a TV, a laptop, a monitor — and everyone's phone is their seat. You scan a QR code, take a chair, and your cards are in your hand while the community cards and pot play out on the big screen. Here is how it holds together.

The server owns the game

The single most important decision: the game state is server-authoritative. No phone ever computes whose turn it is or who won — clients only send intents ("raise to 40") and render what the server sends back. That is what makes cheating by tampering with your own client pointless.

Storage that scales down to zero

With no database configured, a room lives in an in-memory map — zero setup for local development. In production it persists to Postgres (Neon) via Drizzle, with optimistic version locking: every write carries the version it expected, and if two actions race, the stale one is rejected and retried. Two players can never half-apply a move.

Projection: everyone sees only their own seat

Before the server sends the table to a device, it builds a projection— a view tailored to that seat. Your hole cards come through face-up; everyone else's are face-down until showdown. The big-screen spectator sees allcards face-down. This is the same principle that keeps our bots honest: nobody — human or bot — is ever handed information their seat shouldn't have.

project(room, viewerSeat):
  for each seat:
    holeCards = (seat === viewerSeat || showdown) ? seat.cards : FACE_DOWN
  // ...plus pot, board, whose turn, legal actions for the viewer
Each device gets its own projection — your cards up, everyone else's hidden.

Kept in sync the boring, reliable way

Devices poll the room about once a second. No websocket fleet, no realtime service to operate — just a small, cacheable read that is trivial to scale and almost impossible to get subtly wrong. For a turn-based card game, a steady poll feels instant and keeps the whole system simple enough to reason about.

Keep reading