TrioOnline
A real-time card game for my friends, built so nobody can peek.
- 36
- cards in a seed-shuffled deck
- 20
- ticks per second in the state machine
Stack: TypeScript · Colyseus · Next.js · Zustand · Prisma · PostgreSQL · Redis · Turborepo · Kubernetes
Status: Played among friends
GitHubUnder the hood: TrioOnline
I built it to play with friends and ended up treating it like a real engine. When the rivalry is between friends, nobody gets to win through a bug, lag or an open DevTools tab.
What I set out to solve
Matches that replay byte for byte
I banned Math.random() and Date.now() from the core. The 36-card deck is shuffled with Fisher-Yates over a server-seeded Park-Miller generator, time runs in ticks and every action becomes an immutable event: replaying the log on an empty engine yields the same state.
The client never knows what it can’t see
A face-down card travels with a null value; the number leaves the server only in the patch that reveals it. The seed never leaves the server, and every command carries a sequence number, so a packet replayed from another turn is dropped.
One queue per room
Two clicks at the same instant never become a race condition: every command enters a FIFO queue in the Colyseus room and runs one at a time, with the state machine checking the phase before applying it.