touchstart listener, silent buffer trick, resume suspended context
- SHA
704f01b753fa3572d3b055147be7c43e81405840- Parents
-
50b0455 - Tree
482c822
704f01b
704f01b753fa3572d3b055147be7c43e8140584050b0455
482c822| Status | File | + | - |
|---|---|---|---|
| M |
src/renderers/three/index.js
|
7 | 0 |
| M |
src/renderers/three/sounds.js
|
22 | 0 |
src/renderers/three/index.jsmodified@@ -7,6 +7,7 @@ import { OutputPass } from 'three/addons/postprocessing/OutputPass.js' | ||
| 7 | 7 | import { createDoug } from './duck.js' |
| 8 | 8 | import { createPond } from './pond.js' |
| 9 | 9 | import { BreadManager } from './bread.js' |
| 10 | +import { unlockAudio } from './sounds.js' | |
| 10 | 11 | |
| 11 | 12 | let scene, camera, renderer, composer, outlinePass |
| 12 | 13 | let doug, pond, breadManager |
@@ -102,7 +103,13 @@ export function start(container) { | ||
| 102 | 103 | const raycaster = new THREE.Raycaster() |
| 103 | 104 | const mouse = new THREE.Vector2() |
| 104 | 105 | |
| 106 | + // Unlock audio on first touch (for mobile) | |
| 107 | + renderer.domElement.addEventListener('touchstart', unlockAudio, { once: true }) | |
| 108 | + | |
| 105 | 109 | renderer.domElement.addEventListener('click', (event) => { |
| 110 | + // Unlock audio on first interaction (required for mobile) | |
| 111 | + unlockAudio() | |
| 112 | + | |
| 106 | 113 | mouse.x = (event.clientX / window.innerWidth) * 2 - 1 |
| 107 | 114 | mouse.y = -(event.clientY / window.innerHeight) * 2 + 1 |
| 108 | 115 | |
src/renderers/three/sounds.jsmodified@@ -1,6 +1,7 @@ | ||
| 1 | 1 | // Web Audio API sound synthesis for dougk |
| 2 | 2 | |
| 3 | 3 | let audioContext = null |
| 4 | +let unlocked = false | |
| 4 | 5 | |
| 5 | 6 | function getContext() { |
| 6 | 7 | if (!audioContext) { |
@@ -9,6 +10,27 @@ function getContext() { | ||
| 9 | 10 | return audioContext |
| 10 | 11 | } |
| 11 | 12 | |
| 13 | +// Must be called on first user interaction to enable audio on mobile | |
| 14 | +export function unlockAudio() { | |
| 15 | + if (unlocked) return | |
| 16 | + | |
| 17 | + const ctx = getContext() | |
| 18 | + | |
| 19 | + // Resume if suspended | |
| 20 | + if (ctx.state === 'suspended') { | |
| 21 | + ctx.resume() | |
| 22 | + } | |
| 23 | + | |
| 24 | + // Play a silent buffer to fully unlock on iOS/mobile | |
| 25 | + const silentBuffer = ctx.createBuffer(1, 1, ctx.sampleRate) | |
| 26 | + const source = ctx.createBufferSource() | |
| 27 | + source.buffer = silentBuffer | |
| 28 | + source.connect(ctx.destination) | |
| 29 | + source.start(0) | |
| 30 | + | |
| 31 | + unlocked = true | |
| 32 | +} | |
| 33 | + | |
| 12 | 34 | // Damp crunch sound - wet bread being chomped |
| 13 | 35 | export function playMonch() { |
| 14 | 36 | const ctx = getContext() |