Status: design sketch. Covers the UX flow from landing on the site through gameplay. Data model is in
data-model.md; this doc covers the interactive layer on top.
The first thing a user sees.
- Quackster logo + tagline
- Host Game button (primary CTA)
- Join Game section: text field for room code + submit, inline on this screen — no separate page
Join is currently a no-op placeholder; the input field is there to validate the UX shape.
After clicking "Host Game".
Grid of gamemode cards. Each card shows:
- Preview image — decorative illustration (not a screenshot). Stored in
gamemodes/<id>/preview.webp(or SVG). If missing, render a colored placeholder with the gamemode icon. - Name — from manifest
name - Short description — from manifest
description - Player count — derived from
requires.min_players/requires.max_players
The grid should work with 1–8 gamemodes. Currently only grid_quiz exists.
Selecting a card → advance to settings for that gamemode.
Game configuration has two layers that are always present, regardless of gamemode:
- Content — what is played (boards, packs, question lists). Varies by gamemode — grid quiz picks boards, other gamemodes pick packs or question lists.
- Rules — how the game plays (timer, buzz-in, display mode, theme).
Together, content + rules form a game config that can be saved and shared (see §Game configs).
Gamemode-specific. Each gamemode defines what "content" means.
Grid Quiz — board selection:
- Pick one or two boards that are played together (e.g. a "capitals" board
- a "science" board). Two boards are played on after the other and the second board should give more points.
- Boards are loaded from
gamemodes/grid_quiz/boards/*.yaml. Currently onlyschoolexists. - MVP: select from pre-made boards only. A visual board editor (pick categories, swap individual questions, compose custom categories) is planned for a later iteration — see §Board editor.
Other gamemodes (future) — pack / question selection:
- Gamemodes like
classicorbattle_royalewould pick one or more packs, or a flat list of question IDs. Exact UI TBD per gamemode.
Settings default from the game manifest (the data-layer Rules +
GridQuizRules in data/games/*.yaml) and the host may override any of them
in the config UI before creating the room. Each gamemode's rules section is a
Svelte component; common patterns may be extracted later.
Grid Quiz rules:
| Setting | Type | Default (from manifest) | Notes |
|---|---|---|---|
| Time per question | number (seconds) | question_timer_secs |
Countdown after buzz-in. 0 = no timer. |
| Point values visible | toggle | on | Show/hide point values on the board. |
| Answer mode | select | buzz_policy+steal_policy |
UI preset over the data-layer enums. See §Answer modes. |
| Picker mode | select | picker_mode |
Who chooses the next cell. See §Picker modes. |
| Reveal auto-advance | number / off | reveal_auto_advance_secs |
Off (manual) or N seconds before Reveal auto-advances. |
| Display mode | select | screen_is_player |
See §Display & admin modes. |
| Default language | select | en |
Game-level default for i18n. Players can override per-device (see §Per-player overrides). |
| Default theme | select | modern |
Game-level default theme. Players can override per-device (see §Per-player overrides). |
The "big screen" can serve three roles. This is the display mode setting:
| Mode | Description |
|---|---|
screen_is_player |
The big screen is a full participant — it shows the game board and is also a player (e.g. host plays along on the TV). |
screen_is_display |
The big screen shows the game board only, is not a player. Players interact on their own devices. An admin must be designated from the player list to control game flow (advance questions, mark right/wrong). |
screen_is_admin |
The big screen shows the board and has admin controls built in. No separate admin player needed. The screen is not a player. Think classroom / bar trivia — one person at the laptop runs the whole thing. |
Admin controls (available in screen_is_display via designated player, or
directly in screen_is_admin):
- Advance to next question / next round
- Mark answer as correct or incorrect (override if needed)
- Skip question
- Pause / resume timer
- End game early
In screen_is_player mode, no admin controls exist — game follows normal
player flow with automated scoring.
How a question gets answered — a UI preset over the data-layer
(buzz_policy, steal_policy):
| Mode | buzz_policy |
steal_policy |
What happens |
|---|---|---|---|
free_for_all |
open_floor |
open_floor |
Question goes live; anyone buzzes; first wins the floor. Wrong → locked this question, others re-buzz (half points). |
turn_based |
turn_order |
none |
The turn contestant answers directly. Wrong → closed, no steal. |
turn_then_buzz |
turn_order |
open_floor |
The turn contestant answers; wrong → opens to the buzzer for a steal. |
The data layer is finer-grained (broadcast buzz, round_limited steal,
this_round lockout, …); the UI offers these three presets, and an advanced
override can expose more later. Answering is decoupled from picking the
cell — see §Picker modes.
Who chooses the next cell — a separate axis from answering, stored in
GridQuizRules.picker_mode:
| Mode | What happens |
|---|---|
rotate |
Strict rotation through the player order every round. |
winner_picks |
The first player to answer correctly picks next (Jeopardy control). No correct answer → rotate. |
The two axes compose freely (see docs/architecture.md §Grid quiz runtime
for the full matrix). At StartGame the pick order is shuffled; the first
picker is rotation[0].
Who physically taps the cell is a further UI choice: the active picker taps
on their own device, or a moderator taps on the player's behalf (the
player says the cell aloud). This does not change game state — only who may
send PickCell.
After clicking "Create Game" on the settings screen.
- Join code — 6-character alphanumeric, prominently displayed at top
- QR code — next to join code, encodes the join URL
- Player list — auto-updates as players join; each player has an auto-generated name (see §Player identity)
- Start Game button (primary) — always visible
- Start Early subtext — "Start with N players now" shown when at least 1 player has joined
- Start Without Players — available in
screen_is_adminmode only; skips the lobby entirely or starts with 0 joined players, going straight to admin controls - Settings summary — collapsible, shows chosen settings from previous screen
The lobby is a live-updating view — players appear in real-time as they join. No page refresh needed.
Players get an auto-generated animal name on joining:
- Format:
<emoji> <animal>— e.g. 🦆 Ducky, 🐼 Panda, 🦊 Foxy, 🦉 Owly - Randomly assigned from a curated list (no duplicates in the same game)
- Player can rename themselves at any time via an inline edit on their name
- Name is stored in the game session, not a persistent account (no auth for MVP)
The animal list lives in src/lib/data/animals.ts — a simple array of
{ emoji, name } pairs. Enough entries for at least 50 players without repeats.
Language and theme are set at the game level as defaults, but each player can override them on their own device without affecting others:
- Language — a player can switch their interface and question language independently. The game default determines what new joiners see initially, but a German player can play in German while others play in English.
- Theme — same pattern. The host sets
neonas the game default, but a player who prefersmodern-darkcan switch locally. The big screen always uses the game default.
These overrides are device-local (stored in browser localStorage or session). They don't modify the game config.
A game config = content selection + rules + default language + default theme. Configs can be saved and shared:
- Copy config (MVP) — a "Copy config" button serializes the current config as a shareable string (JSON, base64, or a URL fragment). Anyone with the string can paste it to recreate the same game setup. No server-side storage.
- Persisted configs (future) — named presets stored server-side, browsable and reusable. Could include community-shared configs ("Bar Trivia Night", "Classroom Geography 101").
Config sharing is opt-in — the default flow is manual selection. No account required for MVP.
A visual editor for building custom grid quiz boards:
- See the board layout (categories × point values) and what questions fill each slot.
- Pick categories from existing packs or tag filters.
- Swap individual questions (drag-drop or click-to-replace).
- Compose custom categories with hand-picked questions.
- Save the board as a new YAML file (download, or server-side save later).
Not in MVP. The editor will be a dedicated screen/flow, likely replacing the content selection step when invoked.
Gameplay varies significantly by gamemode and display mode. Detailed gameplay flow is gamemode-specific and will be documented per gamemode.
General principles:
- State is streamed over WebSocket from the Rust backend — all clients see
updates in real-time (each connection gets a role-specific projection; see
docs/architecture.md) - Players on phones see the player view (their answer input)
- The big screen shows the board/public view (question, scores, animations)
- Admin controls are an overlay on the host/display view, not a separate page
These were debated; the design above reflects the chosen path.
- Join code inline on home screen — not a separate "Join" page. One field, one button, stays on the start screen. Reduces navigation depth.
- Content + rules as separate layers — content (boards, packs) and rules (timer, buzz-in) are conceptually distinct and edited in separate sections of the config screen. Content is gamemode-specific; rules follow gamemode conventions. Together they form a saveable game config.
- Ad-hoc settings per gamemode — no manifest-level settings schema for now. Gamemodes define their own settings UI component. A common schema may emerge later once patterns stabilize.
- Three display modes, not two — the distinction between "screen is display" (needs a phone-wielding admin from the player list) and "screen is admin" (laptop at the front of a bar) is real and important. Merging them would force bar-trivia hosts to also be players, which they don't want.
- Per-player language and theme overrides — the game sets defaults, but each player can switch independently. No global lock — a German player shouldn't be forced to play in English because the host chose it.
- Copyable game configs (MVP) — serialize as a shareable string. No server-side persistence needed yet. The sharing primitive exists; storage is the later addition.
- Animal names with emoji — low-friction onboarding. No name prompt before joining, no blank names, instant visual identity. Renaming is opt-in.
- Buzz-in: half points on re-buzz — rewards speed but doesn't fully punish wrong fast answers. Grid-quiz-specific; other gamemodes define their own semantics.
- Start without players — the admin-only / bar-trivia flow needs this. Zero-player start means the admin drives everything from the display screen.
- Board editor deferred — MVP uses pre-made boards only. The visual board editor is a second-iteration feature; the content selection UI is designed to accommodate it later without restructuring.
- Join code format — 6-char alphanumeric is common (Kahoot, ClassQuiz). Should we use a word-based code for easier verbal communication (e.g. "blue-duck-forty")? Trade-off: readability vs. collision space.
- QR code content — encodes
https://<host>/join/<code>(full URL). More convenient — players just scan and land on the join page. Host is known at generation time since the game server constructs the URL from its own origin. - Spectator mode — the manifest schema has
spectator_view. Should the lobby support a "join as spectator" option? Likely yes, but deferred. - Reconnection — if a player's phone disconnects, can they rejoin with the same identity? Needs session token or local storage. Deferred until we have real-time working.
- Max players — what's the practical limit? Depends on gamemode. Grid quiz
could support 20+; battle royale might cap at 50. Manifest
max_playersis optional for now. - Late join — can a player join after the game has started? Gamemode- specific. Grid quiz probably yes (pick an unclaimed tile on next round). Battle royale probably no. Deferred.
- Teams — we need to think about how to do teams probably at the player join screen.
- Config serialization format — JSON? Base64-encoded JSON? URL fragment? Need to decide before implementing "Copy config". Should be compact enough to paste in a chat message.
- Two-board play — when two boards are combined, how are categories merged? Concatenated? Interleaved? Host picks the order? Need to define before implementing multi-board selection.