A clean, modern, realtime take on the classic word‑association party game. Spin up a room, share the link, and play instantly on any device — no account, no download. Built‑in chat, a large curated wordbank, fully‑configurable rules, and 20 hand‑tuned gamemodes (plus your own, authored in YAML).
Realtime backend on PartyKit · static client on Vercel · React + TypeScript + Tailwind.
- Instant & accountless — create a room, copy the link, play. Identity persists in
localStorage, so a refresh or redeploy rejoins your exact seat. - Server‑authoritative & cheat‑proof — the key card never leaves the server for operatives or spectators. Roles, turns, and guess counts are enforced server‑side, so a crafted client can't peek or cheat.
- Durable, reconnect‑safe rooms — game state is persisted to PartyKit storage. Disconnect, deploy, or close the tab; the room and your seat survive and you reconnect right where you left off.
- In‑app everything — structured clue input (word + number, validated), all‑chat + team‑chat, and an automatic clue/guess log so the table has one source of truth. No external Discord required.
- 20 configurable gamemodes — Classic, Tiny, Marathon, Blitz, Zen, Duet Co‑op, Triple Threat, Minefield, Sudden Death, Party Night, and more — each fun‑and‑balance judged. Board size, team count, agent/assassin distribution, timers, clue rules, and win conditions are all tunable.
- Custom YAML gamemodes — author or paste a mode in seconds; it's validated against a strict schema (the tile distribution must exactly fill the board) and shared instantly with the room.
- A large curated wordbank — 1,951 independently‑audited words across 11 stackable packs (Classic, Easy/Family, Hard/Abstract, Tech, Pop Culture, Food, Nature, Travel, Sports, Myth, and an 18+ Party pack), with a live word‑pool counter.
- Mobile‑first & accessible — fluid layout from phone to desktop, 44px+ touch targets, full keyboard board navigation, colorblind‑safe tiles (color + pattern + text label, never hue alone), light/dark/high‑contrast themes, and a real
prefers-reduced-motionpath. - Tasteful juice — spring tile flips, active‑team pulse, a dramatic assassin reveal, win confetti, and synthesized sound effects — all reduced‑motion / mute aware.
| Lobby | Spymaster view | Mobile |
|---|---|---|
![]() |
![]() |
![]() |
| Gamemodes | Custom YAML | End of game |
|---|---|---|
![]() |
![]() |
![]() |
share link
┌───────────┐ ws ┌────────────────────┐
│ Browser │◀────▶│ PartyKit room │ ← one durable object per room
│ (React/ │ │ (party/server.ts) │ ← authoritative state + storage
│ Tailwind)│ └────────────────────┘
└───────────┘
▲ role-filtered snapshots (operatives never receive the key card)
party/server.ts— the authoritative game server. One instance per room. Holds all state, validates every action, drives turn timers via alarms, persists to room storage, and broadcasts role‑filtered snapshots (a spymaster sees the key; everyone else getsowner: "hidden"for unrevealed tiles).shared/— code shared by client + server:types.ts(the wire contract),gamemode.ts(strict schema validation),engine.ts(pure, testable game rules),builtins.ts(the validated mode/pack registry), and the generatedgamedata.ts.src/— the React client.lib/party.tswrapspartysocket(auto‑reconnect), components render the role‑filtered view, optimistic taps reconcile against the server broadcast.gamemodes/*.yaml+data/wordbank.jsonare the human‑editable source of truth.npm run gencompiles them intoshared/gamedata.ts.
See docs/research.md for the competitive research that shaped this design, and docs/ARCHITECTURE.md for a deeper dive.
npm install
npm run dev # runs Vite (client) + PartyKit (server) together
# → client on http://localhost:5173, party on http://localhost:1999Open the printed URL, click Create a room, and share the link (or just open it in a second tab/device) to play.
Dev tip: if port 1999 is taken, run the server on another port and point the client at it via
.env.local:VITE_PARTYKIT_HOST=127.0.0.1:1998then
npm run dev:party -- --port 1998andnpm run dev:web.
| Script | What it does |
|---|---|
npm run dev |
client + server together (hot reload) |
npm run gen |
compile gamemodes/*.yaml + data/wordbank.json → shared/gamedata.ts |
npm run build |
production build of the client → dist/ |
npm run typecheck |
strict TypeScript check |
npm run deploy:party |
deploy the PartyKit server |
node scripts/sim-game.mjs |
headless 4‑bot full game (asserts the engine end‑to‑end) |
node scripts/sim-coop.mjs |
headless co‑op game smoke test |
- Client (Vercel): ✅ live → https://codenames-rho-smoky.vercel.app
- Realtime server (PartyKit): ⛔ currently blocked by a PartyKit platform limit (their shared
partykit.devzone is at Cloudflare's 10,000-custom-domain cap). The server builds fine and runs perfectly locally; it just can't be published to partykit.dev right now.
npm run gen
npx partykit deploy # → https://codenames.<your-user>.partykit.dev (when capacity frees)
vercel --prod # static client → VercelSet VITE_PARTYKIT_HOST=codenames.<your-user>.partykit.dev in the Vercel project env so the client talks to your party (the client already falls back to that host).
👉 See docs/DEPLOYMENT.md for the current deploy status and two paths to get realtime live (retry PartyKit, or self-host on your own Cloudflare via wrangler).
Every mode is a YAML document in gamemodes/. Board size, teams (2–4 or co‑op), agent/neutral/assassin counts, clue rules (numbered, numberless, zero, ∞), guess rules (clue_plus_one / exact / unlimited / single), timers, sudden death, and win conditions are all configurable. The only hard rule: the tile distribution must exactly fill the board.
The 20 built‑ins were each rated by three independent persona judges (a casual host, a competitive player, a game designer) for fun / balance / clarity — see docs/gamemodes-judging.md. To make your own, open Change mode → Custom YAML, edit, and Load — it's validated and shared with the room live.
11 stackable packs, 1,951 words, each pack passed through two independent audits (a safety lens and a quality lens) for offensiveness, duplicates, ambiguity, and difficulty balance. The audit log lives in docs/wordbank-audit.md. The 18+ Party pack is clearly badged and off by default.
- Board tiles encode ownership with color + a corner glyph + a text label (e.g.
RED,BYSTANDER,ASSASSIN) — never color alone. - Full keyboard play: arrow‑key roving focus across the grid, Enter to select/confirm, visible focus rings.
- ARIA roles/labels on tiles, dialogs, switches, and the board.
- Light / dark / high‑contrast themes and a real reduced‑motion mode (respects the OS setting).
- Hidden information is a server guarantee. The key card never crosses the wire to operatives or spectators — the server sends each connection a role‑filtered view, and team/role seats lock the moment a game starts, so a crafted client can't switch to spymaster mid‑game to peek. There's a regression assertion for this in
scripts/sim-game.mjs. - Pure, tested rules. All game logic lives in
shared/engine.ts(no I/O) and is exercised end‑to‑end over real WebSockets bysim-game.mjs(full 2‑team game) andsim-coop.mjs(cooperative) — win detection, turn rotation, guess limits, and the key‑leak check. - Reviewed. A 5‑agent adversarial UI/gameplay review plus a 4‑cycle
criticality-loop(independent fresh‑context auditors across structural / deslop / correctness lenses) hardened the build — ~26 fixes, including a Critical mid‑game key‑leak and an expired‑timer recovery bug. - Validated config. Custom YAML gamemodes flow through one strict
validateGamemode()(bounded board/teams/timers; distribution must exactly fill the board), so a pasted mode can't crash or abuse a room.
gamemodes/ 20 gamemode YAML files (source of truth)
data/wordbank.json curated, audited wordbank
scripts/
build-data.mjs yaml + json → shared/gamedata.ts
sim-game.mjs headless multiplayer engine test
sim-coop.mjs headless co-op test
shared/ types, schema validation, pure engine, registry
party/server.ts authoritative PartyKit server
src/ React client (components, lib)
docs/ research, audits, architecture, deployment, criticality log, screenshots
| Doc | What's in it |
|---|---|
| docs/ARCHITECTURE.md | server/client split, hidden‑info model, data flow |
| docs/DEPLOYMENT.md | live status + the PartyKit blocker + two ways to get realtime live |
| docs/self-hosting.md | research: host our own realtime backend free on Cloudflare (partyserver / cloud-prem) |
| docs/research.md | competitive survey → "how we win" product direction |
| docs/wordbank-audit.md | per‑pack independent safety + quality audit log |
| docs/gamemodes-judging.md | fun / balance / clarity scores for all 20 modes |
| docs/criticality-loop.log.md | the 4‑cycle audit‑and‑fix record |
Built with PartyKit · React · Vite · Tailwind · TypeScript.






