Split the single 0–10 'Audio' slider into two controls: - Audio — a dropdown selecting the source: None / Soundtrack / Music - Volume — the renamed 0–10 slider (master gain for the active source) Music is one ethereal, relaxing ambient loop played the same at every altitude (no per-altitude crossfade), per the innerflo.me brief. Sourced CC0: deadrobotmusic 'Ambient F Sharp Minor Ethereal Choir Pad' (freesound #808032); added to build_audio_media.py, the candidate-pool doc, and the public credits colophon. Media stays gitignored; run build_audio_media.py. Routing lives in a pure, node-tested HEFScrub.audioPlan(source, level, frac); app.js's restAudio/blendAudio consult it. None greys out Volume. The welcome screen's mirror level slider is relabeled Volume too. i18n for all 4 langs. Spec: docs/superpowers/specs/2026-07-01-audio-music-option-design.md Tests: +5 node unit (audioPlan), +4 e2e (audio-source.spec.ts); 32 related e2e green, 38 node unit green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Simulator E2E (Playwright)
Browser end-to-end tests for the Human Experience Filter simulator. The §9 pipeline requires an E2E browser tier for any UI surface; this is that tier.
What it covers
tests/altitude-lock.spec.ts drives the real simulator and asserts the
altitude-morph behavior:
- Zooming to a new altitude plays the morph that matches the clip it lands on
(forward zoom-in, and
.revzoom-out). - After landing, the clip is locked — it does not change while parked (no secondary swap, no re-roll).
The played morph paths are read from window.__hefMorphs (a diagnostic array
populated by advance() in static/app.js), which is reliable even when a morph
is served from the in-memory blob cache. The locked clip is read from the
Dev-Mode #scale-name readout.
Running
Prerequisites: the project Python venv on PATH (the Playwright webServer boots
uvicorn simulator.app:app from the repo root), Node, and the Chromium browser.
# from this directory (simulator/e2e)
npm install
npx playwright install chromium
npm test
playwright.config.ts starts uvicorn on port 8099 automatically (reuseExistingServer
locally) and points the tests at it.
Static-build E2E (Cloudflare publish)
tests/static-build.spec.ts verifies the built static site (Cloudflare publish
— see deploy/cloudflare/README.md): it boots with NO /api/* server (baked JSON),
and the WebGL dream shader runs on cross-origin media without tainting the GL
texture (the CORS path R2 will use in production).
It uses playwright.static.config.ts (no uvicorn webServer) and a dual static
server, serve-static.mjs, that serves the built dist/ (app, :8077) and
dist-media/ (media with CORS + Range, :8078):
# from the repo root: build with the media base pointed at the local CORS server
.venv/bin/python -m tools.build_static --media-base http://localhost:8078/
# from this directory (simulator/e2e): start the dual server, then run the spec
DIST_DIR="$PWD/../../dist" MEDIA_DIR_E2E="$PWD/../../dist-media" node serve-static.mjs &
npx playwright test --config playwright.static.config.ts