Browser co-op kitchen game: FastAPI + native WebSockets, server-authoritative 20 Hz simulation, in-memory anonymous lobbies, data-driven YAML levels, Canvas 2D vanilla ES-module client with locally authored SVG art and no external CDNs. .agents/PLAN.md records the LOCKED decisions; .agents/user_stories/ holds the 12 flow contracts; .agents/phases/todo/ breaks them into 14 sequential, test-gated phases (71 task files), each leaving the app launchable and the suite green. LLM-Generated: true
Undercooked
A cooperative, in-browser, Overcooked-style kitchen game: 1–4 players share one kitchen over WebSockets, cook orders against the clock, and climb a campaign of levels that get progressively harder. Lobbies are anonymous — no accounts, no email, no password: you create a room, share the 5-character code, play.
Working title. "Overcooked" is a trademark of Ghost Town Games/Studio MDG; this project is an independent homage with its own art, code, and content.
Status
Roadmap authored, implementation pending. This repository currently contains the
project scaffold and the .agents/ phased-execution plan. Execute phases with the
phased-execution skill (auto-phase.sh), starting at
.agents/phases/todo/01_runnable_service_scaffold/.
Quickstart (development)
Requires uv, Python ≥ 3.12, and podman/docker
for container builds. There is no database — all live game state is in memory.
uv sync --extra dev
uv run undercooked # http://127.0.0.1:8000
uv run undercooked --reload # dev reload
uv run undercooked-levels validate # check every level file loads and is solvable
Run the checks:
uv run ruff check .
uv run pyright
uv run pytest -m "not e2e" # unit + integration
uv run playwright install chromium # once, before the first e2e run
uv run pytest -m e2e # browser suites (app must be running)
bash .agents/validate.sh # the full pipeline gate
Container
podman build -t undercooked -f Containerfile .
podman run --rm -p 8000:8000 undercooked
# or
podman compose up -d
Configuration
All settings come from the environment (a local .env is loaded automatically;
never commit it — see .env.example).
| Variable | Default | Meaning |
|---|---|---|
UNDERCOOKED_HOST |
127.0.0.1 |
Bind address |
UNDERCOOKED_PORT |
8000 |
Bind port |
UNDERCOOKED_TICK_HZ |
20 |
Server simulation rate |
UNDERCOOKED_SNAPSHOT_HZ |
20 |
Snapshot broadcast rate to players |
UNDERCOOKED_MAX_PLAYERS |
4 |
Players per room |
UNDERCOOKED_MAX_ROOMS |
200 |
Concurrent rooms before new rooms are refused |
UNDERCOOKED_LEVELS_DIR |
levels/ |
Directory of level YAML files |
UNDERCOOKED_LOBBY_IDLE_TIMEOUT_S |
900 |
Close empty/idle lobbies |
UNDERCOOKED_GUEST_IDLE_TIMEOUT_S |
45 |
Drop a disconnected player's seat after this |
UNDERCOOKED_MAX_MESSAGE_BYTES |
4096 |
Hard cap on inbound WS frame size |
UNDERCOOKED_MAX_INPUTS_PER_S |
40 |
Per-connection inbound message rate limit |
UNDERCOOKED_SIM_LATENCY_MS |
0 |
Injected server-side downlink latency (netcode tests) |
DEBUGPY |
0 |
1 = open a debugpy listener on DEBUGPY_PORT at startup |
DEBUGPY_PORT |
5678 |
debugpy listen port |
Layout
src/undercooked/ server: FastAPI app, rooms, WebSocket protocol, simulation
levels/ data-driven level definitions (YAML) + recipes
app/static/ browser client: ES modules, Canvas 2D renderer, own SVG art
tests/ unit/, server/, integration/, sim/, e2e/ (one Playwright suite per story)
docs/PROTOCOL.md wire protocol contract (versioned)
.agents/ PLAN.md, user_stories/, phases/ — the phased-execution plan
No external CDNs. Every byte of JS, CSS, font, and image is served by this app
from app/static/. A test enforces it.
Contributing
Read AGENTS.md first: it points at .agents/PLAN.md (LOCKED decisions)
and the phase protocol. Code changes land by executing phase task files, not by
editing ad hoc.