- Go 61.4%
- TypeScript 37.4%
- Shell 0.4%
- Dockerfile 0.3%
- CSS 0.3%
- Other 0.2%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| backend | ||
| scripts | ||
| web | ||
| .gitignore | ||
| analyses.md | ||
| BUG_FIX_CHECKLIST.md | ||
| CONTEXTMAP.md | ||
| CURRENT.md | ||
| DECISIONS.md | ||
| docker-compose.yml | ||
| Dockerfile | ||
| README.md | ||
| sample.env | ||
Who Said That? — Game App
The app for Who Said That? — a private, friends-only online party game. A question appears, everyone writes a funny answer, then players drag guesses of who wrote what and vote a superlative ("Classic Karl", "Grossest"…). Loaded Questions / Fibbage hybrid — zero right answers, low-stakes hang.
This repository contains the application (backend + web client). The game design spec, question content, and validation pipeline live in the companion content repo:
- Content/spec: https://forgejo.richelson.cloud/mercury/WhoSaidThat
(
game-design-spec.mdis the binding spec, 754 lines, Appendix C on wire anti-cheat is non-negotiable) - App repo: https://forgejo.richelson.cloud/mercury/whosaidthat-app (PUBLIC)
Quickstart (local dev)
Backend (Go, :8080):
cd backend
go build -o /tmp/wst-server ./cmd/server/ && /tmp/wst-server
Deck files: By default the server loads data/deck.json (Reddit-sourced
questions). A separate Caleb-authored deck with 50 questions across 4 categories
is available at data/questions_caleb.json. Use it instead:
DECK_PATH=data/questions_caleb.json go run ./cmd/server
All categories are shuffled together each round — you'll get variety automatically. Both files use the same v2 schema so swapping is zero-config.
Frontend (Vite, :5173 — proxies /api and /ws to :8080):
cd web
npm install
npx vite --host
Open http://localhost:5173. Create a room, share the code, join from another browser. Only port 5173 needs forwarding if you're testing from another machine (Vite proxies the backend).
Current gameplay behavior (2026-09-11 playtest build)
- Superlative is chosen randomly by the server each round (13-entry list) and shown to all players during the answer phase — no picker.
- Clicking the timer in any phase adds 15 seconds (
extend_timer). - Result phase: 60s timer, "Next Round →" bypasses it; each answer shows who guessed it correctly vs. wrong; scoreboard shows this-round points + cumulative total with tie-aware medals.
- Deck:
questions_caleb.json(50 Q / 4 decks) is copied from the user's working file — see CONTEXTMAP.md for the exact path. No mapping file.
Project docs — read these first
| File | Purpose |
|---|---|
CURRENT.md |
Live state — what's done, what's next. READ FIRST. |
CONTEXTMAP.md |
Where everything lives. |
DECISIONS.md |
Why it's built this way (app-level decisions). |
README.md |
This file. |
Docker (quick start on another machine)
git clone <repo-url> whosaidthat-app
cd whosaidthat-app
# Optional: customize with a .env file (see sample.env)
cp sample.env .env
docker compose up -d
Open http://localhost:8206. The container serves the frontend and API on a single port — no separate frontend dev server needed. Uses the questions_caleb.json deck by default.
.env file — copy sample.env to .env to customize:
| Variable | Default | Description |
|---|---|---|
QUESTIONS_FILE |
data/questions_caleb.json |
Question deck file (relative to backend/data/) |
PORT_UI |
8206 |
Host port mapped to container's :8080 |
NETWORK_NAME |
(none) | External Docker network (for Caddy in separate compose) |
Container env vars (set automatically, but can be overridden):
| Variable | Default | Description |
|---|---|---|
PORT |
8080 |
Backend listen port (inside container) |
WEB_DIR |
web/dist |
Static frontend directory |
WST_DISCONNECT_DROP |
30s |
Drop disconnected lobby players after this |
WST_SWEEP_INTERVAL |
10s |
How often to sweep stale lobby connections |
To use the original Reddit-sourced deck instead, set QUESTIONS_FILE=data/deck.json in your .env.
Game-design decisions (rules, scoring, anti-cheat) live in the content repo's
decisions.md — that's the design ledger. This repo's DECISIONS.md covers
app-architecture choices (stack, repo layout, persistence).
Repos at a glance
mercury/WhoSaidThat(content, private): spec, decisions,scrape.py→data/deck.json,validation/webapp.mercury/whosaidthat-app(app, public):backend/Go,web/React.