docs(welcome): brainstorming spec for the Welcome screen + controls

Unify #loading / #motion-warning / #run-sim into one #welcome overlay:
heads-up notice + four output controls (Video on, Reduce motion off,
Audio 2, English) + Loading Universe progress. Two states (welcome →
centered loading); right panel hidden until entry; Continue is the single
entry gesture. Forward note: extra welcome copy lands later.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
BenStullsBets
2026-06-30 10:47:14 -07:00
parent a4d3d831b0
commit 05328b1b5e
@@ -0,0 +1,169 @@
# Welcome screen — design
**Date:** 2026-06-30
**Status:** Approved (brainstorming)
**Surface:** `simulator/static/` (frontend only — `index.html`, `style.css`, `app.js`, `i18n.js`)
## Summary
Replace the current bare boot flow with a single **Welcome screen**: the
universal entry gate the visitor sees on every load. It carries the
photosensitivity "Heads up" notice, the four output controls (Video, Reduce
motion, Audio, Language) with welcome-appropriate defaults, a **Continue**
button, and the "Loading Universe" progress. The right control panel is hidden
until the visitor enters the simulation.
## Why
Today the universe loads behind a bare "Loading Universe" splash; the splash
fades to reveal a "Run simulation" button over the black stage; a first-visit
"Heads up" modal appears; clicking through begins the experience. The four
output controls live in the right panel and are visible the whole time —
including during loading, which the panel should not be.
We want a deliberate, configurable front door: the visitor reads the heads-up,
sets their preferences, and presses one button to begin — while the universe
loads in the background so entry is instant when they are ready.
## Current flow (being replaced)
- `#loading` — fullscreen splash (`Loading Universe` title + `#loading-fill`
progress bar). `simulator/static/index.html:10-15`.
- `#run-sim` — "Run simulation" button revealed over the black stage after
phase-1 preload. `index.html:33`; wired at `app.js:1533`.
- `#motion-warning` — first-visit-only "Heads up" modal, persisted dismissed via
`WARN_KEY`. `index.html:34-40`; shown by `maybeShowMotionWarning()`
(`app.js:1161`).
- Four controls in `.panel` (`#visual`, `#audio`, `#reduce-motion`,
`#lang-select`). `index.html:46-65`. The panel is visible throughout.
- `beginExperience()` (`app.js:1505`) snaps Video on and Audio to
`START_AUDIO_LEVEL = "2"` in the click gesture (Safari audio-unlock).
## Target design
### 1. One unified `#welcome` overlay
Fold `#loading`, `#motion-warning`, and `#run-sim` into a single fullscreen
`#welcome` overlay layered above everything. It absorbs all three roles. The
right `.panel` is **hidden** for the entire welcome + loading period and revealed
only on entry to the simulation.
The overlay contains three blocks:
1. **Welcome messaging** — the "Heads up — motion & flashing" title + body
(today's `warn.*` strings, reworded to point at the on-screen Reduce-motion
control rather than "the panel"). Structured so additional welcome copy can be
added later (see Forward notes).
2. **Controls** — the four output controls (below).
3. **Loading** — the "Loading Universe" title + progress bar.
### 2. Two visual states
**State A — Welcome (on load):** welcome messaging + controls + Continue button
shown; the Loading block sits at the **bottom**, its bar filling as phase-1
preload runs in the background.
**State B — Loading (after Continue, only if not yet loaded):** the welcome
messaging and Continue button **disappear**; the **controls remain**; the Loading
block moves to **center**. When phase-1 preload completes, auto-enter the
simulation.
**Continue when already loaded** → skip State B; enter the simulation directly.
State is driven by a class on `#welcome` (e.g. `.welcoming``.loading`), with
the "loaded yet?" decision made from a flag set when phase-1 preload resolves.
### 3. Control defaults (welcome screen)
| Control | Welcome default | Notes |
|---------------|-----------------|--------------------------------------------------|
| Video | **ON** | Was OFF in the panel; on by default here. |
| Reduce motion | **OFF** | Unless OS `prefers-reduced-motion` / stored pref.|
| Audio | **2** (010) | Was 0 in the panel. |
| Language | **English** | Global; changing it re-renders all welcome text. |
Reduce motion keeps deferring to a stored value / the OS media query when those
exist (same precedence as `initReduceMotion()` today); "OFF" is the no-signal
default.
### 4. Entry into the simulation
Continue is the user gesture that unlocks audio (the role `#run-sim` had). On
Continue → enter (either immediately when loaded, or at end of State B):
1. Copy the four welcome values into the existing panel inputs (`#visual`,
`#reduce-motion`, `#audio`, `#lang-select`).
2. Fire the existing handlers / `beginExperience()` so the experience starts in
the same code paths as today.
3. Remove the `#welcome` overlay and reveal `.panel`.
Per decisions: the four controls **stay in the panel** for mid-experience
adjustment; **Video-off at entry** starts with video off (audio still plays per
the Audio level — `beginExperience` no longer forces Video on regardless).
### 5. Controls drive shared state
The welcome controls write the same underlying state as the panel controls — they
are not a parallel system:
- **Language** routes through the global `setLanguage()`, which sets `activeLang`
and runs `applyUiStrings()` over **all** `[data-i18n]` elements — so the
heads-up copy, control labels, and Continue button re-render live, exactly like
the panel selector does today. The panel `#lang-select` value is synced to
`activeLang` on entry.
- **Reduce motion** sets the same `reduceMotion` variable + `RM_KEY` localStorage
and calls `applyReduceMotion()`; panel checkbox synced on entry.
- **Audio / Video** values are applied on entry via the panel inputs +
`beginExperience()`.
### 6. Behavior changes (intentional)
- The welcome screen shows on **every** load (it is the entry gate holding the
controls), so the first-visit-only persistence of the heads-up (`WARN_KEY`) is
**retired**.
- Entry uses the **chosen** Audio level rather than always snapping to 2 (2 is the
default, not a forced value).
- `#run-sim` is removed; **Continue** is the single entry button.
## i18n
New / changed `UI_STRINGS` keys in `simulator/static/i18n.js` (all four langs —
en/es/fr/ja, matching existing parity):
- A Continue button label (reuse `warn.continue` if suitable, or add
`welcome.continue`).
- Reworded `warn.body` (point at the on-screen Reduce-motion control).
- Welcome-screen control labels reuse the existing `output.video`,
`output.audio`, `rm.label` keys (already translated). The language selector
needs no string (native names from the registry).
All welcome text must live under `data-i18n` so `applyUiStrings()` re-renders it
on language change.
## Out of scope / Forward notes
- **Additional welcome copy** beyond the heads-up (intro / framing text) comes in
a later pass, once this flow works. The welcome-messaging block is structured so
adding more `data-i18n` paragraphs is trivial.
- No backend / API changes. No content-pipeline changes.
## Testing
Frontend lives behind Playwright e2e (`simulator/static/` tests) + node unit
tests. Plan to:
- **Update** existing tests that reference `#loading`, `#run-sim`, and
`#motion-warning` to the new `#welcome` flow.
- **Add** coverage for:
- Welcome defaults (Video on, Reduce motion off, Audio 2, English).
- Language switch re-renders welcome text (heads-up + labels + Continue) live.
- Continue **while loading** → State B (messaging gone, controls remain,
progress centered), then auto-enter when loaded.
- Continue **when already loaded** → straight into the simulation.
- `.panel` hidden during welcome + loading; revealed on entry.
- Welcome control values carry into the panel inputs on entry.
Environment caveats (from prior sessions): this box has only `python3` (start
uvicorn by hand; `reuseExistingServer`); a stale uvicorn is the recurring
"no transitions" root cause; `loop-recovery.spec.ts` fails on a clean baseline
here (boots without video).