Pub Quiz Platform

Product Requirements Document

Status Draft
Scope Project (cross-cutting)
Last updated 2026-05-13
Source of truth .claude/context/knowledgebase/

Contents

  1. Overview
  2. Architecture
  3. Requirements
  4. Phases
  5. Process
  6. Reference
  7. Stretch

Overview

Overview

Problem statement

Pub quizzes are a popular social format, and software platforms aimed at running them already exist. They tend to fall into one of two camps: feature-light tools that miss the TV-game-show production values quizmasters want, or feature-heavy platforms whose complexity makes set-up and operation a chore. We bring the rich, animated, interactive experience without the complexity, packaged as a subscription service quizmasters can rely on for a polished quiz night that still works on the unreliable internet typical of a real venue.

Solution outline

Four connected apps, one shared C# schema library:

A slide's elements are instances of object types — pluggable modules that contribute schema, editor surface, runtime behaviour, and protocol extensions. v1 ships a built-in catalogue (text, image, audio, video, multiple-choice/free-text/drawing/buzzer inputs, timer, leaderboard, mini-game) and adding a new built-in object type does not require changing core code. v1 is fully local: no cloud, no account, no internet at quiz time. Cloud-backed authoring (account, library, Designer↔cloud↔Host distribution, bundle-supplied object types) is a stretch goal for a future release. See Architecture for the full picture and Applications for per-app responsibilities.

Goals

v1 ships in four phases — MVP, Alpha, Beta, Production. Beyond v1 is a Stretch backlog. Scope and acceptance criteria for each phase are in Phases.

Users

Role Device Authenticated? Primary actions
Quiz Author Designer (Windows, macOS) + Host (Windows, macOS, iPad, Android tablet) No in v1; stretch Yes (cloud account) for cloud-backed authoring Create, edit, save quizzes locally; transfer to host over LAN; run quiz nights.
Quizmaster (live operator) Host + optional Remote on phone/tablet No (paired with Host on the LAN) Run the live session. The Remote app on the quizmaster's phone mirrors the Host display, shows host-notes, and controls the Host (MVP: advance/go-back; Alpha: rich command set).
Team (Participants / Quiz Goers) Client (iPhone, Android phone, iPad, Android tablet) No (team name only) One shared device per team. Join host, submit team answers, see scores.

The Author and Host operator are typically the same person. Each team plays from a single shared Client device; v1 has no per-individual representation within a team. In v1 no app authenticates against anything — Designer→Host transfer is gated by being on the same local network. stretch When cloud-backed authoring is added, both Designer and Host authenticate against the Author's cloud account; the Client remains unauthenticated by design.

Applications

The platform consists of four apps. This page covers each app's responsibilities and supported platforms; for shared schemas, protocols, and the system-wide architecture see Architecture. For each app's numbered behaviors see Functional Requirements. For each app's UI entry-point inventory (menus / dialogs / panels / shortcuts) see UI Surfaces. For the on-disk shape (scenes, prefabs, scripts) inside each Unity project see Per-App Scaffolding. Phase scoping is in Phases.

App Phase introduced Primary platforms
Designer MVP Windows, macOS (Catalyst) — single .NET MAUI Blazor codebase. iPad / Android tablet authoring is Stretch.
Host MVP iPad, Windows, macOS, Android tablet
Client MVP iPhone, Android phone, iPad, Android tablet
Remote MVP (minimum viable controls); rich control commands in Alpha iPhone, Android phone, iPad, Android tablet

Designer scope in v1: Windows + macOS desktop only, from a single .NET MAUI Blazor Hybrid codebase. iPad and Android tablet authoring are Stretch and ship derived from the web-based Designer codebase. Web authoring itself is also Stretch and reuses the same Razor component library compiled for Blazor WASM.

Designer designer

A content-authoring tool — a single .NET MAUI Blazor Hybrid desktop app for Windows and macOS (Catalyst). Razor / Blazor components form the entire authoring UI — shell, panels, properties inspector, dialogs. Multi-window is real OS-level windowing via the MAUI Window API. Slide preview is a dedicated Quiz.Preview project, built only for WebGL and embedded in BlazorWebView as a <canvas> — same WebGL build serves desktop and (Stretch) the future web tool, ~95-98% pixel fidelity vs native Host (accepted trade-off). iPad / Android tablet authoring is Stretch and ships from the web-based Designer codebase. Architecture and diagram in Designer Shell. Chrome uses the Studio operator-chrome theme — surface tokens, brand-hue ration, and per-component spec live in Design Specification — Studio.

The authoring model is PowerPoint-style slides. A quiz is an ordered list of slides, optionally grouped into rounds. Each slide owns two independent canvases:

Plus a per-slide host-notes field (free-form text the author writes for the quizmaster) which renders only on the Remote app during play, never on the Host canvas or any Client.

Authors place elements on each canvas — a text block, an image, a multiple-choice input, a timer, a drawing input, etc. — by dragging from a palette of object types (the v1 built-in catalogue; bundle-supplied types are a stretch goal). Each element is configured through that object type's editor surface in the Razor properties inspector; the embedded Quiz.Preview Unity WebGL canvas shows the live pixel-accurate render of the same state.

Authors attach media (images, audio, video) as resources referenced by element properties, and configure timing and scoring per slide and per round. Authors also bundle team-customisation assets — buzzer jingles (F-DE-28) and premade avatars (F-DE-30) — which teams pick from at join. The Designer ships a small built-in default asset library (pre-licensed jingles + starter avatars) the author can pull from; selections are copied into the .quiz so the package stays self-contained. Stylus input for sketching and annotation (Apple Pencil and equivalents) is a post-v1 stretch goal — v1 is touch-only.

The MVP-target platforms (Windows, macOS) are stood up cross-platform from day one with the single MAUI Blazor codebase. iPad and Android tablet authoring are Stretch.

For v1 the Designer saves quizzes as .quiz files on the local file system. A "Push to Host" UI action discovers Hosts on the local Wi-Fi (via Bonjour/mDNS), lets the author pick one, and pushes the on-disk .quiz to it over the WebSocket. A separate Run from slide action (F-DE-27) launches a local Host process on the same machine starting at the currently-selected slide — used for fast author-side validation without the network discovery step. Cloud-backed authoring — accounts, a quiz library, version history, soft-delete, restore — is a stretch goal for a future release; v1 ships none of it. Authors can still organize their on-disk quizzes with titles, descriptions, and tags inside the package metadata.

Host host

The "TV show" app, runnable on iPad, Windows, macOS, and Android tablets, and typically connected to a projector or TV (HDMI on most platforms; AirPlay or USB-C also available on Apple devices). The Host:

Client client

A lightweight team app for phones and tablets — iPhone, Android phone, iPad, and Android tablet. One shared device per team. The Client:

Remote remote

Phase: MVP (minimum viable controls); the rich control-command set lands in Alpha.

The quizmaster's pocket controller — a phone (or tablet) the quizmaster carries while walking the room. It runs on iPhone, Android phone, iPad, and Android tablet. The same person who runs the Host operates the Remote; the Remote does not replace the Host's keyboard/click controls but supplements them.

The Remote in MVP:

Remote chrome uses the Studio operator-chrome theme — controls, host-notes pane, status bar all sit on calm surfaces. The audience-mirror region inside renders Showtime at full saturation. See Design Specification — Studio for tokens and the per-app component spec.

In Alpha the Remote gains the rich control-command set: jump to a specific slide, trigger element reveals (show the leaderboard / show the answer), lock or unlock Client input, extend or skip the timer, override scoring per team. This is F-RE-9 / F-HO-24.

A session can run with zero or one Remote. There is no multi-Remote support in v1 — the Host owns the session, and one paired Remote at a time is enough to walk the room.

Architecture

Architecture Overview

The platform pairs three Unity live-play apps (Host, Client, Remote) with a .NET MAUI Blazor Hybrid Designer for content authoring, plus a fourth Quiz.Preview Unity project that serves only as the Designer's slide-render target (WebGL build, embedded in BlazorWebView). All four apps ship in MVP. The Remote ships in MVP with a minimum viable controls feature set (discovery, pairing, mirror, host-notes, advance/go-back); its rich control command set lands in Alpha. The Designer's authoring UI is built entirely from Razor / Blazor components running on Windows + macOS (Catalyst) + iPad from one C# codebase. See Designer Shell for the architecture and Decisions for the rationale. The Host, Client, and Remote are UGUI-driven for creative freedom (custom shaders, prefab compositions, animations).

For v1 the platform is fully local: the Designer exports .quiz files to disk and sends them to the Host over local Wi-Fi via a UI action; the Host receives, stores them locally, and at session time pushes per-Client content to each Client as they join. A cloud-backed authoring service (account, library, Designer→cloud→Host distribution) is a stretch goal for a future release — see Backend Schema.

%%{init: { "theme": "base", "themeVariables": { "fontFamily": "Inter, system-ui, sans-serif", "fontSize": "14px", "primaryColor": "#FFFFFF", "primaryBorderColor": "#FF009F", "primaryTextColor": "#1F1933", "secondaryColor": "#F0EBE3", "secondaryBorderColor": "#16B2EB", "secondaryTextColor": "#1F1933", "tertiaryColor": "#F8F5F0", "tertiaryBorderColor": "#5A536B", "tertiaryTextColor": "#1F1933", "lineColor": "#5A536B", "edgeLabelBackground": "#F8F5F0", "mainBkg": "#F8F5F0", "clusterBkg": "#F0EBE3", "clusterBorder": "#DDD5C8", "titleColor": "#1F1933", "nodeBorder": "#5A536B" }, "flowchart": { "useMaxWidth": false, "htmlLabels": false, "curve": "basis", "padding": 20, "nodeSpacing": 70, "rankSpacing": 80 } }}%% flowchart TB subgraph v1["v1 — LOCAL"] direction TB DES[Designer\n.NET MAUI Blazor Hybrid\nauthoring app — Win + macOS] HOST[Host\nUnity · TV / projector app] REM[Remote\nUnity · quizmaster controller] CLI[Clients × N\nUnity · one device per team] DES -->|.quiz over WebSocket\nBonjour / mDNS discovery| HOST HOST -->|live-play WebSocket\neager push on join| CLI HOST <-->|control WebSocket| REM end subgraph stretch["Stretch — Cloud-backed"] CLOUD[Cloud backend\nAuth · Quiz library — Postgres\nStorage — .quiz packages\nvendor TBD when promoted] end DES -.->|future: publish / sync| CLOUD HOST -.->|future: download library| CLOUD classDef primary fill:#FFFFFF,stroke:#FF009F,stroke-width:2px,color:#1F1933,rx:10,ry:10 classDef secondary fill:#F0EBE3,stroke:#16B2EB,stroke-width:2px,color:#1F1933,rx:10,ry:10 classDef accent fill:#F0EBE3,stroke:#961EEF,stroke-width:2px,color:#1F1933,rx:10,ry:10 class DES primary class HOST,REM,CLI secondary class CLOUD accent

Designer ships on Windows + macOS in MVP from one MAUI Blazor codebase, desktop only. iPad and Android tablet authoring are Stretch. Web authoring is also Stretch and reuses the same Razor component library compiled for Blazor WASM. The Host runs on iPad, Windows, macOS, and Android tablets. Clients run on iPhone, Android phones, iPad, and Android tablets. The Remote runs on the same Client-class platforms. Per-app responsibilities are in Applications.

In v1 there is no cloud and no authentication on any app. Designer and Host meet over the local network; Clients never reach beyond the local network either. The cloud-backed authoring path on the right is a future stretch goal, sketched here so the v1 architecture stays compatible with it.

Where to go next


Status: Draft Last updated: 2026-04-27

Tech Stack

