Sound: per-node scene music + synthesized SFX (zero-dep)

- migration 023: story_nodes.music_asset_id (references the asset store).
- src/audio.ts: a tiny audio manager — one looping scene-music track (fade,
  dedup, gesture-primed autoplay) and synthesized one-shot SFX; global mute.
- Runtime: RuntimeNode.musicUrl; music follows the current node (null inherits,
  a finished playthrough stops). SFX blips on dialogue advance/choice (not in the
  editor preview). Floating mute toggle during play.
- Graph inspector: a "Scene music" picker sourced from uploaded audio assets.

No external library (we did not adopt react-winamp — it is a React 16 CRA app,
not an installable package). Bundle grew ~2 KB.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-18 19:51:44 +02:00
co-authored by Claude Opus 4.8
parent 0b1e5e7fc7
commit dafc70b047
9 changed files with 135 additions and 22 deletions
+5
View File
@@ -587,3 +587,8 @@ button:focus-visible { outline: 2px solid #e99a44; outline-offset: 2px; }
.dialogue-preview-empty { display: grid; place-items: center; height: 100%; color: #5f7b73; font: 9px IBM Plex Mono; }
.graph-node-preview { position: absolute; z-index: 6; }
.utterance-preview-dock { position: absolute; left: 16px; bottom: 16px; z-index: 6; }
/* Audio mute toggle (floats over cutscene/dialogue/board) */
.audio-toggle { position: fixed; top: 14px; right: 14px; z-index: 300; width: 34px; height: 34px; display: grid; place-items: center; border: 1px solid #3c5a52; background: #0a211de6; color: #d58a46; font-size: 16px; line-height: 1; cursor: pointer; }
.audio-toggle:hover { border-color: #6f8f85; color: #e7b57e; }
.audio-toggle.muted { color: #5f7b73; text-decoration: line-through; }