zeroed-some/dougk / 704f01b

Browse files

touchstart listener, silent buffer trick, resume suspended context

Authored by espadonne
SHA
704f01b753fa3572d3b055147be7c43e81405840
Parents
50b0455
Tree
482c822

2 changed files

StatusFile+-
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
 import { createDoug } from './duck.js'
7
 import { createDoug } from './duck.js'
8
 import { createPond } from './pond.js'
8
 import { createPond } from './pond.js'
9
 import { BreadManager } from './bread.js'
9
 import { BreadManager } from './bread.js'
10
+import { unlockAudio } from './sounds.js'
10
 
11
 
11
 let scene, camera, renderer, composer, outlinePass
12
 let scene, camera, renderer, composer, outlinePass
12
 let doug, pond, breadManager
13
 let doug, pond, breadManager
@@ -102,7 +103,13 @@ export function start(container) {
102
   const raycaster = new THREE.Raycaster()
103
   const raycaster = new THREE.Raycaster()
103
   const mouse = new THREE.Vector2()
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
   renderer.domElement.addEventListener('click', (event) => {
109
   renderer.domElement.addEventListener('click', (event) => {
110
+    // Unlock audio on first interaction (required for mobile)
111
+    unlockAudio()
112
+
106
     mouse.x = (event.clientX / window.innerWidth) * 2 - 1
113
     mouse.x = (event.clientX / window.innerWidth) * 2 - 1
107
     mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
114
     mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
108
 
115
 
src/renderers/three/sounds.jsmodified
@@ -1,6 +1,7 @@
1
 // Web Audio API sound synthesis for dougk
1
 // Web Audio API sound synthesis for dougk
2
 
2
 
3
 let audioContext = null
3
 let audioContext = null
4
+let unlocked = false
4
 
5
 
5
 function getContext() {
6
 function getContext() {
6
   if (!audioContext) {
7
   if (!audioContext) {
@@ -9,6 +10,27 @@ function getContext() {
9
   return audioContext
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
 // Damp crunch sound - wet bread being chomped
34
 // Damp crunch sound - wet bread being chomped
13
 export function playMonch() {
35
 export function playMonch() {
14
   const ctx = getContext()
36
   const ctx = getContext()