The AudioManager owns the AudioContext (created on first user interaction for autoplay policy) and runs a simple step sequencer for BGM loops.
// AudioManager.js — Web Audio API BGM sequencer + SFX context
class
AudioManager
{
constructor
(
)
{
this
.
ctx
=
null
;
this
.
currentBgm
=
null
;
//
this
.
masterGain
=
null
;
}
init
(
)
{
if
(
this
.
ctx
)
return
;
this
.
ctx
=
new
(
window
.
AudioContext
||
window
.
webkitAudioContext
)
(
)
;
this
.
masterGain
=
this
.
ctx
.
createGain
(
)
;
this
.
masterGain
.
connect
(
this
.
ctx
.
destination
)
;
}
getCtx
(
)
{
if
(
!
this
.
ctx
)
this
.
init
(
)
;
return
this
.
ctx
;
}
getMaster
(
)
{
if
(
!
this
.
masterGain
)
this
.
init
(
)
;
return
this
.
masterGain
;
}
playMusic
(
patternFn
)
{
this
.
stopMusic
(
)
;
try
{
this
.
currentBgm
=
patternFn
(
this
.
getCtx
(
)
,
this
.
getMaster
(
)
)
;
}
catch
(
e
)
{
console
.
warn
(
'[Audio] BGM error:'
,
e
)
;
}
}
stopMusic
(
)
{
if
(
this
.
currentBgm
)
{
try
{
this
.
currentBgm
.
stop
(
)
;
}
catch
(
_
)
{
}
this
.
currentBgm
=
null
;
}
}
setMuted
(
muted
)
{
if
(
this
.
masterGain
)
{
this
.
masterGain
.
gain
.
value
=
muted
?
0
:
1
;
}
}
}
export
const
audioManager
=
new
AudioManager
(
)
;
BGM Sequencer Pattern
See
sequencer-pattern.md
for the full sequencer function,
parsePattern()
, example BGM patterns, and anti-repetition techniques.
SFX Engine (Web Audio API -- one-shot)
See
sfx-engine.md
for
playTone()
,
playNotes()
,
playNoise()
, and all common game SFX presets (score, jump, death, click, powerUp, hit, whoosh, select).
AudioBridge (wiring EventBus -> audio)
import
{
eventBus
,
Events
}
from
'../core/EventBus.js'
;
import
{
audioManager
}
from
'./AudioManager.js'
;
import
{
gameplayBGM
,
gameOverTheme
}
from
'./music.js'
;
import
{
scoreSfx
,
deathSfx
,
clickSfx
}
from
'./sfx.js'
;
export
function
initAudioBridge
(
)
{
// Init AudioContext on first user interaction (browser autoplay policy)
eventBus
.
on
(
Events
.
AUDIO_INIT
,
(
)
=>
audioManager
.
init
(
)
)
;
// BGM transitions
eventBus
.
on
(
Events
.
MUSIC_GAMEPLAY
,
(
)
=>
audioManager
.
playMusic
(
gameplayBGM
)
)
;
eventBus
.
on
(
Events
.
MUSIC_GAMEOVER
,
(
)
=>
audioManager
.
playMusic
(
gameOverTheme
)
)
;
eventBus
.
on
(
Events
.
MUSIC_STOP
,
(
)
=>
audioManager
.
stopMusic
(
)
)
;
// SFX (one-shot)
eventBus
.
on
(
Events
.
SCORE_CHANGED
,
(
)
=>
scoreSfx
(
)
)
;
eventBus
.
on
(
Events
.
PLAYER_DIED
,
(
)
=>
deathSfx
(
)
)
;
}
Mute State Management
See
mute-button.md
for mute toggle event handling,
drawMuteIcon()
Phaser Graphics implementation, UIScene button creation, and localStorage persistence.
Integration Checklist
Create
src/audio/AudioManager.js
— AudioContext + sequencer + master gain
Create
src/audio/music.js
— BGM patterns as note arrays + sequencer calls
Create
src/audio/sfx.js
— SFX using Web Audio API (oscillator + gain + filter)
Create
src/audio/AudioBridge.js
— wire EventBus events to audio
Wire
initAudioBridge()
in
main.js
Emit
AUDIO_INIT
on first user click (browser autoplay policy)
Emit
MUSIC_GAMEPLAY
,
MUSIC_GAMEOVER
,
MUSIC_STOP
at scene transitions
Add mute toggle
—
AUDIO_TOGGLE_MUTE
event, UI button, M key shortcut
Test: BGM loops seamlessly, SFX fire once and stop, mute silences everything
Important Notes
Zero dependencies
Everything uses the built-in Web Audio API. No npm packages needed for audio.
Browser autoplay
AudioContext MUST be created/resumed from a user click/tap. The
AUDIO_INIT
event handles this.
Master gain for mute
Route everything through a single GainNode. Setting
gain.value = 0
mutes all audio instantly.
Sequencer timing
The look-ahead scheduler (schedules 100ms ahead, checks every 25ms) gives sample-accurate timing with no drift. This is the standard Web Audio scheduling pattern.
No external audio files needed
Everything is synthesized with oscillators.
SFX are instant
Web Audio API fires immediately with zero scheduler latency.
Optional: Strudel.cc Upgrade
For richer procedural BGM with pattern language support, you can optionally install
@strudel/web
:
npm
install
@strudel/web
Note
Strudel is
AGPL-3.0
— projects using it must be open source. See
strudel-reference.md
and
bgm-patterns.md
in this directory for Strudel-specific patterns.
The Strudel upgrade replaces the Web Audio sequencer for BGM only. SFX always use Web Audio API directly.