[004] / SELECTED WORK
·PROJECTSPYFALL UNLIMITED
Browser-based multiplayer social deduction. Built solo to learn realtime systems.
- ROLE:
- Solo developer
- TIMELINE:
- June – July 2025
- STATUS:
- Shipped
- STACK:
- Next.js, TypeScript, Supabase Realtime
A browser-based multiplayer social deduction game. Built solo to learn realtime systems with Supabase channels and a PostgreSQL session model.
[METRICS]
[STACK]
- Next.js
- TypeScript
- Supabase Realtime
- Tailwind
[01]
BUILDING REAL-TIME MULTIPLAYER WITH SUPABASE CHANNELS
Spyfall is a 4–8 player social deduction game where one player is the spy and everyone else shares a secret location. The game state — players, roles, votes — has to be in sync across every client within ~200ms or the experience falls apart. I built the realtime layer on Supabase channels, with PostgreSQL as the source of truth. Each game session is a row, with player state in a child table; channel subscriptions broadcast row changes to every connected client. No custom WebSocket server — the database is the server.
[02]
DESIGNING THE ROLE-ASSIGNMENT ALGORITHM
The naive role assignment is `roles = shuffle([spy, civilian, civilian, ...])`. Easy. The actual constraint is that players need to feel like the spy assignment is random *across rounds* — if the same person is the spy three rounds in a row, the game becomes a joke. I added a session-level memory that tracks recent spy assignments and weights against repeats, while preserving the surface randomness. A small change with an outsized effect on how the game plays.
[03]
WHAT I LEARNED ABOUT LATENCY
Realtime games are unforgiving about latency. A 300ms delay between "player votes" and "player sees the vote" breaks the social rhythm — you can feel the lag in the room. I learned to measure round-trip time per channel event, not per HTTP request, and to render local state changes optimistically before server confirmation. The instinct from web apps — wait for the server, then update — produces a worse experience here than just trusting local state and reconciling on conflict.
[UP NEXT]
→ 001 / EUNO