diff --git a/docs/features.md b/docs/features.md index 2698a8c..66c5f18 100644 --- a/docs/features.md +++ b/docs/features.md @@ -83,6 +83,29 @@ district is credited the same points for the round (`submitRoundScore` in on its `gameResult.levels` entry. Scores earned under the earlier region-scaled ladder stay on the boards as they were recorded. +## Sound & Music +- **Sound effects**: nine one-shots on the play flow — a click when Play or + Back changes screen, a pop when the guess pin lands, a rising sound on + submit, a jingle tiered off the score (4-5 points, 1-3, a miss), a transition + into the next round, a flat blip on skip, and a low tone when the panorama + viewer fails to start or a guess is not recorded. The click is deliberately + limited to the two buttons that navigate: on every button in the chrome it + became noise within a few rounds +- **Background music**: one ambient loop at roughly a third of the effects' + volume, so it sits under the panorama rather than competing with it. It is + mounted app-wide rather than per page, which is what lets it play unbroken + across the menu and a round — and means it also plays on Credits and the + debug pages +- **Two switches, both on by default**: music and effects toggle independently + from the header, beside the theme switch, and both choices persist in + localStorage. The game header collapses them into a single mute-everything + button below the `sm` breakpoint, where there is no room for the pair +- **Nothing plays before a gesture**: every browser blocks audio until the + player interacts, so no audio file is even fetched until the first click or + keypress — which also means music on iOS starts at the first tap, not on load +- **All assets are CC0**: sourced from Kenney and OpenGameArt, with per-file + provenance in `public/audio/SOURCES.md` and credit on `/credits` + ## Leaderboards - **Rollup fan-out**: one guess credits the district its panorama sat in, then that district's province, then Vietnam — three score boards and three diff --git a/docs/project-structure.md b/docs/project-structure.md index a7320fb..eeb51a4 100644 --- a/docs/project-structure.md +++ b/docs/project-structure.md @@ -77,6 +77,11 @@ Next.js 16 App Router structure: the panorama pane's top row via `PanoramaViewer`'s `topBarSlot` - `DonateQRModal.js` - Donation QR code modal - `ThemeToggle.js` - Light/dark switch +- `SoundToggle.js` - Music and sound-effect switches; `compact` renders one + mute-everything button for the game header below `sm` +- `MusicPlayer.js` - The background loop. Renders nothing and is mounted in the + root layout, not in a page, so a client navigation between the menu and a + round does not restart it ### Reusable Components (`src/components/`) @@ -136,6 +141,9 @@ Neon Postgres, which is what the app queries at runtime. - `session.js` - Redis-based session management with 30-min expiry - `upstash.js` - Upstash Redis REST client adapter with multi-tenant key prefix - `theme.js`, `use-count-up.js` - Theme persistence and a count-up hook +- `audio.js` - **Client-side only.** The audio context, its first-gesture + unlock, the decoded-buffer cache, one-shot playback, and the music/effects + preferences ## Build Scripts (`scripts/`) Each carries a header comment with its flags and its cost. @@ -183,4 +191,7 @@ only. ## Public Assets (`public/`) - `zlp.jpg` - Donation QR code image +- `audio/` - Nine sound effects plus the music loop (shipped as both + WebM/Opus and MP3), with `SOURCES.md` recording each file's upstream source + and licence - Static assets served directly by Next.js