← Back to the projects

Personal project / In progress

Undercover

Real-time multiplayer social deduction game

TypeScriptNextJsReactCloudflare WorkersDurable ObjectsWebSocketVitest
Undercover

A closer look

01

Role assignment and word pairing

Civil/undercover/Mr. White roles distributed based on player count (2 undercovers from 7 players up, optional Mr. White from 5 players up), with a mathematical guarantee of a civilian majority. The undercover's word is chosen by tag-similarity with the civil's (none/close/very-close levels), automatically relaxed if the chosen theme doesn't have enough characters to satisfy it, plus a variant where the undercover's word becomes a plain attribute of the civil's character instead of a whole different one.

02

Note (numeric) mode

Character-free variant: civil and undercover each get a random note (0-20) separated by a host-configurable gap, always guaranteed satisfiable even at extreme values (0 or 20). A player proposes a theme each round, in turn order.

03

Clue rounds and turn rotation

Turn order shuffled once per game and kept for its whole duration. Disconnected players are auto-skipped (empty clue) without stalling the round; a configurable clue timer (30-90s) does the same for a slow player, without eliminating them. Number of clue passes before each vote is host-adjustable (1 to 5).

04

Plurality voting with tie-breaking

Plurality voting (no absolute majority required), with the ability to abstain or change a vote until the window closes. Early resolution once everyone has voted (with a short grace period to change one's mind), or after a configurable timer (30-180s) otherwise. A tie triggers a runoff restricted to the tied leaders; a tie again reopens the vote to everyone.

05

Bonus vote instead of removal

A player who misses their clue timer is no longer eliminated outright: they get a phantom "accusation vote" held against them for the next tally, then cleared regardless of the outcome.

06

Elimination and end-game

A reveal phase after every elimination (role/word optionally hidden), win condition checked (civilians, undercover, or Mr. White) after every elimination and every kick. Mr. White gets a 60s window to guess the civil's word or note and steal the win.

07

Host settings

Theme selection with sub-category (franchise/series) filtering, word similarity level, Mr. White toggle, classic/note mode, note gap, clue and vote timers (toggleable, adjustable duration), clue passes per vote, reveal-on-elimination toggle — all editable only in the lobby, before the game starts.

08

Reconnection and player management

Reconnecting into the same seat after a dropped connection, with no lost progress; host role auto-transfers if the host disconnects. Host-side kick at any point in the game (client ban, distinct from a plain disconnect), with the consequence resolved immediately (turn passed on, vote recomputed, win checked).

09

Real-time architecture

One room = one Cloudflare Durable Object (state persisted in embedded SQLite, survives redeploys), driven by a single state machine (lobby → reveal → clues/theme → vote → elimination → end) and one alarm-based timer multiplexed across phases. Broadcasts a per-player state snapshot over WebSocket (other players' secret info stays server-side).

10

UI and content

Dedicated screen per phase (lobby, role reveal, clues, vote, elimination, end), a round recap table, a "Rules" button explaining the whole game, several character franchises (movies, anime...) with dozens of entries each.