BLOBBO/demos/lane-f.html
type-two 1e2e49518f Lane F: procedural WebAudio engine + soundboard demo
Event-driven comedy SFX, all synthesized (no assets, no deps):
- engine.ts: lazy AudioContext (dormant until ui:play / first gesture),
  master chain at ~-12dBFS through a soft limiter, persisted mute
  (blobbo:muted), 10-voice polyphony cap with steal-nearest-to-finish.
- sounds.ts: squish (impact-scaled wet splat), boing (springy sweep),
  splat (per-colour pentatonic bloop, PALETTE order), sparkle (bubbly
  arpeggio), clank+windup (0.5s accelerating ratchet matching the machine
  telegraph), tick (starter blip), fanfare (major arpeggio + new-best
  flourish), optional ambient conveyor hum. All ±10% pitch-randomized,
  all <400ms except fanfare.
- install.ts: subscribes the frozen wire events, returns { engine },
  mounts the bottom-right mute button.
- demos/lane-f.html + src/demo/lane-f.ts: soundboard driving the real
  installAudio wiring; impact slider, colour picker, splat-storm stress.

Verified: tsc strict + vite build pass; 36-check headless harness proves
dormancy pre-unlock, single-context creation, every sound synthesizes,
voice cap holds under a 60-splat storm, and mute persists. Caught+fixed a
clank() infinite loop (geometric tick interval converged below windup).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 11:46:28 +10:00

46 lines
2.4 KiB
HTML

<!doctype html>
<html lang="en">
<head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" />
<title>BLOBBO lane f — audio soundboard</title>
<style>
:root { color-scheme: dark; }
html,body{margin:0;min-height:100%;background:#12151f;color:#eaf2ff;
font-family:system-ui,sans-serif}
#app{max-width:760px;margin:0 auto;padding:26px 18px 90px}
h1{font-size:20px;margin:0 0 4px}
.sub{opacity:.7;font-size:13px;margin:0 0 20px;line-height:1.5}
.unlock{background:#1c3d6b;border:1px solid #2f5fa8;border-radius:10px;
padding:10px 14px;font-size:13px;margin:0 0 20px}
.unlock.on{background:#12351f;border-color:#2f8a4a}
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px}
button.pad{appearance:none;border:none;border-radius:12px;padding:16px 12px;
background:#222838;color:#fff;font-size:15px;font-weight:600;cursor:pointer;
transition:transform .05s, background .1s;box-shadow:0 2px 6px rgba(0,0,0,.3)}
button.pad:hover{background:#2c3448}
button.pad:active{transform:scale(.96);background:#3a4258}
button.pad small{display:block;font-weight:400;opacity:.6;font-size:11px;margin-top:4px}
.ctl{margin:22px 0 6px;padding:14px 16px;background:#1a1e2b;border-radius:12px}
.ctl h2{font-size:13px;margin:0 0 10px;opacity:.8;text-transform:uppercase;letter-spacing:.05em}
.row{display:flex;align-items:center;gap:12px;margin:8px 0;flex-wrap:wrap}
.row label{min-width:120px;font-size:13px}
input[type=range]{flex:1;min-width:140px}
select{background:#222838;color:#fff;border:1px solid #333c52;border-radius:8px;padding:6px 8px}
.val{font-variant-numeric:tabular-nums;min-width:52px;text-align:right;opacity:.85;font-size:13px}
code{background:#0d1017;padding:1px 5px;border-radius:4px;font-size:12px}
kbd{background:#333c52;border-radius:5px;padding:1px 6px;font-size:12px}
</style></head>
<body>
<div id="app">
<h1>🔊 BLOBBO — Lane F audio soundboard</h1>
<p class="sub">
Every sound is synthesized WebAudio (no assets). Fires the same frozen wire
events the game does, through <code>installAudio()</code>.
The <kbd>🔈</kbd> button (bottom-right) toggles mute — it persists across reloads.
</p>
<div class="unlock" id="unlock">🔒 Audio is dormant — click anywhere (or a pad) to unlock the AudioContext.</div>
<div class="grid" id="pads"></div>
</div>
<script type="module" src="/src/demo/lane-f.ts"></script>
</body>
</html>