zeroed-some/dougk / 82408db

Browse files

sound effect to duck monch

Authored by espadonne
SHA
82408db47c1088d1d3210eb567dd1c67bb6623b4
Parents
b78d533
Tree
26e577c

2 changed files

StatusFile+-
M src/renderers/three/duck.js 3 0
A src/renderers/three/sounds.js 107 0
src/renderers/three/duck.jsmodified
@@ -1,5 +1,6 @@
11
 // Doug the Duck - 3D low-poly model with Wind Waker cel-shading
22
 import * as THREE from 'three'
3
+import { playMonch } from './sounds.js'
34
 
45
 export function createDoug(scene, gradientMap) {
56
   const group = new THREE.Group()
@@ -342,6 +343,8 @@ export function createDoug(scene, gradientMap) {
342343
               bread.eaten = true
343344
               // Eating splash ripple
344345
               pond.addRipple(state.position.x, state.position.z)
346
+              // Damp crunch sound
347
+              playMonch()
345348
             }
346349
           }
347350
         }
src/renderers/three/sounds.jsadded
@@ -0,0 +1,107 @@
1
+// Web Audio API sound synthesis for dougk
2
+
3
+let audioContext = null
4
+
5
+function getContext() {
6
+  if (!audioContext) {
7
+    audioContext = new (window.AudioContext || window.webkitAudioContext)()
8
+  }
9
+  return audioContext
10
+}
11
+
12
+// Damp crunch sound - wet bread being chomped
13
+export function playMonch() {
14
+  const ctx = getContext()
15
+  const now = ctx.currentTime
16
+
17
+  // Resume context if suspended (browser autoplay policy)
18
+  if (ctx.state === 'suspended') {
19
+    ctx.resume()
20
+  }
21
+
22
+  // Create noise buffer for the crunch texture
23
+  const noiseLength = 0.15
24
+  const bufferSize = ctx.sampleRate * noiseLength
25
+  const noiseBuffer = ctx.createBuffer(1, bufferSize, ctx.sampleRate)
26
+  const noiseData = noiseBuffer.getChannelData(0)
27
+
28
+  // Fill with noise
29
+  for (let i = 0; i < bufferSize; i++) {
30
+    noiseData[i] = Math.random() * 2 - 1
31
+  }
32
+
33
+  // Noise source
34
+  const noise = ctx.createBufferSource()
35
+  noise.buffer = noiseBuffer
36
+
37
+  // Low-pass filter for the "damp" wet quality
38
+  const dampFilter = ctx.createBiquadFilter()
39
+  dampFilter.type = 'lowpass'
40
+  dampFilter.frequency.setValueAtTime(800, now)
41
+  dampFilter.frequency.exponentialRampToValueAtTime(300, now + 0.08)
42
+  dampFilter.Q.value = 2
43
+
44
+  // Bandpass for crunch character
45
+  const crunchFilter = ctx.createBiquadFilter()
46
+  crunchFilter.type = 'bandpass'
47
+  crunchFilter.frequency.value = 400
48
+  crunchFilter.Q.value = 1.5
49
+
50
+  // Envelope for the noise burst
51
+  const noiseGain = ctx.createGain()
52
+  noiseGain.gain.setValueAtTime(0, now)
53
+  noiseGain.gain.linearRampToValueAtTime(0.4, now + 0.01) // Quick attack
54
+  noiseGain.gain.exponentialRampToValueAtTime(0.15, now + 0.04) // Initial drop
55
+  noiseGain.gain.exponentialRampToValueAtTime(0.01, now + 0.12) // Tail off
56
+
57
+  // Low thump for the bite impact
58
+  const thump = ctx.createOscillator()
59
+  thump.type = 'sine'
60
+  thump.frequency.setValueAtTime(120, now)
61
+  thump.frequency.exponentialRampToValueAtTime(50, now + 0.06)
62
+
63
+  const thumpGain = ctx.createGain()
64
+  thumpGain.gain.setValueAtTime(0, now)
65
+  thumpGain.gain.linearRampToValueAtTime(0.3, now + 0.005)
66
+  thumpGain.gain.exponentialRampToValueAtTime(0.01, now + 0.08)
67
+
68
+  // Secondary squelch - adds wetness
69
+  const squelch = ctx.createOscillator()
70
+  squelch.type = 'triangle'
71
+  squelch.frequency.setValueAtTime(200, now)
72
+  squelch.frequency.exponentialRampToValueAtTime(80, now + 0.05)
73
+
74
+  const squelchGain = ctx.createGain()
75
+  squelchGain.gain.setValueAtTime(0, now + 0.01)
76
+  squelchGain.gain.linearRampToValueAtTime(0.15, now + 0.02)
77
+  squelchGain.gain.exponentialRampToValueAtTime(0.01, now + 0.07)
78
+
79
+  // Master output with slight compression feel
80
+  const master = ctx.createGain()
81
+  master.gain.value = 0.6
82
+
83
+  // Connect noise chain
84
+  noise.connect(dampFilter)
85
+  dampFilter.connect(crunchFilter)
86
+  crunchFilter.connect(noiseGain)
87
+  noiseGain.connect(master)
88
+
89
+  // Connect thump
90
+  thump.connect(thumpGain)
91
+  thumpGain.connect(master)
92
+
93
+  // Connect squelch
94
+  squelch.connect(squelchGain)
95
+  squelchGain.connect(master)
96
+
97
+  // Output
98
+  master.connect(ctx.destination)
99
+
100
+  // Play
101
+  noise.start(now)
102
+  noise.stop(now + noiseLength)
103
+  thump.start(now)
104
+  thump.stop(now + 0.1)
105
+  squelch.start(now)
106
+  squelch.stop(now + 0.08)
107
+}