Component Choice Rationale
Host, Client, Remote Unity (C#) Single engine across the three live-play apps; native 2D and 3D from day one; broad platform reach including iPad / Android tablet; mature animation, particle, and shader tooling. All three apps ship in MVP; the Remote ships with minimum viable controls in MVP and gains its rich control command set in Alpha.
Designer shell .NET MAUI Blazor Hybrid (.NET 8/9, C#, Razor) Single C# codebase reaches Windows + macOS (Catalyst) — desktop only in v1. Razor components run unchanged on both targets. Future Blazor WASM web tool (Stretch) reuses the same component library — code-share is the load-bearing reason for the framework pick. iPad / Android tablet authoring is Stretch and ships from the web Designer codebase. Mac via Catalyst (UIKit-on-Mac). See Designer Shell and Decisions.
Designer UI Razor / Blazor components (MudBlazor library) Tool windows, panels, dialogs, modals, chrome, properties inspector — all Razor components, themeable via MudBlazor's CSS variables. Same component library reused by the future Blazor WASM web tool.
Designer multi-window MAUI Window API Real OS-level windows on Windows + macOS Catalyst, each hosting its own BlazorWebView.
Designer slide preview Unity WebGL build in BlazorWebView Single preview integration across desktop and the future web tool. ~95%+ pixel fidelity vs native Host (WebGL ≠ D3D11/Metal/Vulkan exactly — accepted trade-off). No platform-specific embedding, no HWND/NSView reparenting, no Unity-as-a-Library.
Designer ↔ Preview transport JS bridge (SendMessage + JS callbacks, in-process) The preview runs inside the Designer's BlazorWebView; no inter-process IPC. Razor components push slide state via unityInstance.SendMessage; the WebGL build emits ready / rendered / error events back via [DllImport("__Internal")] callbacks into JS.
Slide preview Unity project Quiz.Preview (WebGL-only build target) Dedicated Unity project, sibling to Quiz.Host / Quiz.Client / Quiz.Remote. Renders a single .quiz slide from pushed JSON state. No editing UI — pure render target driven by the Razor shell. Shares scenes, prefabs, and rendering setup with Host / Client via com.quiz.shared-assets (and com.quiz.runtime where Unity-aware). Replaces the previous Quiz.Designer Unity project, which is renamed and repurposed for this role.
Live-play UI (Host / Client / Remote) UGUI (Unity's prefab/canvas UI system) Creative freedom for live-play visuals — element prefabs, custom shaders, particle effects, animation, "big reveals." The Remote uses UGUI for its mirrored Host-canvas preview.
Shared logic C# class library (.NET Standard 2.1) consumed by all four Unity projects via local UPM packages (see Repository Layout) Quiz schemas, DTOs, scoring rules, and message contracts shared between all four apps. UnityEngine-free, so it also runs in headless test harnesses.
Embedded WebSocket server + client (Unity apps) SimpleWebTransport (James-Frowen, MIT, active 2026) Single library for both server (Host) and client (Client / Remote) inside Unity. Bypasses Unity's broken HttpListener.AcceptWebSocketAsync with its own pure-TCP WebSocket impl. Used by Mirror Networking in production. Resolved OQ#5 2026-05-11.
WebSocket client (Designer) Bare System.Net.WebSockets.ClientWebSocket (BCL) The Designer is .NET MAUI Blazor Hybrid on .NET 8/9 — full BCL access, no Unity constraints. No third-party library needed on the Designer side.
Live-play and control protocol WebSocket with typed message envelopes (validated against shared C# schemas) Lightweight, well-supported on all target platforms; bidirectional; works over plain Wi-Fi without infrastructure. Distinct message families for Designer transfer, Client live-play, and Remote control share the same transport. See Networking.
Session state persistence (Host) Local file (JSON, periodic snapshot) on the Host's local app data directory Crash recovery is an Alpha-phase v1 requirement. Snapshot after every scoring event and every slide advance — small writes, fsync. Format is the same DTOs as the live message envelopes, so deserialise == replay.
Service discovery Makaretu.Dns.Multicast.New (active fork, MIT) Single C# library for both Host advertisement and Client / Designer / Remote discovery. Pure C#; native NSNetService (iOS) / NsdManager (Android) bridges held as fallback if Alpha prototype reveals reliability issues. Resolved OQ#6 2026-05-11.
Camera capture (Client) Unity WebCamTexture Single cross-platform Unity API for team-photo capture at join (F-CL-14). Covers our minimum: capture frame → centre-crop → bilinear downscale to 256 × 256 → JPEG-encode (q75, 96 KB cap). Native pickers + camera-roll access deferred to Beta if UX feedback demands. Resolved OQ#11 2026-05-11.
Authentication & storage None for v1. stretch Cloud account with authentication and storage; vendor TBD when stretch is promoted. v1 is fully local; no auth, no cloud. The stretch-goal cloud backend gives multi-tenant authoring with per-owner isolation. See Backend Schema and Authentication.
2D animation/effects Unity native — UGUI, Animator, particle system, Shader Graph First-class tooling for snappy animations, custom shaders, and "big reveal" moments.
Code-driven tweens DOTween Sequences, easings, and chained tweens for UGUI elements and the Designer's preview. Mature and widely used in Unity; pairs cleanly with Animator/Timeline rather than competing with them.
2D mini-games Unity native Same engine as the rest of the app; no embed required.
3D content Unity native First-class from day one. Whether any v1 object type actually uses 3D is a content decision, not a platform constraint.

Rationale for each load-bearing choice is in Decisions.

Designer Shell

The Designer is a single .NET MAUI Blazor Hybrid app that runs on Windows and macOS (Catalyst) from one C# codebase, desktop only. Razor components form the entire authoring UI — shell chrome, panels, properties inspector, dialogs. Slide preview is a dedicated Quiz.Preview Unity project built only for WebGL and embedded inside a BlazorWebView as a <canvas>. The same WebGL build serves both desktop and the future Blazor WASM web tool (Stretch).

iPad / Android tablet authoring is Stretch and ships from the web-based Designer codebase.

For the rationale and the alternatives that were rejected, see Decisions. For the live stack table see Tech Stack. For the full inventory of authoring-shell entry-points (menus, dialogs, panels, shortcuts, context menus) and where each leads, see Designer surfaces.

Diagram

%%{init: { "theme": "base", "themeVariables": { "fontFamily": "Inter, system-ui, sans-serif", "fontSize": "14px", "primaryColor": "#FFFFFF", "primaryBorderColor": "#FF009F", "primaryTextColor": "#1F1933", "secondaryColor": "#F0EBE3", "secondaryBorderColor": "#16B2EB", "secondaryTextColor": "#1F1933", "tertiaryColor": "#F8F5F0", "tertiaryBorderColor": "#5A536B", "tertiaryTextColor": "#1F1933", "lineColor": "#5A536B", "edgeLabelBackground": "#F8F5F0", "mainBkg": "#F8F5F0", "clusterBkg": "#F0EBE3", "clusterBorder": "#DDD5C8", "titleColor": "#1F1933", "nodeBorder": "#5A536B" }, "flowchart": { "useMaxWidth": false, "htmlLabels": false, "curve": "basis", "padding": 20, "nodeSpacing": 70, "rankSpacing": 80 } }}%% flowchart TB USER([User]) subgraph t1["Tier 1 · MAUI Blazor Hybrid — single .NET 8/9 process · Windows + macOS Catalyst (desktop only)"] direction TB MSHELL[MAUI Shell\napp bootstrap · platform integration] WIN[Multi-window\nMAUI Window API · real OS-level windows] FIO[File I/O\nsave · load · asset import] QM[Quiz Model + Undo\nsource of truth · C#] JSB[JS Bridge\nin-process · SendMessage · JS callbacks] BWV[BlazorWebView\nhosts Razor components · embeds Quiz.Preview canvas] RAZOR[Razor Component Library — MudBlazor\nshell · slide list · palette · properties inspector · dialogs\nreusable in future Blazor WASM web Designer] end subgraph t2["Tier 2 · Quiz.Preview — Unity project (WebGL) · sibling to Quiz.Host / Quiz.Client / Quiz.Remote"] PREV[Quiz.Preview — Unity, WebGL only\nno editing UI · pure render target\nrenders single .quiz slide from JSON state pushed via JS bridge\nemits ready / rendered / error events back to the Razor shell\nshares scenes / prefabs / materials with Quiz.Host + Quiz.Client via com.quiz.shared-assets\n~95-98% pixel fidelity vs native Host - accepted trade-off] end subgraph t3["Tier 3 · Foundation — shared libraries / packages"] direction LR CORE[Quiz.Core\n.NET Standard 2.1 · schema · validation · scoring · .quiz read/write\nreferenced by MAUI Blazor + every Unity project] ASSETS[com.quiz.shared-assets\nUPM package · scenes · prefabs · materials · shaders · fonts\nshared by Quiz.Host, Quiz.Client, Quiz.Remote, Quiz.Preview] RT[com.quiz.runtime\nUPM package · Unity-aware adapters · main-thread marshalling\nreferenced by every Unity project] end USER --> MSHELL MSHELL -->|composes| WIN MSHELL -->|hosts| BWV BWV -->|renders| RAZOR RAZOR <-->|model binding · drag-drop edits| QM RAZOR -->|push state| JSB JSB <-.->|in-process · SendMessage + render events| PREV PREV --> ASSETS PREV --> RT QM -.->|deserialises / serialises| CORE FIO -.->|reads / writes .quiz| CORE classDef primary fill:#FFFFFF,stroke:#FF009F,stroke-width:2px,color:#1F1933,rx:10,ry:10 classDef secondary fill:#F0EBE3,stroke:#16B2EB,stroke-width:2px,color:#1F1933,rx:10,ry:10 classDef accent fill:#F0EBE3,stroke:#961EEF,stroke-width:2px,color:#1F1933,rx:10,ry:10 classDef external fill:#1F1933,stroke:#1F1933,stroke-width:1px,color:#F8F5F0,rx:10,ry:10 class MSHELL,WIN,FIO,QM,JSB,BWV,RAZOR primary class PREV secondary class CORE,ASSETS,RT accent class USER external

Tier 1 — MAUI Blazor Hybrid shell (single .NET process)

One .NET 8/9 process per launched Designer. Targets Windows and macOS (Catalyst — UIKit-on-Mac) from one source tree.

Subsystems:

Tier 2 — Quiz.Preview Unity project (WebGL build, embedded)

A sibling Unity project to Quiz.Host / Quiz.Client / Quiz.Remote, built only for WebGL, embedded in BlazorWebView as a <canvas> via the Unity loader.

Responsibilities:

Quiz.Preview is the renamed Quiz.Designer Unity project, repurposed: the previous project's authoring chrome is gone now that authoring lives in MAUI Blazor. Asmdef and namespace renames follow the project rename.

Quiz.Preview may diverge from Quiz.Host / Quiz.Client in narrow ways the WebGL environment requires (e.g. tap-only input, preview-mode-specific render hints), but scenes, prefabs, materials, shaders, and the overall render setup stay shared via the com.quiz.shared-assets UPM package. Visual fidelity stays aligned with Host / Client without forking the asset tree.

Tier 3 — Foundation

Shared libraries / packages referenced by every consumer:

Pixel-fidelity trade-off

Designer preview is ~95-98% pixel-equivalent to native Host / Client. The same Unity scenes render in WebGL via WebGL2/WebGL1 shader profiles instead of D3D11 / Metal / Vulkan, with subtle drift in shadow filtering, post-processing, and AA.

No pixel-diff harness or CI gate is planned — drift is accepted up-front.

Multi-window

MAUI.Window API; each tool window or palette can be a separate OS-level window, each hosting its own BlazorWebView. Real OS chrome, real OS-level focus, real OS-level minimise / maximise / close.

Editing flow

  1. User opens a .quiz (or starts a new one). Razor shell loads it through Quiz.Core deserialisation.
  2. User selects a slide. Razor design surface renders an approximate / form-driven view of the slide for fast editing.
  3. Razor pushes the slide's state (JSON) to Quiz.Preview over the JS bridge. Quiz.Preview renders the slide pixel-accurate (modulo WebGL drift).
  4. User edits a property. Razor updates Quiz Model (commands → undoable), pushes the delta to Quiz.Preview. WebGL re-renders.
  5. User clicks "Send to Host" — same .quiz-over-WebSocket flow as the live-play path.

Default asset library

The Designer ships with a built-in default library of small, pre-licensed assets the author can drop into a new quiz. The library covers two asset families today:

The library lives inside the Designer install (under the app's resources, not in the quiz). When the author picks a clip / avatar from the library and adds it to the active quiz, the Designer copies the file into the in-memory .quiz package's resources/audio/buzzers/ or resources/avatars/ folder. The selection is registered in the manifest's buzzers[] / avatars[] array. The package therefore stays self-contained — a Host that doesn't have the Designer installed still has every byte it needs to play the chosen jingle or render the chosen avatar.

Library contents and licensing (resolved 2026-05-11):

Family Count in v1 Source / licence
Buzzer jingles 10 CC0 / public domain (Freesound CC0 archives). Zero attribution needed; distributable inside any author-shipped .quiz.
Premade avatars 24 CC0 / public domain (OpenGameArt CC0 archives or commissioned-then-released-CC0). Same distribution terms.

Library updates ride app releases — no content endpoint, no auth, no extra infrastructure in v1. New clips ship when the Designer ships. The author is expected to bring their own assets for any "this is my quiz personality" moment beyond the starter set.

Run from slide — local Host process spawn

Per F-DE-27, the Designer can launch a local Quiz.Host process on the same machine starting at the currently-selected slide. This is the toolbar ▸ Run CTA (and F5). The Designer:

  1. Saves the in-memory quiz to a scratch .quiz (or uses the on-disk path if clean).
  2. Spawns the platform Host binary as a child process, passing --quiz <path> --start-at-slide <index> --launched-from-designer.
  3. The Host opens in operator-window mode (F-HO-25) if multiple displays are connected — audience window on the primary external display, operator window on the laptop / iPad. On a single-display machine the operator surfaces are an overlay.

This is local-machine-only. Network push (Bonjour discovery → pick a remote Host) stays under File → Push to Host (F-DE-14).

Razor component library — MudBlazor

The Razor component library for the Designer is MudBlazor (MIT-licensed, Material-Design-based, actively maintained). Themeable via CSS variables to match the project brand. Multiple production-ready MAUI Blazor Hybrid + MudBlazor starter templates exist in the community.

Multi-window configuration

Multi-window is supported via Application.Current.OpenWindow(...) on Windows and Mac Catalyst.

Build-plan tasks track each.

Repository Layout

How the apps and their shared code are arranged on disk in this monorepo, and how shared code/assets are wired into each app. The high-level decision — one shared C# class library consumed by every app via UPM (Unity) and project reference (MAUI) — is recorded in Decisions; this page is the concrete on-disk realisation.

Top-level structure

Quiz/
├── Quiz.Designer/              # .NET MAUI Blazor Hybrid — authoring app (Win/Mac/iPad)
├── Quiz.Host/                  # Unity project — TV-show app
├── Quiz.Client/                # Unity project — team device app
├── Quiz.Remote/                # Unity project — quizmaster controller
├── Quiz.Preview/               # Unity project — WebGL slide-render target embedded in Designer
├── packages/                   # Shared local UPM packages (source-on-disk)
│   ├── com.quiz.core/          # Pure C# (.NET Standard 2.1) — schemas, DTOs, scoring
│   ├── com.quiz.runtime/       # Unity-aware shared runtime — registry, networking glue
│   └── com.quiz.shared-assets/ # Cross-app prefabs, fonts, brand assets, shaders
├── docs/                       # Generated PDF/HTML artefacts (derived; do not edit)
└── .claude/                    # Knowledgebase, skills, context

Quiz.Designer/ is a .NET MAUI Blazor Hybrid project — a regular .NET solution / .csproj. Razor components form the entire authoring UI; the project bundles a Unity WebGL build of Quiz.Preview/ and embeds it via BlazorWebView. See Designer Shell.

Quiz.Preview/ is a Unity project, sibling to Quiz.Host / Quiz.Client / Quiz.Remote, that builds only for WebGL and is consumed by the MAUI Blazor Designer as its embedded slide-render target. It is the renamed previous Quiz.Designer Unity project; authoring UI moved to the MAUI Blazor Designer, leaving this project as a pure render target. Asmdefs, namespaces, and folder names follow the rename.

Each Unity project sits at the repo root as a sibling, not nested. Unity needs an entire project tree per app (Assets/, ProjectSettings/, Packages/, Library/, etc.) and these trees do not nest cleanly. Sibling folders is the only layout Unity tolerates without per-platform symlink gymnastics.

Because four Unity projects can be open in Unity Editor at the same time, AI tooling that drives the editor must route each call to the correct editor instance. See Unity MCP for the routing protocol — without it, edits land in the wrong app's Assets/ tree.

The on-disk shape inside each project's Assets/_Game/ is documented in Per-App Scaffolding.

Shared code via local UPM packages

Shared code lives under packages/ as one or more local UPM packages. Each Unity project's Packages/manifest.json references them with a relative file: path:

{
  "dependencies": {
    "com.quiz.core":          "file:../../packages/com.quiz.core",
    "com.quiz.runtime":       "file:../../packages/com.quiz.runtime",
    "com.quiz.shared-assets": "file:../../packages/com.quiz.shared-assets"
  }
}

file: references are not a registry round-trip — Unity reads the source files directly out of packages/. Edits made from any of the four projects propagate live with hot reload. The package.json inside each package is the minimal sentinel Unity needs to recognise a folder as a package; it is not a packaging or publishing step.

Option Why not
Symlinks / NTFS junctions from each Assets/ into a shared folder Junctions don't replicate cleanly across git clone; symlinks need core.symlinks=true on Windows; both can produce duplicate-import warnings or GUID churn if two projects ever resolve the same asset via different absolute paths.
DLL drop into Assets/Plugins/ Loses jump-to-definition, no live edits across apps, requires a build step in the shared library on every change. Acceptable as a fallback but a worse developer experience.
Git submodule for shared code Adds a git submodule update step to every clone and every shared-code change, with no benefit over a local folder in the same repo.

Local UPM packages give source-level edits, stable asset GUIDs, and zero extra steps for contributors — git clone produces a working tree.

Package responsibilities

com.quiz.core

com.quiz.runtime

The package split also drives where each kind of test lives — pure-C# edit-mode tests in com.quiz.core/Tests/, Unity-aware play-mode tests in com.quiz.runtime/Tests/, per-app play-mode tests in each Quiz.{App}/Assets/_Game/Tests/. See Testing for the full test layout and TDD convention.

com.quiz.shared-assets

The split is conservative — if any package ends up trivially small at MVP, it can be folded into the next one up. Going the other way (splitting later) is harder because callers fan out.

Assembly definitions and dependency direction

Game-side assemblies in each app  →  Quiz.Runtime  →  Quiz.Core
                                          ↓
                                   (Unity engine APIs)

App-specific code references Quiz.Runtime and (transitively) Quiz.Core. The shared packages never reference app-specific code. This mirrors the module discipline documented in the unity-development skill's modules page: packages are modules, only one level higher up the tree.

Adding a new Unity project later

If a fifth app is ever introduced, the steps are:

  1. Create the Unity project as a sibling at the repo root (Quiz.{Name}/).
  2. Add the three file: package entries to its Packages/manifest.json.
  3. Reference the shared assemblies from its game-side asmdef.

No changes to packages/ are needed — the shared packages stay symmetrical across all apps.

Adding a new shared package later

If a new shared concern emerges that doesn't fit the existing three (e.g. a com.quiz.test-utilities for cross-project test fixtures):

  1. Create packages/com.quiz.{name}/ with a package.json and Runtime/ (and optionally Editor/ and Tests/) folders, each with its own asmdef.
  2. Add a file: entry to every Unity project's Packages/manifest.json that needs it.
  3. Document the new package's responsibility in this page's "Package responsibilities" section.

Separation of Concerns: Business Logic vs Engine Logic

A guiding architectural principle, applied across all four apps: business logic is pure C# and lives in com.quiz.core; engine concerns live in com.quiz.runtime and per-app code. The boundary is enforced by noEngineReferences: true on the Quiz.Core asmdef — com.quiz.core cannot accidentally take a UnityEngine dependency.

This page is the why and the rule of thumb. The on-disk realisation is in Repository Layout; the load-bearing decision behind a single shared C# library is in Decisions.

What counts as business logic (lives in com.quiz.core)

What counts as engine concerns (lives in com.quiz.runtime or per-app code)

Why this boundary

How the Unity-aware layer adapts engine-free code

com.quiz.runtime is intentionally a thin adapter, not a replication of business logic. The shape is consistently:

  1. Instantiate the engine-free objects from com.quiz.core (e.g. WebSocketServer, SessionStateMachine, MessageDispatcher).
  2. Subscribe to their events on whatever thread they fire on.
  3. Marshal those events to the Unity main thread (via SynchronizationContext or a queue drained in Update).
  4. Re-emit them as Unity-friendly C# events / UnityEvents for views and game code to consume.
  5. On the way back out (player input, view actions), call into the engine-free objects directly from the main thread — they're thread-safe at their public API, or document where they aren't.

If a method in com.quiz.runtime is doing more than adapt / marshal / wire-up — if it's deciding something about scoring, protocol state, or session validity — that decision belongs back in com.quiz.core.

Rule of thumb when in doubt

Could this code, conceptually, run on a server with no display? If yes, it's business logic — put it in com.quiz.core. If it would be meaningless without a screen, an audio device, or an input device, it's an engine concern — put it in com.quiz.runtime or per-app code.

The socket layer answers "yes" to that question — sockets don't need a display. So sockets live in core.

Quiz Package Format

Quizzes are stored and transferred as .quiz files (zip archives) with the following structure:

mypub_quiz_v3.quiz
├── manifest.json           # Quiz metadata, schema version, ordered slide list,
│                           #   round groupings, declared object-type registry,
│                           #   buzzers[] + avatars[] registries
├── slides/
│   ├── slide_001.json      # Slide def: id, title, round_id, timing, scoring,
│   │                       #   host_canvas, client_canvas
│   ├── slide_002.json
│   └── ...
└── resources/
    ├── images/             # general slide imagery referenced by elements
    ├── audio/
    │   ├── slides/         # general slide audio referenced by elements
    │   └── buzzers/        # author-bundled buzzer jingles (F-DE-28, F-CL-15)
    ├── video/
    └── avatars/            # author-bundled premade avatars (F-DE-30, F-CL-14)

A .quiz package contains data only — no runtime code. Each element on a slide references an object type by id (e.g. core.text) and version. The Designer can only emit packages whose declared type ids/versions match its own built-in registry; the Host and Client refuse to load a package whose declared types they do not have built-in. Bundle-supplied object types (a object_types/ folder shipping C# behaviours + prefabs alongside the data) are deferred to a stretch goal — see Open Questions.

A slide is the unit of presentation. Each slide carries a Host canvas (a fixed 1920×1080 virtual canvas, scaled to fit the connected display) and a Client canvas (a responsive layout that adapts across phone and tablet form factors). Both canvases hold elements; the same slide can have completely different content on each. (For term definitions see the Glossary.)

An element is a placed instance of an object type with its own per-instance properties. An object type is a pluggable module — see Object-Type Architecture.

A round is a contiguous range of slides sharing a title and scoring metadata — equivalent to a PowerPoint section. Rounds are optional metadata over the slide list, not the unit of presentation.

The manifest.json is validated against a C# schema in the shared class library, so the Designer cannot produce — and the Host cannot accept — a malformed package. The manifest also declares every object type the quiz uses (with required schema versions); apps refuse the package if any declared type is missing or version-incompatible against the app's built-in registry.

A quiz package declares its overall schema version in the manifest. The Host gracefully refuses or upgrades older packages. Maximum package size is 200 MB, capped to keep the always-eager-push live-play model viable on older mobile devices (2–3 GB RAM). This was cut from an earlier ~500 MB ceiling when OQ#8 resolved on always-eager-push without a streaming fallback (resolved 2026-05-11). Authors needing more than 200 MB of media in a single quiz are out of scope for v1.

The manifest also declares a theme (an enum: dark (default), light, plus brand presets) that the Host, Client, and Remote render the quiz against. The Designer's chrome theme is independent of this — an author may author in light theme but ship a dark-themed quiz. Per-quiz custom palette objects (richer than the enum) are tracked in Stretch. Theming overall is Beta scope; MVP and Alpha render against the default dark theme regardless of manifest declaration.

Team-customisation resources

Two resources/ sub-buckets exist solely to feed the team join flow at runtime; neither is referenced by slide elements:

Both arrays are optional. If absent, the Client hides the buzzer picker (Host falls back to a silent buzz tone) and the avatar picker (Client only offers photo capture).

Photos captured by teams at join are not stored inside the .quiz — that is a session-scoped artefact, transmitted over the WebSocket join message and held in the Host's session snapshot only. See Networking.

Designer→Host transfer

The Designer→Host transfer of a .quiz file over local Wi-Fi is described in Networking.

Networking

Local Wi-Fi (primary path, v1)

The Host runs an in-process WebSocket server on its local network interface. It advertises itself on the local network via Bonjour/mDNS. The same server handles three message families:

  1. Designer transfer — receiving .quiz package transfers from a Designer.
  2. Client live-play — running live quiz sessions with team Clients.
  3. Remote control — accepting control connections from the Remote app. MVP carries the minimum viable controls (discovery, pairing, mirror, host-notes, live state, advance/go-back); the rich control command set lands in Alpha.

Each family has distinct typed envelopes defined in the shared C# class library, but they share one transport, one TLS posture, and one server lifecycle.

Designer→Host package transfer

When the Designer is in "send to Host" mode, it discovers Hosts on the local network via the Bonjour advertisement, the author picks one, and the Designer pushes the .quiz file (already saved to local disk via the export action) to the chosen Host over the WebSocket connection.

Gating: manual confirm per push. The Host shows a prompt — "Designer X wants to send 'My Quiz' (50 MB). Accept?" — every time a transfer arrives. One tap to accept. Same-LAN auto-accept was rejected because pub Wi-Fi is shared and a stranger on the network pushing nonsense to the Host would be operationally bad. A future PIN-pairing flow that lets a known Designer auto-accept thereafter is possible but not in v1.

The Host receives the package, validates the manifest, resolves every declared object type against its built-in registry (see Object-Type Architecture), refuses on any mismatch, and otherwise stores it locally for play.

Wire-level details (resolved 2026-05-11):

Aspect Pinned default
Chunk size 64 KB per WebSocket frame
Progress reporting Sender pushes a transfer-progress event after each chunk is acknowledged by the receiver
Integrity CRC32 per chunk; receiver verifies, requests re-send of any failed chunk
Resume after disconnect Receiver tracks offset on disk; sender resumes from the last acknowledged offset on reconnect
Wire compression None (.quiz is already a zip archive — extra compression wastes CPU on both ends)
Max package size 200 MB (see Quiz Package Format)

The full framing schema lives in the (planned) Transfer Protocol document; the defaults above pin the design space so the prototype validates specifics rather than the architecture.

The on-disk format being transferred is described in Quiz Package Format.

Live play and per-Client distribution

Clients discover the Host via the same Bonjour service, connect, and exchange typed messages defined in the shared C# class library. When a Client joins a session, the Host eagerly pushes the slides' Client-canvas content plus the resources those elements reference (images, audio clips that play on the Client, etc.) over the WebSocket. Buzzer-jingle audio (see Quiz Package Format) is not pushed to Clients — those clips play on the Host, not the Client, and the Client only needs the list of { id, name, file } records to render the join-time picker. The Client downloads each individual jingle file lazily when the team taps "preview" on the picker.

Team join — identity payload

The team join message is a single WebSocket frame that carries everything the Host needs to register a new team:

Field Type Required Notes
team_name string Per F-CL-2.
team_colour string (token from .quiz manifest) Beta only Per F-CL-12.
avatar_choice { kind: "photo", jpeg: <base64>, w, h }
\| { kind: "premade", avatar_id: <manifest avatars[].id> }
✓ from Alpha onwards Per F-CL-14. Photo is JPEG, square, target ≤ 64 KB / 256 × 256 px — Client downscales + encodes before sending. Premade refers to a manifest.avatars[].id (Designer-bundled, see package format).
buzzer_jingle_id string | null Alpha (if quiz has buzzers) Per F-CL-15. References manifest.buzzers[].id. null if the quiz bundles no jingles or the team didn't pick one.
client_token string ✓ from Alpha onwards Per-Client persistent identity for crash-recovery rejoin (F-CL-10).

The Host validates the payload, stores the photo / jingle-choice in its session snapshot (so crash recovery preserves them), and broadcasts a "team joined" event to the rest of the session participants.

The photo is held in memory by the Host and written to the session-snapshot file for crash recovery; it is cleared at session end unless cloud-backed team identity (F-HO-23 Stretch) is enabled.

Joining mid-quiz: progress UI + jump-to-current. A Client joining after the quiz has begun shows a progress bar while the eager push transfers; on completion, the Client jumps directly to the slide the quiz is currently on. Earlier rounds are not replayed (acceptable for the pub-quiz format — a late team picks up where the room is). Slides advanced during the eager push are queued and applied in order when the push completes.

Timer authority and clock sync. The Host is authoritative on time. Clients render a local countdown that periodically reconciles with the Host's "time-remaining" tick. On time-up, the Host emits a "lock" message; Clients stop accepting input. Submissions arriving after the lock are rejected — unless the slide is configured (by the author) to accept late submissions, in which case the Host applies the configured scoring rule (no penalty / fixed penalty / per-second decay). The quizmaster (via the Remote, or directly on the Host) can override the timer for a slide live: extend, skip, manually lock, or manually unlock. Override semantics are documented in the (planned) Live Play Protocol document.

Reliability requirements:

The detailed message envelopes, lifecycle, and reconnection semantics belong in the (planned) Live Play Protocol document.

Crash recovery (Alpha onwards)

The Host snapshots session state to disk after every scoring event and every slide advance. State is sufficient to resume: current slide pointer, team list (team_id, team_name, score), per-element state where the element flagged itself "stateful" (e.g. a timer's remaining time at the moment of snapshot, a leaderboard's last reveal index), and the live-play protocol's last sequence number per Client.

If the Host process crashes, the operator relaunches the Host. On launch with a saved session that matches the loaded .quiz, the Host prompts the operator to resume or start fresh. Resume restores the snapshot; the Host re-advertises on Bonjour; Clients reconnect using their persisted team identity (a stable token written to local storage on first join) and re-attach as their original team with their score intact. The reconnecting protocol path is the same one used for Wi-Fi-blip recovery; the only difference is the Host's state was rebuilt from disk rather than from memory.

This is a v1 capability, not a Stretch goal — it lands in the Alpha phase. MVP runs the disconnect/reconnect protocol but does not persist state across a Host restart.

Remote control

The Remote pairs with one Host at a time. Discovery is the same Bonjour advertisement Designers and Clients use. Pairing is gated — manual confirm on the Host the first time a given Remote connects, or QR-code pairing where the Host shows a code that the Remote scans (final UX is a build-plan decision).

Once paired, the Remote opens a control-message-family WebSocket to the Host. Messages flow both ways. The MVP and Alpha cuts of this protocol are:

Direction MVP — minimum viable controls Alpha — rich control commands
Host → Remote Periodic mirror of the Host canvas (scaled-down). Per-slide host-notes from the loaded .quiz. Live state — current scores, current timer remaining, current slide index. (No additions — the same telemetry stream supports the richer commands.)
Remote → Host Advance, go-back. Jump to a specific slide. Trigger element reveals (e.g. show the leaderboard, show the answer). Lock / unlock Client input. Extend / skip the timer. Override scoring per team.

The MVP control-message envelope schema must reserve room for the rich commands so adding them in Alpha is purely additive — no protocol break.

The Remote does not expose any participant-facing surface. It is the quizmaster's tool, not a team's tool.

A session supports zero or one Remote in v1. Multi-Remote (co-quizmasters on separate phones) is not in scope.

Internet-based play (future)

Architecturally accommodated, not a v1 feature — see Stretch. The plan is an optional relay (Cloudflare Durable Objects or similar) that proxies WebSocket traffic between Host and Clients in different locations. Same protocol; different transport.

Session-code join

Discovery shifts from Bonjour to a host-issued session code (F-X-6 Stretch) — a short alphanumeric token (e.g. Q7-3K9-FX) the Host displays prominently on its idle / join screen alongside the existing LAN QR. Clients type the code on the discover screen instead of (or in addition to) picking a Host from the Bonjour list. Remotes do the same on their pairing screen.

The code resolves through the relay to the Host's WebSocket endpoint; from that point on the join + live-play message families are bit-identical to the LAN path. Only the transport — direct WebSocket vs relay-tunnelled WebSocket — differs. This is deliberate: a v1 Client / Host / Remote that's been written against the LAN message envelopes runs over the relay with no protocol change.

Concrete relay design — code allocation, lease + expiry, NAT traversal fallback, billing / abuse controls — is the work tracked in Open Questions #3. UI surfaces for the code-entry flow are tracked in Client surfaces, Host surfaces, and Remote surfaces.

Slide and Object-Type Architecture

The atomic unit a quiz is built from is the slide. A slide owns two canvases (Host and Client) and an ordered list of elements placed on each. Elements are instances of object types.

An object type is a self-contained pluggable module that contributes:

  1. Schema — a C# type (in the shared class library or a plugin assembly) describing the element's data shape, with a declared schema version and a JSON serialisation contract.
  2. Designer editor surface — a Razor component (with backing C# view-model) that lets the author edit an element's properties in the Designer's properties inspector. The pixel-accurate render comes from the Host runtime surface (#3) running in the embedded Quiz.Preview Unity WebGL build via the JS bridge (see Designer Shell).
  3. Host runtime surface — a UGUI prefab + C# behaviour that renders and animates the element on the Host canvas during live play.
  4. Client runtime surface — a UGUI prefab + C# behaviour that renders the element on the Client canvas, including any input handling.
  5. Optional protocol extension — typed message envelopes the object type sends or receives between Host and Client (e.g. an answer submission, a buzzer press). Extensions register on the shared message envelope; the core protocol does not need to change to add a new object type.

Each object type has a globally unique, namespaced type id (e.g. core.text, core.image, core.multiple-choice-input, music.spotify-clip-player) and a schema version. Quiz manifests declare the type ids and versions they use; apps resolve them against a built-in registry compiled into each app and refuse a package they cannot fully resolve. The manifest declaration is part of the Quiz Package Format.

In v1, packages contain no runtime code — every object type a quiz uses must already be present as a built-in in the app's registry. Adding a new object type means adding a new self-contained module to the apps' source tree (it registers itself in the registry on startup) and shipping a new app build; the core orchestration, slide-rendering, and editor code do not change. Bundle-supplied object types — runtime modules carried inside a .quiz — are deferred to a stretch goal alongside cloud-backed authoring (see Open Questions).

Built-in object-type catalogue

The platform ships these built-in object types. Phase columns show when each lands — see Phases. Thirteen types ship across the v1 phases (MVP, Alpha, Beta); a further four are tracked in Stretch as pub-quiz formats that earn their own type rather than being expressed as configuration on existing types.

Type id Phase Purpose Typical placement
core.text MVP Rendered text block. Either canvas.
core.multiple-choice-input MVP Tappable answer options; first/only selection submitted. Client canvas.
core.free-text-input MVP Text-entry answer field. Client canvas.
core.numeric-input MVP Closest-wins scoring instead of exact-match; numeric validation. Essential for estimation rounds and tiebreakers. Client canvas.
core.timer MVP Countdown / elapsed timer. Default behaviour: lock Client input on time-up. Author-configurable per slide: lock-or-not, late-submission scoring rule (none / fixed penalty / per-second decay), and whether the quizmaster can manually extend or skip. The Host is authoritative on time — see Networking. Either canvas.
core.leaderboard MVP Per-team standings. Trigger-driven reveal: the author places the leaderboard wherever in the slide order they want it to appear, configures its reveal trigger (on slide entry / on quizmaster trigger / after delay), and optionally configures a reveal animation (full table / one row at a time / bottom-up). The leaderboard is not Host app chrome that appears automatically — it's an explicitly placed element under author and quizmaster control. Either canvas.
core.image Alpha Static image from the package's resources/images/. Either canvas.
core.audio-clip Alpha Playable audio clip from resources/audio/. Host canvas.
core.video Alpha Video clip from resources/video/. Host canvas.
core.drawing-input Alpha Finger drawing capture (touch-only in v1); can mirror to Host. Client canvas (with optional Host mirror).
core.buzzer-input Alpha Buzzer with first-press semantics across clients. When a team wins the press, the Host plays that team's chosen buzzer jingle (F-HO-27) — a clip the team picked at join from the quiz's bundled set (F-CL-15, F-DE-28). The element itself owns only the press-race protocol + on-canvas visual; jingle resolution happens in the Host's team-identity registry, not in the element. Client canvas.
core.categories-input Beta Multi-line free-text. "Name 5 X." Each line scored independently. Client canvas.
core.mini-game Beta Embeds a mini-game. The mini-game framework itself is a separate build-plan deliverable. Client canvas (typically).
core.match-pairs-input Stretch Drag/connect UX. Multi-pair scoring (each correct pairing scores). Client canvas.
core.ranking-input Stretch Drag-to-reorder UX. Scoring all-or-nothing or partial-credit per item-in-correct-position. Client canvas.
core.hotspot-input Stretch Tap on image coordinates. "Where on the map is X?" / anatomy / geography. Client canvas.
core.true-false-input Stretch Visually distinct from multiple-choice (✓ / ✗ vs A / B). Lower priority — multiple-choice covers it functionally. Client canvas.

Leaderboard and timer are object types (not Host/Client app chrome) so the author has full creative control over when and where they appear. Reveal triggers are a general element capability, not specific to the leaderboard: any element can declare a reveal trigger (on slide entry / on quizmaster trigger / after delay) and a reveal animation. The slide schema reserves a reveal field on every element for this. The detailed plugin contract (interfaces, lifecycle, serialisation rules, version negotiation, the reveal field shape) belongs in the (planned) Object Types document.

Shared element properties

Every placed element — regardless of object type — carries the same shared property block in addition to its object-type-specific fields. These are the fields a generic editor (move / lock / reveal) needs to operate on any element, and the Designer's right-pane Properties tab renders this block once at the top followed by the object-type-specific editor below.

Property Type Description
id string (UUID) Stable per-element identifier; survives reorder and clipboard ops. Used by reveal-trigger commands and protocol messages targeting a specific element.
name string (optional) Author-facing label. Defaults to the object type's display name; surfaces in the slide outline, error messages, and reveal-target picker.
type string Object-type id (e.g. core.text, core.multiple-choice-input). Bound at insertion; not re-editable.
canvas host | client Which canvas the element lives on. An element can be duplicated to the other canvas (independent instance), but a single element is on exactly one.
transform.host { x, y, w, h, rotation, z-order } Host-canvas placement — absolute coordinates against the fixed 1920×1080 virtual canvas. Present only when canvas == host.
transform.client { anchors, region \| stack-slot, z-order } Client-canvas placement — responsive layout. Anchors and region pin the element to one of the slide's declared regions / stacks. Present only when canvas == client.
visibility always | triggered always = visible from slide-entry. triggered = hidden until its reveal.trigger fires.
reveal.trigger on-entry | on-cue | after-delay When this element appears (or animates in) on the canvas during play. on-cue is fired by the quizmaster from the Host operator window or paired Remote (F-HO-25, F-RE-9).
reveal.delay-ms int Used only when reveal.trigger == after-delay. Milliseconds from slide-entry.
reveal.animation string Animation key — fade, slide-in-left, pop, etc. Animations are part of the design language; the catalogue lands in Beta. MVP supports none and fade.
lock bool Designer-only flag. When true, the canvas selection chrome prevents drag / resize / rotate; inspector edits remain available. Lands in Beta.
notes string (optional) Internal author notes about the element (not the slide-level host-notes per F-DE-19). Lands in Beta.

The shared block is serialised once per element; per-object-type schemas extend it. The slide schema is canonical and pinned in the shared C# class library — see Open Questions #2. The right-pane inspector renders the shared block in a fixed order (Identity → Transform → Reveal → Lock) followed by the object-type editor.

For the per-app UI inventory of where these properties surface in the Designer (inspector sections, context-menu items, lock badge, reveal cue affordance) see Designer surfaces — §8 Right pane.

Authentication

v1: No authentication on any app. The Designer, Host, and Client all run unauthenticated. Designer→Host transfer is gated by being on the same local network (and the Host accepting the incoming connection through its UI), not by credentials. See Networking for the transfer model.

Stretch goal (alongside cloud-backed authoring):

Backend Schema — stretch goal, not v1

Cloud-backed authoring (account, library, Designer→cloud upload, Host→cloud download) is in Stretch. v1 has no cloud backend; the Designer saves .quiz files to disk and transfers them to the Host over the local network — see Networking. The schema below sketches the entities so the v1 architecture stays compatible with the eventual cloud path. The vendor (managed Postgres + S3-compatible object storage) is decided when the stretch is promoted; the SQL shape below is illustrative, not vendor-specific.

profiles
  id uuid PK references the auth provider's user id
  display_name text
  created_at timestamptz

quizzes
  id uuid PK
  owner_id uuid FK → auth user id
  title text
  description text
  tags text[]
  current_version_id uuid FK → quiz_versions
  created_at timestamptz
  updated_at timestamptz
  deleted_at timestamptz   -- soft delete

quiz_versions
  id uuid PK
  quiz_id uuid FK → quizzes
  version_number int
  storage_path text        -- path in object storage
  size_bytes bigint
  notes text
  pinned boolean default false  -- protect from version pruning
  created_at timestamptz

Storage layout: quizzes/{owner_id}/{quiz_id}/v{n}.quiz

Tenant isolation: users can SELECT/INSERT/UPDATE/DELETE only rows where owner_id matches their authenticated identity. Object-storage paths are similarly scoped to the user's own prefix. Whether this is enforced via row-level security at the database or via the application layer depends on the chosen vendor, decided when this stretch is promoted.

Version retention: the latest 20 versions per quiz are kept, plus any versions marked pinned = true. Older versions are pruned by a scheduled job. Soft-deleted quizzes are recoverable from a "trash" view for 30 days.

Auth flows on top of this schema are covered in Authentication.

CI / CD

The project uses Azure DevOps Pipelines for continuous integration and deployment. Source lives in Azure Repos; pipelines are defined in YAML in this repo and run on a mix of Microsoft-hosted agents and a project-owned self-hosted Mac mini agent.

Why Azure DevOps Pipelines

Agent pool layout

Agent Hosted by Purpose
Azure Pipelines (Microsoft-hosted) Microsoft Windows builds (Quiz.Designer MAUI Win, Quiz.Host / Client / Remote Unity native Windows player), shared C# analysers + tests, .NET Standard 2.1 library tests, generic CI tasks.
MacMini (self-hosted) Project macOS Catalyst + iPad / iOS builds (Quiz.Designer MAUI for those targets), Quiz.Host / Client / Remote Unity macOS + iOS Player builds, Quiz.Preview WebGL build, code-signing, notarisation.

The Mac mini runs the latest macOS supported by Apple's developer toolchain, has the current Xcode + iOS / Catalyst SDKs installed, and holds the project's signing certificates and provisioning profiles in the macOS Keychain.

Pipelines

Each YAML pipeline lives at the repo root under .azure-pipelines/. Pipelines are intentionally small and composable; multi-stage pipelines orchestrate them.

Pipeline Trigger Agent What it runs
pr-validation.yml Pull request Both pools as needed Build everything that compiles on each agent + run all fast tests + lint + format. Required check before merge.
quiz-core.yml Push to main touching packages/com.quiz.core/ or its tests Microsoft-hosted dotnet test headless; .NET Standard 2.1 library; no Unity.
quiz-runtime.yml Push to main touching packages/com.quiz.runtime/ Mac mini (Unity license) Unity edit-mode + play-mode tests under com.quiz.runtime/Tests/.
quiz-host.yml Push to main touching Quiz.Host/ Mac mini for macOS / iOS Player; Microsoft-hosted for Windows Player Unity Player builds for the configured Host platforms; play-mode tests.
quiz-client.yml Push to main touching Quiz.Client/ Mac mini for macOS / iOS Player; Microsoft-hosted for Windows / Android Same shape as Host.
quiz-remote.yml Push to main touching Quiz.Remote/ Mac mini / Microsoft-hosted as appropriate Same shape.
quiz-preview-webgl.yml Push to main touching Quiz.Preview/ or packages/com.quiz.shared-assets/ Mac mini (Unity license) Unity WebGL Player build of Quiz.Preview/; output published as a pipeline artefact consumed by quiz-designer.yml.
quiz-designer.yml Push to main touching Quiz.Designer/, or successful quiz-preview-webgl.yml artefact Microsoft-hosted (Win); Mac mini (macOS Catalyst + iPad) .NET MAUI build of Quiz.Designer/ for each target framework, bundles the published Quiz.Preview WebGL artefact under wwwroot/, runs Razor / xUnit unit tests.
release.yml Manual run / git tag Both pools Signs, packages, and publishes installers to the configured store / distribution channels.

Pipelines that need a Unity license use a shared "Unity" service connection holding the activation file; the activation is cached on the Mac mini agent so cold starts are fast.

Caching + artefacts

Branching + PR policy

Local mirror

.azure-pipelines/ is checked in. Devs can lint pipeline YAML locally with az pipelines runs list and validate with az pipelines validate. The pipeline definitions are owned end-to-end by the repo, not the ADO portal, so changes are reviewed in PR alongside the code they validate.

Testing

How testing is practised on this project. This page is the development-process source of truth — the Build Plan deliberately does not restate test tasks per item; instead the build plan's Definition of Done references this page.

Test-driven development is the default

For feature work, write the failing test first, make it pass with the smallest viable change, then refactor. The "feature work" boundary is anything that delivers user-visible behaviour, exercises a network protocol, or touches scoring/persistence/rendering pipelines.

The following work is excepted from TDD:

Anything else: failing test first.

Where each kind of test lives

Layer Location Mode Why
Pure C# domain — schemas, scoring, protocol state, package format packages/com.quiz.core/Tests/Runtime/ Edit-mode com.quiz.core is .NET Standard 2.1 with noEngineReferences: true. Tests run headless under NUnit, no Unity Player needed. Fast in CI.
Unity-aware shared adapters — registry impl, slide adapters, networking glue packages/com.quiz.runtime/Tests/Runtime/ Play-mode Touches MonoBehaviour lifecycle, main-thread marshalling. Requires the Unity Player.
App-specific behaviour — Designer authoring flows, Host session, Client team-play, Remote control Quiz.{App}/Assets/_Game/Tests/ (each app) Play-mode Exercises scenes, prefabs, UI interactions. Per-app because behaviour diverges.
App boot-chain smoke (Setup → MainMenu/Designer) Quiz.{App}/Assets/_Game/Tests/Smoke/ Play-mode Per-app — verifies the boot chain reaches its target scene with zero console errors. Replaces the manual MCP smoke-tests once they exist.

Each test folder owns its own asmdef referencing only the assemblies under test plus nunit.framework. The com.wildfiregames.core test fixtures (if any) are not used directly — test the project's code, not the third-party.

Conventions

CI gate

Azure DevOps Pipelines runs the test suites on every PR and every push to main:

Local fast feedback: edit-mode tests run from Unity's Test Runner window in <1 s per fixture; CLI parity via Unity.exe -batchmode -runTests -testPlatform editmode for Unity, dotnet test for Quiz.Core and Quiz.Designer.

Smoke-test workflow during scaffolding

Before automated play-mode smoke tests are wired up, the scaffolding workflow uses Unity MCP as a lightweight stand-in:

  1. set_active_instance to the target editor.
  2. read_console action=clear.
  3. manage_editor action=play.
  4. Wait for the boot chain.
  5. read_console types=["error","warning"].
  6. manage_editor action=stop.

This is a manual cross-check, not a regression gate. Once the per-app Tests/Smoke/ suite lands, that suite becomes the gate and the manual MCP procedure is retired.

Cross-references

AI Tooling

The project uses AI tooling throughout the development workflow to accelerate implementation, surface issues early, and keep code quality high.

Tools

Tool Role
Claude Code Primary AI coding assistant. Drives feature implementation, refactoring, code review, and knowledgebase maintenance.
OpenCode Secondary AI coding assistant. Complements Claude Code on tasks where a different model behaviour is preferred or when running in parallel on independent work.
Self-hosted Qwen3.5 LLM Local fallback for both Claude Code and OpenCode when usage limits are hit on the primary providers. Keeps the team unblocked without paying for additional cloud-LLM throughput.

No other AI providers are part of the project's tooling.

How AI tooling is used

Conventions

The detailed multi-editor routing protocol the AI tooling uses to drive the four open Unity Editors (Host, Client, Remote, Quiz.Preview) lives in the team-facing unity-mcp.md page; that level of detail is operational, not requirements-level, and lives in the knowledgebase rather than the PRD.

Per-App Scaffolding

The on-disk shape each Unity project converges on. Covers Host, Client, Remote, and the renamed Quiz.Preview render-only project. The MAUI Blazor Designer is a separate .NET project — see Designer Shell.

com.wildfiregames.core (third-party UPM) provides the underlying Registry, GameBehaviour, SceneReference, audio system, event queue, pooling, settings, and entity infrastructure every Unity project depends on. com.quiz.runtime (the local UPM package described in Repository Layout) holds the Quiz-domain Unity adapters: object-type registry implementation, slide/element MonoBehaviour scaffolding, networking glue. Game-loop bootstrap (PersistentSystems, SaveSystem, AudioController, scene loaders) lives per-app because each app's needs diverge quickly.

Universal conventions

Apply to all four Unity projects (Host, Client, Remote, Quiz.Preview).

Quiz.Preview designer

A Unity project, sibling to Host / Client / Remote, built only for WebGL — see Designer Shell.

Scenes: - Preview/Preview.unity — single render scene with the slide canvas + camera; entry point for the WebGL boot.

Per-scene scripts: - Preview/Scripts/PreviewBoot.cs — listens for JS messages (LoadSlide, UpdateProperty, SetSelection); calls back via [DllImport("__Internal")] for ready / rendered / error events.

References: com.quiz.shared-assets, com.quiz.runtime, com.quiz.core via Packages/manifest.json.

Project Settings: - WebGL build target only. - defaultScreenWidth/Height flexible (canvas size driven by the host page). - WebGLMemorySize tuned for the largest expected scene.

Host host

The live-quiz runtime — renders the quiz to a TV / projector and runs the in-process WebSocket server. Two top-level scenes:

Per-scene scripts and prefabs: - MainMenu/Scripts/MainMenuViewController.cs — drives the lobby UI. - Play/Scripts/PlayController.cs — orchestrates slide advance, element runtime, scoring, and session state. - _Common/Resources/PersistentSystems.prefab and _Common/Scripts/PersistentSystems/* — audio bus + persistent loader. - _Common/Scripts/SaveSystem/* — session-state persistence per Networking — Crash recovery.

References: com.quiz.shared-assets, com.quiz.runtime, com.quiz.core.

Project Settings: - defaultScreenOrientation: 3 (LandscapeLeft); portrait autorotates disabled. - defaultScreenWidth: 1920, defaultScreenHeight: 1080 (TV / projector target).

Client client

The team-device app — joins a session, renders each slide's Client canvas, collects answers. Two top-level scenes:

Per-scene scripts and prefabs: - MainMenu/Scripts/MainMenuViewController.cs — discovery + join UI. - Play/Scripts/PlayController.cs — slide rendering, input dispatch, score display. - _Common/Resources/PersistentSystems.prefab + scripts — audio + persistent loader. - _Common/Scripts/SaveSystem/* — team-identity persistence per F-CL-10.

References: com.quiz.shared-assets, com.quiz.runtime, com.quiz.core.

Project Settings: - defaultScreenOrientation: 4 (AutoRotation) — Client supports portrait and landscape on phones and tablets.

Remote remote

The quizmaster's pocket controller — pairs with a Host, mirrors its display, sends control commands. Two top-level scenes:

Per-scene scripts and prefabs: - MainMenu/Scripts/MainMenuViewController.cs — discovery + pairing UI. - Play/Scripts/PlayController.cs — mirror rendering, host-notes display, live-state display, command dispatch. - _Common/Resources/PersistentSystems.prefab + scripts — audio + persistent loader.

References: com.quiz.shared-assets, com.quiz.runtime, com.quiz.core.

Project Settings: - defaultScreenOrientation: 1 (Portrait); landscape autorotate disabled, portrait autorotate enabled.

Key Architectural Decisions and Tradeoffs

Load-bearing decisions, with the rationale that justifies each. Decisions are recorded so they are not relitigated without a good reason.

Unity for live-play apps (Host, Client, Remote); .NET MAUI Blazor Hybrid for the Designer shell, with an embedded Unity WebGL preview via BlazorWebView. Unity gives a single engine for live-play visuals (animation, particle, shader, 2D and 3D) and unifies the build/CI shape across the three live-play apps. The Designer is forms-heavy authoring (slide list, palette, properties inspector, modal panels, menus) plus a slide preview — concerns where Razor components reach Windows + macOS (Catalyst) in a single C# codebase, and where the same component library is reusable for the future Blazor WASM web tool (Stretch). Mac uses Catalyst (UIKit-on-Mac). See Tech Stack and Designer Shell.

Slide preview is a dedicated Quiz.Preview Unity project, WebGL-only, embedded in BlazorWebView. Quiz.Preview is a sibling Unity project to Quiz.Host / Quiz.Client / Quiz.Remote that builds only for WebGL and serves as the slide-render target for the Designer (and the future web Designer). It shares scenes, prefabs, materials, and rendering setup with Host / Client via the com.quiz.shared-assets UPM package — visual output stays aligned across the four Unity projects without forking the asset tree. Razor components push slide state to Quiz.Preview over the in-process JS bridge (unityInstance.SendMessage); the WebGL build emits ready / rendered / error events back via [DllImport("__Internal")] callbacks. Two Unity build targets across the broader product: native (Host / Client / Remote) and WebGL (Preview).

Pixel-accuracy is ~95-98%. WebGL renders differ from native D3D11 / Metal / Vulkan in shadows, AA, and post-processing. Accepted trade-off for one preview integration that serves desktop and the future web tool with no per-platform native plumbing.

The Designer ships desktop only in v1 — Windows + macOS (Catalyst) — single MAUI Blazor codebase. iPad and Android tablet authoring are deferred to Stretch and ship from the web-based Designer codebase. v1 Designer focus is the deepest authoring workflow on desktop (multi-window, real OS chrome, file pickers, drag-drop) without compromising for the touch surface.

One shared C# class library, consumed by every Unity project via UPM and by the MAUI Blazor Designer via project reference. The library is .NET Standard 2.1 and UnityEngine-free. Schemas, scoring logic, message contracts, the WebSocket protocol state machine, and mDNS coordination logic are all shared; UI, rendering, audio, input, and scene code is per-app. This avoids RPC-style network code between apps that don't need to talk to each other directly — the Designer and Host meet in the cloud, not over the wire — and lets headless test harnesses reference the library too. The on-disk realisation — pure-C# com.quiz.core, Unity-aware com.quiz.runtime, and com.quiz.shared-assets for cross-app prefabs — is in Repository Layout. The pure-C# / Unity-aware boundary is in Separation of Concerns.

Server embedded in the Host (not a separate process). Simpler deployment, fewer moving parts. The Host always operates in a foregrounded, user-attended state during a quiz, which is the state in which all target platforms (iPad, Windows, macOS, Android tablet) can reliably run an in-process server. The same server handles three message families: Designer transfer, Client live-play, and Remote control. Unity scripts run on the main thread, so the WebSocket server runs on a background thread and dispatches state changes back via Unity's main-thread synchronisation primitives. See Networking.

Slide-based authoring with two independent canvases per slide. A quiz is an ordered list of slides, each with a Host canvas (TV-format, fixed 1920×1080 virtual) and a Client canvas (phone/tablet, responsive). The two canvases share the slide's identity, timing, and scoring but can hold completely different elements — the question text might fill the Host canvas while the Client canvas shows tappable answer options. The split lets the same quiz be a "TV show" experience on the projected display and a snug, one-handed phone experience for participants, without the author maintaining two parallel quizzes.

Object-type plugin model is first-class from v1. The schema is modular: new element types can be added without changing core code. This shapes the Quiz Package Format (manifest declares object types by id/version), the shared class library (defines the plugin contract; ships no concrete object-type implementations), and the apps (each maintains a built-in registry that types register themselves into on startup). For v1, every object type a package uses must already be present as a built-in on every app loading the package; bundle-supplied object types are deferred to a stretch goal. See Object-Type Architecture.

v1 is fully local — cloud-backed authoring is a stretch goal. v1 has no auth, no account, no cloud library. The Designer exports .quiz files to disk and sends them to the Host over the local network via a UI action; the Host loads packages from disk. The architecture stays compatible with a future cloud path so adding it later is additive, not a rewrite. Cloud vendor (managed Postgres + S3-compatible object storage) is decided when the stretch goal is promoted. See Backend Schema.

Multi-tenant in the eventual cloud schema, even though authoring starts single-user. Designing for multi-tenancy when the cloud path lands is cheaper than retrofitting it.

v1 .quiz packages contain no runtime C# code. Packages are data only (manifest, slides, resources). Every object type a quiz uses must already be a built-in on the Host and Client, version-matched. Avoids the security/sandboxing/signing surface of loading bundled C# at runtime. Bundle-supplied object types are deferred to the cloud-backed-authoring stretch goal.

Host eagerly distributes per-Client slide content at join time. When a Client connects to a session, the Host pushes the full set of Client-canvas content and Client-side resources for the quiz over the WebSocket. Per-slide play then issues only "show slide N" commands — no per-slide network round-trips, no stutter waiting for media to download mid-display. The trade-off is a larger payload at join (typically tens of MB for a media-heavy quiz, well within local Wi-Fi bandwidth), which is the right side of the trade for a single-room quiz format.

Local network is the primary live-play transport, not a fallback. Once a quiz is on the Host, no internet is required. Hard requirement because pub Wi-Fi cannot be relied on. Internet-based live play is a future, optional transport — not a substitute for the local-network primary path.

Host persists session state to disk; crash recovery is a v1 requirement, not Stretch. The Host writes a session snapshot (current slide, team list, scores, slide-element state) after every scoring event and every slide advance. If the Host process crashes, the operator relaunches the Host with the same .quiz and resumes the session — Clients reconnect using their persisted team identity and re-attach as the same team. The cost is small (single JSON file, frequent small writes) and the outcome is materially better operational behaviour for what is a live, time-pressured event. Alpha-phase deliverable.

The Remote app is a fourth distinct app, not a mode of the Client. A quizmaster walking the room with a controller is a different ergonomic problem from a team submitting answers. Folding both into one app conflates roles, complicates pairing/auth, and makes the per-slide UI compromise (control panel vs answer surface). Separate apps keep each role's UI clean and the message families distinct on the wire.

3D content is a first-class option from day one. Unity makes 3D scenes and effects native rather than bolt-on. Whether any v1 object type uses 3D is a content decision driven by the built-in object-type catalogue, not a platform constraint.

Requirements

Functional Requirements

Numbered functional requirements per app, plus cross-cutting requirements. Per-app responsibilities and platforms are in Applications; per-app UI entry-point inventory in UI Surfaces; non-functional targets are in Non-Functional Requirements. Each requirement is tagged with its target Phase.

Phase tags: MVP, Alpha, Beta, Production, Stretch. The Stretch tag means out-of-scope for the initial launch (v1) and tied to features in Stretch.

Designer designer

Host host

Client client

Remote remote

The Remote app ships in MVP with minimum viable controls — discovery, pairing, mirror, host-notes, live state, and the core navigation commands (advance / go-back). The richer control-command set lands in Alpha (F-RE-9).

Cross-cutting project

Non-Functional Requirements

Area Requirement
Reliability Live quiz sessions must survive client disconnects, the Host backgrounding briefly, and minor Wi-Fi instability. No single client misbehaving should affect other clients or the Host. Crash recovery (Alpha onwards): if the Host process crashes, relaunching the Host with the same .quiz loaded must restore the session — current slide pointer, team list, and scores — and accept reconnecting Clients as their original teams.
Performance UI animations target 60 fps on iPhone 12 / equivalent Android phone or tablet and above; ≥ 60 fps on supported desktops (Windows, macOS). Question transitions complete within 500 ms of trigger on the Host. Client message round-trip target under 200 ms on local Wi-Fi. Remote control message round-trip target under 200 ms on local Wi-Fi.
Compatibility Designer: Windows 10/11, macOS 12+ (desktop only in v1). Host: Windows 10/11, macOS 12+, iPadOS 16+, Android 12+ tablets. Client and Remote: iOS 16+, Android 11+; also runs on iPad and Android tablets. iPad / Android tablet authoring is Stretch. All minimums are provisional — final confirmation against Unity's Editor and Player support matrix and real-device testing happens in the Beta phase.
Quiz scale Support sessions of up to ~50 concurrent Client devices (one per team — see Glossary) on local Wi-Fi. With typical team sizes that covers ~150–300 participants per pub quiz; well beyond any realistic single-room format.
Quiz package size Up to 500 MB per package (covers media-heavy rounds with audio and video).
Storage v1: local-disk only on the Designer and Host (subject to device free space). stretch Cloud quotas: 5 GB per author; version retention: latest 20 versions plus any pinned versions.
Privacy v1: no cloud account, so author data lives only on the Designer device and any Host they push to. Team names are ephemeral and not stored beyond the session. stretch Author data in the cloud is private to their account.
Offline operation v1 is fully local — neither Designer nor Host nor Client requires internet at any point. Designer→Host transfer needs same-LAN connectivity, not internet. stretch Once a cloud quiz package is downloaded to the Host, the entire live experience runs without internet access.

Phases

MVP

Goal: Prove the production architecture end-to-end on every supported platform, with the smallest feature set that still constitutes a working pub quiz. The output is a thing an author can use to design a quiz and run it for one or more teams in a room — no media, no minigames, no polish, but real.

This is the architecture. Nothing here is throwaway prototype code; the choices made in MVP carry through to Alpha, Beta, and Production.

In scope

Architecture and platform

Networking and transfer

Schemas and the object-type plugin contract

Built-in object types (MVP cohort)

Type id Notes
core.text Either canvas. Reference implementation of the plugin contract.
core.multiple-choice-input Client canvas. Tap to submit one option.
core.free-text-input Client canvas. Text-entry, submit answer.
core.numeric-input Client canvas. Numeric validation; closest-wins scoring; essential for tiebreakers and estimation rounds.
core.timer Either canvas. Countdown / elapsed. Foundational to most quiz formats.
core.leaderboard Either canvas. Per-team standings.

The other seven object types (core.image, core.audio-clip, core.video, core.drawing-input, core.buzzer-input, core.categories-input, core.mini-game) are deferred to Alpha or Beta.

Designer behaviours

F-DE-2 to F-DE-14, F-DE-18, F-DE-19, F-DE-20: create quiz, slides, rounds; place / move / configure elements on both canvases; object-type palette listing the MVP cohort; embedded preview; per-slide and per-round timing and scoring; per-slide host-notes; per-element reveal trigger; save and re-open .quiz files; discover Hosts and push over LAN; canonical .quiz package format.

Out: F-DE-1 (cloud auth), F-DE-15/16 (cloud save/version/trash), F-DE-17 (stylus). All are stretch.

Host behaviours

F-HO-3 to F-HO-17, F-HO-20, F-HO-21: WebSocket server + Bonjour; receive Designer push; load .quiz from disk; offline; resolve object-type registry; start session; join screen showing connected teams; eager push on join; advance through slides; receive answers, score, leaderboard; handle disconnect/reconnect; timer-authority and live override; pair with a Remote and stream mirror + host-notes + state; accept advance/go-back commands from the Remote.

Out: F-HO-1 (auth), F-HO-2 (cloud library), F-HO-24 (rich Remote command set — Alpha).

Client behaviours

F-CL-1 to F-CL-9, restricted to the MVP object-type cohort. One shared device per team; team enters team name; receive eager push; render Client canvas; submit multiple-choice and free-text answers; show team score; reconnect.

Out: drawing input, buzzer input, mini-games — those object types come in Alpha or Beta.

Remote behaviours (minimum viable controls)

F-RE-1 to F-RE-8: the minimum viable Remote controller. Discover Hosts; pair with one; open the control-message-family WebSocket; render a live mirror of the Host's display; show per-slide host-notes; show live session state (scores, timer remaining, slide index); send the core navigation commands (advance, go-back); reconnect after Wi-Fi blips.

Out: F-RE-9 — the rich control command set (jump-to-slide, trigger reveals, lock/unlock Client input, extend/skip timer, override scoring per team) — lands in Alpha alongside F-HO-24. Latency / soak verification of the Remote control loop is also Alpha.

End-to-end vertical slice

Explicitly not in scope

Acceptance criteria

How success is measured

Alpha

Goal: Internal testing builds capable of hosting a full quiz. Every built-in object type is in place except the mini-game framework. The Remote app — already shipping in MVP with minimum viable controls — gains its rich control command set. Crash recovery becomes operational. Quality is "developer-acceptable" — the visual/motion polish, cross-cutting design language, and final brand treatment still come in Beta.

The scope shift from MVP is breadth and operational hardening: more object types, richer Remote controls, and the persistence/recovery work that makes a session safe to run for real.

In scope

Object types added in Alpha

Type id Why it lands here
core.image Static media is the lowest-risk media type; unlocks image-based rounds.
core.audio-clip Music rounds are a defining pub-quiz format; pairs naturally with core.free-text-input and core.multiple-choice-input.
core.video Builds on core.audio-clip's media-loading work; less common than audio for pub quizzes but completes the media set.
core.drawing-input First object type to introduce a Client→Host live mirror channel; protocol-extension exercise. Touch-only in v1 — stylus support is Stretch.
core.buzzer-input First-press semantics across multiple Client devices; tests fairness and the message round-trip target.

Each object type implements the full plugin contract (schema, Designer editor surface, Host runtime, Client runtime, optional protocol extension) and ships with an end-to-end test exercising a slide that uses it.

Remote rich control commands

The Remote app itself ships in MVP with minimum viable controls (F-RE-1 through F-RE-8 — discovery, pairing, mirror, host-notes, live state, advance / go-back, reconnect). Alpha layers on the rich control command set — F-RE-9 and the matching F-HO-24:

These are the controls that turn the Remote from "advance the deck" into "fully run the room from your pocket". They land in Alpha because each one cuts across non-trivial Host state (timer authority, scoring rules, element reveal triggers) and benefits from the operational hardening that lands alongside (crash recovery, soak testing).

Designer additions for Alpha

Team customisation at join

Lands alongside core.buzzer-input since the buzzer-jingle pick only makes sense once buzzer slides are playable, and the photo + avatar palette gives the leaderboard something to render the moment any Alpha element runs:

The team-photo binary travels in the join WebSocket message and lives in the session snapshot for crash recovery; it's cleared at session end (Networking — Team join).

Crash recovery (operational hardening)

This is the load-bearing piece of Alpha — see Networking — Crash recovery and the new requirements:

The reconnection protocol path is the same one used for Wi-Fi-blip recovery — only difference is whether the Host's state was rebuilt from disk or in-memory. This means the Wi-Fi-recovery work in MVP and the crash-recovery work in Alpha are largely the same code path, with Alpha adding the on-disk snapshot.

Animation and reveal beats

Reliability and performance

Documentation

Explicitly not in scope

Acceptance criteria

How success is measured

Beta

Goal: A polished build that Quiz UK can run with real teams in a real venue. Mini-games are introduced. The platform looks and moves like the Design Specification describes, not like a developer build.

The scope shift from Alpha is polish + mini-games — the loud, performative parts of the experience that the brand identity promises.

In scope

Mini-game framework

Categories question type

Cross-cutting design language

The full Design Specification is applied across Designer, Host, Client, and Remote:

This includes Quiz UK validation — the mascot, branding, and final brand name are confirmed and applied.

Theme system

Two layers of theming, both Beta:

The two layers are independent. Richer per-quiz custom palettes are Stretch.

Sound design

Full audio language ships in Beta (F-X-5): branded stings (correct / incorrect / lock / time-up / big reveal / end of round / end of quiz), transition motifs, and an optional ambient music bed. Audio is mixed for the venue — stings cut through pub ambient noise without being intrusive. Licensing/sourcing decisions belong to Beta-phase work.

Mascot animation rig

The mascot is rigged once (a single Unity-native rig in the shared assets package) and reused across the Designer, Host, Client, and Remote. Each app picks the appropriate pose from a shared animation library — waving on welcome, cheering on big reveals, sleepy on end-of-quiz.

Per-team theming

A team picks a colour and avatar at join (from a fixed author-configured palette inside the quiz, so brand consistency is preserved). The choice carries through every Client surface, every leaderboard row, and any Host moment that renders per-team identity.

Visual flair beyond the team name. Lands here because Beta is the polish phase — not in MVP/Alpha (where it's deliberately utilitarian), and not Stretch (because it doesn't depend on cloud and adds meaningful UX value to a full Beta playthrough).

Real-device validation

Quiz UK pilot

Explicitly not in scope

Acceptance criteria

How success is measured

Production

Goal: A launch build distributed through real channels to real users — quizmasters beyond Quiz UK. Polish is locked; the remaining work is the commercial and operational layer.

The scope shift from Beta is launch readiness: distribution, identity, store listings, privacy, and operational posture.

In scope

Distribution

Confirmed distribution channels:

Each channel adds work for: signing, packaging, submission flow, and update mechanism.

Identity and policy

Hardware and OS minimums

The provisional minimums in Non-Functional Requirements are finalised in Beta, not Production. By Production they are locked. Production work is to ensure each store listing accurately reflects the locked minimums and that the app refuses to run (with a clear message) below them.

Operational

Explicitly not in scope

Acceptance criteria

How success is measured

Process

Project Management

Project management runs in Azure DevOps Boards, in the same project that hosts the code repository and CI / CD pipelines.

Workflow from PRD sign-off to delivery

  1. PRD sign-off. The PRD is the locked-in requirements artefact, generated from the knowledgebase by the docs skill. Sign-off ends "what are we building" debate; subsequent changes go through a controlled change process (re-edit the knowledgebase, regenerate, re-sign-off).
  2. Decompose into Epics + Features. Each major area of the PRD becomes one Epic in ADO. Features sit under Epics and represent shippable user-facing capabilities.
  3. Decompose Features into Stories + Issues. Stories are user-shaped slices of a Feature; Issues are technical or bug-fix items inside a Story or directly under a Feature. Sprint-sized.
  4. Sprint planning. The team commits to Stories / Issues for the upcoming sprint based on capacity and priority. The sprint backlog is the day-to-day work queue.
  5. Sprint execution. Work flows across the board columns below. Daily standup checks for blockers and surfaces "Ready For Test" work to QA.
  6. Sprint review + retro. End-of-sprint demo of "Done" work; retrospective captures process improvements.

Work-item hierarchy

Epic
└── Feature
    ├── Story
    │   └── Issue (task-level)
    └── Issue (standalone)
Level Used for Example
Epic A major area of the PRD that takes multiple sprints. "Designer authoring features (MVP)"
Feature A shippable user-facing capability. "Designer can save and reopen .quiz files"
Story A user-shaped slice of a Feature, sprint-sized. "As an author I can save my quiz to disk so I don't lose my work."
Issue A technical task or bug. May sit under a Story or directly under a Feature. "Implement atomic write protocol with <target>.tmp → fsync → rename"

Definition of done lives on each Story / Issue per the Testing and development-standards conventions: failing test first, end-to-end automated test for user-facing behaviour, lint + format pass, code reviewed.

Board columns

The sprint board has six columns. Work moves left-to-right. No column other than "To Do" is allowed to grow unbounded — WIP limits are enforced informally during standup.

Column Meaning Entry criteria Exit criteria
To Do Committed for the sprint, not yet started. Estimated, has acceptance criteria. Picked up by an engineer.
Blocked In flight but cannot progress without external action. Blocking issue identified and assigned an owner. Block resolved — moves back to whichever column it came from.
In Development Engineer is actively working on it. Branch created, work in progress. Code complete, PR opened, CI green.
Ready For Test Code merged or ready to merge; needs verification. PR merged to main (or behind a feature flag) and CI green. Picked up by a tester.
In Testing A tester is exercising the change end-to-end on real platforms. Tester assigned. All test cases pass on every target platform; bugs (if any) become new Issues that block reopening.
Done Verified and shipped to the relevant phase build. All acceptance criteria + Definition of Done met; verified in staging / preview build.

"Blocked" is a separate column from "In Development" so blockers are visible at-a-glance during standup. Items don't sit in "Blocked" without an assigned owner driving the unblock.

PRD-to-Board mapping

When the PRD is signed off, the work is broken down following the hierarchy above. Build-plan sections map cleanly:

The build plan and the ADO board are kept in sync: completing a Story / Issue in ADO ticks the corresponding - [ ] in the build plan; adding a new build-plan item adds a corresponding ADO work item. The build plan is the source of truth for sprint-able scope; the ADO board is the source of truth for in-flight status.

Cadence

Branching convention

Every change lands on a short-lived branch off main and merges back via PR. Branch names tie back to the work item driving the change:

{work-item-id} is the ADO work-item id (Story / Issue) the branch addresses. {short-description} is a kebab-case slug of three to five words capturing the gist. Branches stay short-lived: created when work starts, merged when "Done", deleted on merge. PR titles include the work-item id so the link is bidirectional.

Tooling notes

Reference

Open Questions

Decisions deferred until more is known. Each entry says what is open and the reason it is being deferred.

Each entry is tagged with its status:

  1. Bundle-supplied object types (Phase-deferred — Stretch). v1 ships built-ins-only; .quiz packages contain no runtime C# code. A future stretch goal — alongside cloud-backed authoring — is to allow object types bundled inside a .quiz package as additional C# behaviours + prefabs. That brings real questions: signing/verification of bundled modules, sandboxing their C# behaviours, the author-facing UX for installing third-party object types, and whether the loader is exposed to end users at all. Tracked in Stretch.

  2. Slide schema specification (Prototype-deferred). The exact field shape of a slide JSON file (host_canvas, client_canvas, element coordinates / region anchors, timing, scoring overrides, host-notes, per-element reveal trigger) needs to be pinned down in the shared C# class library. The Quiz Package Format describes the model; the field-by-field schema is build-plan work in MVP.

  3. Internet-based live play relay (Phase-deferred — Stretch). Cloudflare Durable Objects are the leading candidate but the protocol design and discovery mechanism are not yet specified. Tracked in Stretch.

  4. Cross-quiz analytics (Phase-deferred — Stretch). Whether and how to record quiz session data for the author's later review (e.g. "which slides were hardest?"). Privacy posture would need to be designed alongside. Tracked in Stretch.

  5. Embedded WebSocket server library (Resolved). SimpleWebTransport (James-Frowen, MIT, active 2026 — v3.1.0). Picked 2026-05-11 after a research round comparing it against dormant websocket-sharp forks, modern but Unity-incompatible ASP.NET Core libraries (SuperSocket 2.0, Watson Webserver), and Unity's broken built-in HttpListener.AcceptWebSocketAsync. SimpleWebTransport runs as both server and client in Unity-IL2CPP, is used by Mirror Networking (24k+ stars) in production, and bypasses Unity's HttpListener gap with its own TCP-based WebSocket impl. The Designer (.NET MAUI Blazor Hybrid) uses bare System.Net.WebSockets.ClientWebSocket from the BCL — no third-party library on the Designer side. See Tech Stack.

  6. mDNS library and per-platform bridges (Resolved). Makaretu.Dns.Multicast.New (active fork of richardschneider/net-mdns, MIT, latest release May 2025). Picked 2026-05-11. Pure C#, supports both advertisement (Host) and discovery (Client / Designer / Remote), targets .NET Standard 2.0 + .NET 8 + .NET 9. Native iOS (NSNetService) and Android (NsdManager) bridges are documented as fallback if Alpha-prototype validation reveals reliability issues with the pure-C# multicast on those platforms — primary path is the single C# library. See Tech Stack.

  7. Designer→Host transfer wire-level details (Resolved). Picked 2026-05-11: 64 KB chunks, push-progress per chunk, CRC32 per chunk, resume-from-offset on reconnect, no extra wire compression (.quiz is already zip-compressed). The framing schema lives in the (planned) Live Play Protocol document; defaults pinned here keep the design space narrow during prototype work. See Networking — Designer→Host package transfer.

  8. Eager-push very-large-package fallback (Resolved). Picked 2026-05-11: always eager-push; no streaming fallback. Maximum .quiz package size cut from 500 MB to 200 MB to keep eager-push viable on older mobile devices (2–3 GB RAM). Authors who need more than 200 MB of media are out of scope for v1. See Quiz Package Format.

  9. Remote pairing UX (Resolved). Both options ship — the Host shows a short numeric pairing code and a QR; the Remote can either type the code manually or scan. Picked 2026-05-11. See F-RE-2, Host surfaces — Remote pairing, Remote surfaces — Discover + pair.

  10. Final brand and product name (Phase-deferred — Beta). The apps are referred to by their engineering project names (Quiz.Designer, Quiz.Host, Quiz.Client, Quiz.Remote) through MVP and Alpha. The Quiz UK pilot in Beta is the moment to lock the final brand and product name, alongside store-listing prep and brand-true polish. See Design Specification.

  11. Per-platform camera-capture API (Resolved). Unity WebCamTexture — single cross-platform API. Picked 2026-05-11. Skip native pickers + camera-roll access for v1; covers our minimum (capture frame → save as JPEG). Re-evaluate in Beta if UX feedback demands a native picker. See F-CL-14.

  12. Team-photo size + transmit budget (Resolved). Picked 2026-05-11: 256 × 256 px, JPEG quality 75, 96 KB hard cap (re-encode at quality 60 if first pass exceeds the cap). Centre-crop, bilinear downscale. Belongs in the (planned) Live Play Protocol document alongside the join-message envelope schema. See F-CL-14, Networking — Team join.

  13. Designer default asset library — licensing + content (Resolved). Picked 2026-05-11: CC0 / public domain only (Freesound CC0 buzzers, OpenGameArt CC0 avatars — zero attribution friction, distributable inside any author-shipped .quiz). 10 buzzers + 24 premade avatars in the v1 library. Library updates ride app releases — no content endpoint, no auth, no infrastructure in v1. See Designer Shell — Default asset library, F-DE-29, F-DE-30.

Glossary

Term Definition
Author The person who creates a quiz using the Designer.
Designer The authoring app, used on Windows or macOS desktop. (iPad / Android tablet authoring is Stretch.)
Host The app that runs the live quiz session, typically on a device connected to a projector or TV. Runs on Windows, macOS, iPad, and Android tablet.
Client The team app, used on iPhone, Android phone, iPad, or Android tablet. One shared device per team.
Remote The quizmaster's pocket controller, runs on phone or tablet. Pairs with one Host; mirrors the Host display, shows the per-slide host-notes, and sends control messages. Ships in MVP with minimum viable controls (advance / go-back); the rich control commands (trigger reveals, lock/unlock input, extend/skip timer, override scoring, jump-to-slide) land in Alpha.
Quizmaster The person running the live session at the venue. Operates the Host directly and optionally a paired Remote.
Host-notes Free-form per-slide text the author writes in the Designer for the quizmaster — hints, answer keys, presentation cues. Visible only on the Remote during play; never on the Host canvas or any Client.
Participant / Quiz Goer A person playing the quiz. v1 has no per-individual representation; participants are organised into Teams that share a Client device.
Team The unit of play in v1. One Team = one shared Client device + one team name. All scoring, answers, and standings are per-Team.
Slide The unit of presentation in a quiz, like a PowerPoint slide. Each slide has its own Host canvas and Client canvas.
Round A grouping of contiguous slides sharing a theme, scoring rule, or section title — a "section" in PowerPoint terms. Rounds are optional metadata over the slide list, not the unit of presentation.
Host canvas The TV/projector-facing surface of a slide. A fixed virtual canvas (1920×1080 baseline) that scales to fit the connected display.
Client canvas The phone/tablet-facing surface of a slide. A responsive layout (anchors / regions / stacks) that adapts across phone, tablet, portrait, and landscape.
Element A placed instance of an object type on a canvas, with its own per-instance properties.
Object type A self-contained module that defines an addable element — its schema, Designer editor surface, Host runtime surface, Client runtime surface, and any messages it exchanges. New object types can be added without modifying core code. See Object-Type Architecture.
Round type An informal term for a recognisable composition of slides and object types (e.g. "music round", "drawing round"). Not a load-bearing schema concept under the slide model — recipes, not primitives.
Quiz package / .quiz file Zip archive containing the manifest, slide definitions, optional bundled object types, and resources for a quiz. See Quiz Package Format.
Live play The act of running a quiz with a Host and connected Clients.

Stretch

Stretch

Features beyond the initial launch. Worth noting down so the ideas aren't lost, not planned for a specific release. Scope and acceptance criteria for a Stretch item are defined when the item is promoted to a real phase, not now.

Web-based Designer

A browser-based authoring app — alternate producer of the same .quiz package format the desktop / iPad MAUI Blazor Designer writes today. Host / Client / Remote are unchanged: they consume .quiz files regardless of which Designer produced them.

Big simplification vs the previously-considered Angular path: the web Designer reuses the same Razor component library the desktop / iPad MAUI Blazor Designer is built from. Single component library, three deployment targets (MAUI desktop, MAUI iPad, Blazor WASM web). No fork, no codegen, no TypeScript.

Stack:

Code reuse:

Why this is now small: with the MAUI Blazor commit, the web Designer is mostly a deployment target for an existing component library — the heavy lift is the backend (auth, multi-tenant Postgres, CDN, k8s ops), not the UI.

Risks specific to this stretch:

Spikes (when stretch is promoted):

  1. Blazor WASM bootstrap of a subset of the Razor component library — confirm components render and behave equivalently to the MAUI host.
  2. Quiz.Preview (WebGL) embedded directly in a Blazor WASM page (no BlazorWebView); JS bridge round-trip.
  3. ASP.NET Core API skeleton in k8s — minimal API + Postgres + CDN + CI deploy.
  4. Auth + multi-tenant story — OAuth login, per-tenant .quiz storage, isolation enforced.

Cloud-backed authoring

The single biggest stretch goal. Adds a cloud account with authentication and storage — quizmasters sign in, save quizzes to the cloud, get a versioned library, and download quizzes onto a Host. Vendor (managed Postgres + S3-compatible object storage) decided when this stretch is promoted; the architecture stays vendor-neutral until that point.

Bundle-supplied object types

This is gated on cloud-backed authoring landing first because the trust / distribution / install model only really makes sense in a cloud-aware world.

Stylus support

v1 is touch-only on every platform.

iPad / Android tablet Designer

The Designer ships first-class desktop only in v1 (Windows + macOS). iPad and Android tablet authoring is deferred to Stretch and ships derived from the Web-based Designer codebase — same Razor component library wrapped as a mobile app shell. Drives the iPad / Android tablet authoring path off the same WebGL-in-browser preview the web tool uses, sidestepping the WebGL-in-WKWebView risk that would otherwise apply to a MAUI iPad target. Ships only after the web-based Designer is in place.

Real-time collaborative quiz authoring

Google-Docs-style multi-author editing of a single quiz. Depends on cloud-backed authoring + a CRDT layer (e.g. Yjs equivalent) over the quiz model. Adds significant infrastructure complexity and is deferred until clear demand from quizmasters working in teams.

Public quiz marketplace

A discovery + sharing surface where authors can publish quizzes for others to clone or play. Distinct from internal team sharing: this is public, with reputation, ratings, and possibly paid distribution. Depends on cloud-backed authoring. Deferred until cloud-backed authoring is in place and a clear product proposition forms around third-party sharing.

Per-individual identity within a team

The resolved decision is one shared Client device per team. Per-individual play (each team member on their own Client, scoring rolls up to the team) is a possible future expansion. Reopening would change the eager-push model and the join screen — not a small change. Worth keeping on the backlog so the idea isn't forgotten.

Multi-Remote (co-quizmasters)

v1 supports zero or one Remote per session. Multiple Remotes paired to the same Host (e.g. a quizmaster and a scorekeeper on separate phones) is a possible future expansion. Adds protocol questions: who can override whom, what happens when two Remotes try to advance simultaneously. Defer until anyone asks.

Cross-quiz analytics

Recording quiz session data for the author's later review — "which slides were hardest?" "which round dragged?" — see OQ#4. Privacy posture would need to be designed alongside the feature.

Internet-based live play

An optional relay (Cloudflare Durable Objects or similar) that proxies WebSocket traffic between Host and Clients in different locations. Same protocol; different transport. Discovery shifts from Bonjour to a host-issued session code (F-X-6). The join / live-play message families are identical between LAN and internet paths; only the transport differs. UI surfaces this brings in:

See Networking — Internet-based play and OQ#3 for the open relay-protocol design questions.

Better Host crash recovery

Crash recovery is in v1 (Alpha phase). Stretch ambition: faster snapshot cadence (per-message rather than per-event), fully resumable timer state including elapsed time during a Host outage, multi-instance Host failover. None of these are required for a workable v1.

Additional question types

The v1 object-type catalogue ships 13 built-ins across MVP, Alpha, and Beta. The candidates below extend the catalogue with formats that are distinct enough to warrant their own object-type modules but didn't make the v1 cut. Each implements the full plugin contract.

Type id Why it earns its own type Typical placement
core.match-pairs-input Drag/connect UX. Multi-pair scoring (each correct pairing scores). Client canvas.
core.ranking-input Drag-to-reorder UX. Scoring can be all-or-nothing or partial-credit per item-in-correct-position. Client canvas.
core.hotspot-input Tap on image coordinates. "Where on the map is X?" / anatomy / geography. Client canvas.
core.true-false-input Visually distinct from multiple-choice (✓ / ✗ vs A / B). Lower priority — multiple-choice covers the format functionally. Client canvas.

AI-aided quiz authoring

LLM-backed assistance inside the Designer: generate questions on a topic, suggest distractors for a multiple-choice question, propose a difficulty rating, draft host-notes from a question and its answer.

The hard part isn't the integration — it's verification. LLMs hallucinate trivia answers regularly, so the UX must surface the model's confidence, cite sources where possible, and require the author to confirm each generated artefact before it lands in the quiz. Treating LLM output as a starting draft (always edited, never blindly accepted) is the load-bearing UX choice.

Privacy posture decisions: whether prompts and generated content stay local-only, are sent to a third-party API, or run via a local model.

Broadcast / streaming-friendly Host view

A dedicated Host display mode optimised for streaming. The default Host display is designed for a TV in a venue; a streaming view tones down screen-burn elements (no large solid backgrounds), boosts overlay readability, and crops out chrome that's only useful in-room (e.g. join-code prompts after the quiz starts).

Goal: a quizmaster can drive an OBS-quality stream by capturing the Host window directly — no separate OBS overlay, no second-monitor wrangling. Notes from the original proposal review flagged "OBS as a hard requirement" as a barrier; this absorbs that into the Host app.

Recurring teams across sessions

Persistent team identity across multiple quiz nights. A team that played last week shows up tonight with their previous name, colour, and (optionally) a season-long score history.

Depends on:

Privacy and data-protection requirements need design alongside — see OQ#4 for the analogous concern with cross-quiz analytics.

Tournaments / leagues

A series of quiz sessions feeding a season-long aggregate leaderboard. Distinct from a single quiz: needs season schema, cross-session scoring rules, league standings UI on the Designer/Host. Depends on recurring teams and cloud-backed authoring landing first.

Quiz templates / starter packs

A library of pre-built quizzes the author can clone and adapt. Lowers the barrier to a first-night quiz — start from "Pub Quiz Classic" rather than a blank slide.

Depends on cloud-backed authoring (templates live in the cloud library; cloning makes a private copy for the author to edit).

Question bank / reusable questions

The author's personal library of questions, decoupled from any specific quiz. A question can be authored once and pulled into any number of quizzes. Each question carries its own answer, scoring rule, and metadata.

Depends on cloud-backed authoring (question bank lives in the cloud library, not a single .quiz package).

Speed-round mode

A round-level config that runs slides back-to-back with very short timers and no inter-slide pause. Different cadence to the default per-slide pacing — pressure-format rounds.

Could be promoted to Alpha as a small addition to the round-level timing schema rather than waiting for Stretch — it's mostly configuration on top of existing timer and round primitives. Logged here so it isn't lost.