0
# Audio Management
1
2
Phaser provides a comprehensive audio system supporting multiple backends (Web Audio API and HTML5 Audio) with features like spatial audio, audio sprites, and dynamic sound effects. The system automatically chooses the best available audio implementation.
3
4
## Sound Manager
5
6
### Audio System Setup
7
The sound system is automatically configured based on browser capabilities:
8
9
```javascript { .api }
10
// Game configuration with audio settings
11
const config = {
12
type: Phaser.AUTO,
13
width: 800,
14
height: 600,
15
audio: {
16
disableWebAudio: false, // Force HTML5 Audio if true
17
context: null, // Custom AudioContext
18
noAudio: false // Disable all audio if true
19
},
20
scene: AudioScene
21
};
22
23
class AudioScene extends Phaser.Scene {
24
create() {
25
// Access sound manager
26
const sound = this.sound;
27
28
// Sound manager properties
29
console.log('Audio implementation:', sound.constructor.name);
30
console.log('Context locked:', sound.locked);
31
console.log('Muted:', sound.mute);
32
console.log('Volume:', sound.volume);
33
console.log('Sounds count:', sound.sounds.length);
34
35
// Global audio settings
36
sound.setVolume(0.8); // Master volume (0-1)
37
sound.setMute(false); // Mute/unmute all sounds
38
sound.setRate(1.0); // Global playback rate
39
sound.setDetune(0); // Global detune in cents
40
41
// Audio context unlock (required for Web Audio)
42
if (sound.locked) {
43
sound.once('unlocked', () => {
44
console.log('Audio context unlocked');
45
});
46
}
47
}
48
}
49
```
50
51
### Loading Audio Files
52
Load various audio formats and configurations:
53
54
```javascript { .api }
55
class AudioLoadingScene extends Phaser.Scene {
56
preload() {
57
// Basic audio loading
58
this.load.audio('music', 'assets/music.mp3');
59
this.load.audio('jump', 'assets/jump.wav');
60
61
// Multiple format support (browser will choose best)
62
this.load.audio('bgm', [
63
'assets/music.ogg',
64
'assets/music.mp3',
65
'assets/music.m4a'
66
]);
67
68
// Audio with configuration
69
this.load.audio('explosion', 'assets/explosion.wav', {
70
instances: 5 // Pre-create 5 instances for overlapping playback
71
});
72
73
// Audio sprite (multiple sounds in one file)
74
this.load.audioSprite('sfx', 'assets/sfx.json', 'assets/sfx.mp3');
75
76
// Base64 encoded audio
77
this.load.audio('beep', 'data:audio/wav;base64,UklGRnoGAABXQVZFZm10IBAAAAABAAEA...');
78
79
// Loading progress
80
this.load.on('progress', (percent) => {
81
console.log('Audio loading:', Math.round(percent * 100) + '%');
82
});
83
84
this.load.on('complete', () => {
85
console.log('Audio loading complete');
86
});
87
}
88
}
89
```
90
91
## Sound Playback
92
93
### Basic Sound Control
94
Create and control sound instances:
95
96
```javascript { .api }
97
class SoundPlaybackScene extends Phaser.Scene {
98
create() {
99
// Create sound objects
100
this.music = this.sound.add('bgm');
101
this.jumpSound = this.sound.add('jump');
102
this.explosionSound = this.sound.add('explosion');
103
104
// Sound configuration
105
this.music = this.sound.add('bgm', {
106
volume: 0.6,
107
loop: true,
108
delay: 0.5
109
});
110
111
// Play sounds
112
this.music.play(); // Basic playback
113
this.jumpSound.play({ volume: 0.8 }); // Play with options
114
115
// Sound control
116
this.music.pause(); // Pause playback
117
this.music.resume(); // Resume playback
118
this.music.stop(); // Stop playback
119
this.music.restart(); // Restart from beginning
120
121
// Sound properties
122
this.music.setVolume(0.5); // Set volume (0-1)
123
this.music.setRate(1.2); // Set playback rate (speed)
124
this.music.setDetune(-100); // Detune in cents
125
this.music.setSeek(30); // Seek to 30 seconds
126
this.music.setLoop(true); // Enable looping
127
this.music.setPan(-0.3); // Stereo pan (-1 to 1)
128
129
// Get sound properties
130
console.log('Duration:', this.music.duration);
131
console.log('Current time:', this.music.seek);
132
console.log('Is playing:', this.music.isPlaying);
133
console.log('Is paused:', this.music.isPaused);
134
console.log('Volume:', this.music.volume);
135
console.log('Rate:', this.music.rate);
136
137
// Destroy sound when done
138
this.music.destroy();
139
}
140
}
141
```
142
143
### Audio Sprites
144
Use audio sprites for efficient sound management:
145
146
```javascript { .api }
147
class AudioSpriteScene extends Phaser.Scene {
148
preload() {
149
// Audio sprite JSON format
150
const audioSpriteData = {
151
"spritemap": {
152
"coin": { "start": 0, "end": 0.5, "loop": false },
153
"jump": { "start": 1, "end": 1.3, "loop": false },
154
"powerup": { "start": 2, "end": 3.5, "loop": false },
155
"background": { "start": 4, "end": 34, "loop": true }
156
}
157
};
158
159
this.load.audioSprite('gamesfx', audioSpriteData, 'assets/gamesfx.mp3');
160
}
161
162
create() {
163
// Play audio sprite sounds
164
this.sound.playAudioSprite('gamesfx', 'coin');
165
this.sound.playAudioSprite('gamesfx', 'jump', { volume: 0.8 });
166
167
// Create audio sprite object
168
this.gameSfx = this.sound.addAudioSprite('gamesfx');
169
170
// Play specific sounds
171
this.gameSfx.play('coin');
172
this.gameSfx.play('background', { loop: true });
173
174
// Stop specific sounds
175
this.gameSfx.stop('background');
176
177
// Audio sprite events
178
this.gameSfx.on('play', (sound) => {
179
console.log('Audio sprite playing:', sound.key);
180
});
181
182
this.gameSfx.on('complete', (sound) => {
183
console.log('Audio sprite completed:', sound.key);
184
});
185
}
186
187
collectCoin() {
188
this.sound.playAudioSprite('gamesfx', 'coin');
189
}
190
191
playerJump() {
192
this.sound.playAudioSprite('gamesfx', 'jump');
193
}
194
}
195
```
196
197
### Sound Events
198
Handle sound playback events:
199
200
```javascript { .api }
201
class SoundEventsScene extends Phaser.Scene {
202
create() {
203
this.music = this.sound.add('bgm');
204
this.sfx = this.sound.add('explosion');
205
206
// Sound-specific events
207
this.music.on('play', () => {
208
console.log('Music started playing');
209
});
210
211
this.music.on('pause', () => {
212
console.log('Music paused');
213
});
214
215
this.music.on('resume', () => {
216
console.log('Music resumed');
217
});
218
219
this.music.on('stop', () => {
220
console.log('Music stopped');
221
});
222
223
this.music.on('complete', () => {
224
console.log('Music completed');
225
this.onMusicComplete();
226
});
227
228
this.music.on('looped', () => {
229
console.log('Music looped');
230
});
231
232
this.music.on('volume', (sound, volume) => {
233
console.log('Music volume changed to:', volume);
234
});
235
236
this.music.on('rate', (sound, rate) => {
237
console.log('Music rate changed to:', rate);
238
});
239
240
this.music.on('seek', (sound, time) => {
241
console.log('Music seeked to:', time);
242
});
243
244
// Global sound events
245
this.sound.on('mute', (soundManager, mute) => {
246
console.log('All sounds muted:', mute);
247
});
248
249
this.sound.on('volume', (soundManager, volume) => {
250
console.log('Global volume changed to:', volume);
251
});
252
253
this.sound.on('pauseall', () => {
254
console.log('All sounds paused');
255
});
256
257
this.sound.on('resumeall', () => {
258
console.log('All sounds resumed');
259
});
260
261
this.sound.on('stopall', () => {
262
console.log('All sounds stopped');
263
});
264
}
265
266
onMusicComplete() {
267
// Handle music completion
268
this.startNextTrack();
269
}
270
}
271
```
272
273
## Advanced Audio Features
274
275
### Spatial Audio
276
Implement 3D audio positioning:
277
278
```javascript { .api }
279
class SpatialAudioScene extends Phaser.Scene {
280
create() {
281
this.player = this.add.sprite(400, 300, 'player');
282
this.enemy = this.add.sprite(600, 200, 'enemy');
283
284
// Create positional sound
285
this.enemySound = this.sound.add('enemyNoise', { loop: true });
286
287
// Update audio position based on distance
288
this.updateSpatialAudio();
289
}
290
291
update() {
292
this.updateSpatialAudio();
293
}
294
295
updateSpatialAudio() {
296
if (this.enemySound.isPlaying) {
297
// Calculate distance between player and enemy
298
const distance = Phaser.Math.Distance.Between(
299
this.player.x, this.player.y,
300
this.enemy.x, this.enemy.y
301
);
302
303
// Maximum hearing distance
304
const maxDistance = 400;
305
306
// Calculate volume based on distance (closer = louder)
307
const volume = Math.max(0, 1 - (distance / maxDistance));
308
this.enemySound.setVolume(volume);
309
310
// Calculate stereo pan based on horizontal position
311
const panRange = 200; // Distance for full pan
312
const deltaX = this.enemy.x - this.player.x;
313
const pan = Phaser.Math.Clamp(deltaX / panRange, -1, 1);
314
this.enemySound.setPan(pan);
315
316
// Optional: Adjust pitch based on distance (Doppler effect)
317
const pitchVariation = 0.2;
318
const pitch = 1 + (volume - 0.5) * pitchVariation;
319
this.enemySound.setRate(pitch);
320
}
321
}
322
323
enemyAttack() {
324
// Play attack sound with spatial positioning
325
const attackSound = this.sound.add('attack');
326
this.updateSoundPosition(attackSound, this.enemy.x, this.enemy.y);
327
attackSound.play();
328
}
329
330
updateSoundPosition(sound, x, y) {
331
const distance = Phaser.Math.Distance.Between(
332
this.player.x, this.player.y, x, y
333
);
334
335
const volume = Math.max(0, 1 - (distance / 300));
336
const pan = Phaser.Math.Clamp((x - this.player.x) / 200, -1, 1);
337
338
sound.setVolume(volume);
339
sound.setPan(pan);
340
}
341
}
342
```
343
344
### Dynamic Music System
345
Create adaptive music that responds to gameplay:
346
347
```javascript { .api }
348
class DynamicMusicScene extends Phaser.Scene {
349
create() {
350
// Multiple music layers
351
this.musicLayers = {
352
base: this.sound.add('music_base', { loop: true }),
353
drums: this.sound.add('music_drums', { loop: true }),
354
melody: this.sound.add('music_melody', { loop: true }),
355
tension: this.sound.add('music_tension', { loop: true })
356
};
357
358
// Start with base layer
359
this.musicLayers.base.play();
360
this.currentIntensity = 0;
361
362
// Sync all layers
363
Object.values(this.musicLayers).forEach(layer => {
364
layer.setVolume(0);
365
});
366
this.musicLayers.base.setVolume(0.8);
367
368
// Music state
369
this.musicState = 'calm';
370
this.targetVolumes = {
371
base: 0.8,
372
drums: 0,
373
melody: 0,
374
tension: 0
375
};
376
}
377
378
update() {
379
// Smoothly transition layer volumes
380
Object.entries(this.musicLayers).forEach(([name, layer]) => {
381
const current = layer.volume;
382
const target = this.targetVolumes[name];
383
const lerp = Phaser.Math.Linear(current, target, 0.02);
384
layer.setVolume(lerp);
385
});
386
}
387
388
setMusicState(state) {
389
this.musicState = state;
390
391
switch (state) {
392
case 'calm':
393
this.targetVolumes = { base: 0.8, drums: 0, melody: 0.4, tension: 0 };
394
break;
395
case 'action':
396
this.targetVolumes = { base: 0.6, drums: 0.8, melody: 0.6, tension: 0.3 };
397
break;
398
case 'boss':
399
this.targetVolumes = { base: 0.4, drums: 1.0, melody: 0.8, tension: 0.9 };
400
break;
401
case 'silence':
402
this.targetVolumes = { base: 0, drums: 0, melody: 0, tension: 0 };
403
break;
404
}
405
}
406
407
onEnemySpawn() {
408
this.setMusicState('action');
409
}
410
411
onBossAppear() {
412
this.setMusicState('boss');
413
}
414
415
onAllEnemiesDefeated() {
416
this.setMusicState('calm');
417
}
418
}
419
```
420
421
### Audio Analysis
422
Analyze audio for reactive visuals:
423
424
```javascript { .api }
425
class AudioAnalysisScene extends Phaser.Scene {
426
create() {
427
// Web Audio API required for analysis
428
if (this.sound.context) {
429
this.music = this.sound.add('music');
430
431
// Create analyser node
432
this.analyser = this.sound.context.createAnalyser();
433
this.analyser.fftSize = 256;
434
this.bufferLength = this.analyser.frequencyBinCount;
435
this.dataArray = new Uint8Array(this.bufferLength);
436
437
// Connect audio to analyser
438
if (this.music.source) {
439
this.music.source.connect(this.analyser);
440
}
441
442
// Visual elements that react to audio
443
this.visualBars = [];
444
for (let i = 0; i < 32; i++) {
445
const bar = this.add.rectangle(
446
50 + i * 20, 400, 15, 100, 0x00ff00
447
);
448
this.visualBars.push(bar);
449
}
450
451
this.music.play({ loop: true });
452
}
453
}
454
455
update() {
456
if (this.analyser && this.music.isPlaying) {
457
// Get frequency data
458
this.analyser.getByteFrequencyData(this.dataArray);
459
460
// Update visual bars based on frequency data
461
for (let i = 0; i < this.visualBars.length; i++) {
462
const bar = this.visualBars[i];
463
const frequency = this.dataArray[i * 4] || 0; // Sample every 4th frequency
464
const height = (frequency / 255) * 200; // Scale to bar height
465
466
bar.height = height;
467
bar.y = 400 - height / 2;
468
469
// Color based on frequency intensity
470
const hue = (frequency / 255) * 120; // Green to red
471
bar.fillColor = Phaser.Display.Color.HSVToRGB(hue / 360, 1, 1).color;
472
}
473
474
// Beat detection
475
const bassFreq = this.dataArray.slice(0, 4).reduce((a, b) => a + b) / 4;
476
if (bassFreq > 200 && !this.beatDetected) {
477
this.onBeatDetected();
478
this.beatDetected = true;
479
this.time.delayedCall(200, () => {
480
this.beatDetected = false;
481
});
482
}
483
}
484
}
485
486
onBeatDetected() {
487
// Flash screen on beat
488
this.cameras.main.flash(100, 255, 255, 255, false);
489
490
// Pulse visual elements
491
this.visualBars.forEach(bar => {
492
this.tweens.add({
493
targets: bar,
494
scaleX: 1.5,
495
duration: 100,
496
yoyo: true
497
});
498
});
499
}
500
}
501
```
502
503
### Audio Memory Management
504
Efficiently manage audio resources:
505
506
```javascript { .api }
507
class AudioMemoryScene extends Phaser.Scene {
508
create() {
509
// Pre-create sound instances for frequent sounds
510
this.soundPool = {
511
gunshot: [],
512
explosion: [],
513
pickup: []
514
};
515
516
// Pre-populate sound pools
517
for (let i = 0; i < 10; i++) {
518
this.soundPool.gunshot.push(this.sound.add('gunshot'));
519
this.soundPool.explosion.push(this.sound.add('explosion'));
520
this.soundPool.pickup.push(this.sound.add('pickup'));
521
}
522
523
// Track active sounds for cleanup
524
this.activeSounds = new Set();
525
}
526
527
playPooledSound(type, config = {}) {
528
const pool = this.soundPool[type];
529
if (pool) {
530
// Find available sound in pool
531
const sound = pool.find(s => !s.isPlaying);
532
if (sound) {
533
sound.play(config);
534
this.activeSounds.add(sound);
535
536
// Remove from active sounds when complete
537
sound.once('complete', () => {
538
this.activeSounds.delete(sound);
539
});
540
541
return sound;
542
}
543
}
544
return null;
545
}
546
547
fireWeapon() {
548
this.playPooledSound('gunshot', { volume: 0.6 });
549
}
550
551
explode() {
552
this.playPooledSound('explosion', { volume: 0.8 });
553
}
554
555
collectItem() {
556
this.playPooledSound('pickup', { volume: 0.4 });
557
}
558
559
cleanup() {
560
// Stop all active sounds
561
this.activeSounds.forEach(sound => {
562
sound.stop();
563
});
564
this.activeSounds.clear();
565
566
// Destroy all sounds in pools
567
Object.values(this.soundPool).forEach(pool => {
568
pool.forEach(sound => sound.destroy());
569
});
570
571
// Clear pools
572
this.soundPool = {};
573
}
574
575
shutdown() {
576
this.cleanup();
577
}
578
}
579
```
580
581
### Audio Settings
582
Implement user audio preferences:
583
584
```javascript { .api }
585
class AudioSettingsScene extends Phaser.Scene {
586
create() {
587
// Load saved audio settings
588
this.audioSettings = {
589
masterVolume: parseFloat(localStorage.getItem('masterVolume')) || 1.0,
590
musicVolume: parseFloat(localStorage.getItem('musicVolume')) || 0.8,
591
sfxVolume: parseFloat(localStorage.getItem('sfxVolume')) || 1.0,
592
muted: localStorage.getItem('audioMuted') === 'true'
593
};
594
595
// Apply settings
596
this.applyAudioSettings();
597
598
// Create audio categories
599
this.musicSounds = new Set();
600
this.sfxSounds = new Set();
601
602
// Background music
603
this.bgMusic = this.sound.add('bgm', { loop: true });
604
this.musicSounds.add(this.bgMusic);
605
this.bgMusic.play();
606
607
// UI for audio settings
608
this.createAudioUI();
609
}
610
611
createAudioUI() {
612
// Master volume slider
613
this.add.text(50, 50, 'Master Volume', { fontSize: '20px' });
614
this.masterSlider = this.createVolumeSlider(50, 80, this.audioSettings.masterVolume, (value) => {
615
this.audioSettings.masterVolume = value;
616
this.saveAudioSettings();
617
this.applyAudioSettings();
618
});
619
620
// Music volume slider
621
this.add.text(50, 130, 'Music Volume', { fontSize: '20px' });
622
this.musicSlider = this.createVolumeSlider(50, 160, this.audioSettings.musicVolume, (value) => {
623
this.audioSettings.musicVolume = value;
624
this.saveAudioSettings();
625
this.applyAudioSettings();
626
});
627
628
// SFX volume slider
629
this.add.text(50, 210, 'SFX Volume', { fontSize: '20px' });
630
this.sfxSlider = this.createVolumeSlider(50, 240, this.audioSettings.sfxVolume, (value) => {
631
this.audioSettings.sfxVolume = value;
632
this.saveAudioSettings();
633
this.applyAudioSettings();
634
});
635
636
// Mute button
637
this.muteButton = this.add.text(50, 300, this.audioSettings.muted ? 'Unmute' : 'Mute', {
638
fontSize: '20px',
639
backgroundColor: '#444444',
640
padding: { x: 10, y: 5 }
641
});
642
this.muteButton.setInteractive();
643
this.muteButton.on('pointerdown', () => {
644
this.audioSettings.muted = !this.audioSettings.muted;
645
this.muteButton.setText(this.audioSettings.muted ? 'Unmute' : 'Mute');
646
this.saveAudioSettings();
647
this.applyAudioSettings();
648
});
649
}
650
651
createVolumeSlider(x, y, initialValue, callback) {
652
const slider = {
653
background: this.add.rectangle(x, y, 200, 20, 0x666666),
654
handle: this.add.rectangle(x + (initialValue - 0.5) * 200, y, 20, 30, 0xffffff),
655
callback: callback
656
};
657
658
slider.handle.setInteractive({ draggable: true });
659
slider.handle.on('drag', (pointer, dragX) => {
660
const minX = x - 100;
661
const maxX = x + 100;
662
const clampedX = Phaser.Math.Clamp(dragX, minX, maxX);
663
slider.handle.x = clampedX;
664
665
const value = (clampedX - minX) / 200;
666
callback(value);
667
});
668
669
return slider;
670
}
671
672
applyAudioSettings() {
673
// Apply master volume and mute
674
this.sound.setVolume(this.audioSettings.muted ? 0 : this.audioSettings.masterVolume);
675
676
// Apply category-specific volumes
677
this.musicSounds.forEach(sound => {
678
const volume = this.audioSettings.musicVolume * this.audioSettings.masterVolume;
679
sound.setVolume(this.audioSettings.muted ? 0 : volume);
680
});
681
682
this.sfxSounds.forEach(sound => {
683
const volume = this.audioSettings.sfxVolume * this.audioSettings.masterVolume;
684
sound.setVolume(this.audioSettings.muted ? 0 : volume);
685
});
686
}
687
688
saveAudioSettings() {
689
localStorage.setItem('masterVolume', this.audioSettings.masterVolume.toString());
690
localStorage.setItem('musicVolume', this.audioSettings.musicVolume.toString());
691
localStorage.setItem('sfxVolume', this.audioSettings.sfxVolume.toString());
692
localStorage.setItem('audioMuted', this.audioSettings.muted.toString());
693
}
694
695
playMusic(key, config = {}) {
696
const sound = this.sound.add(key, config);
697
this.musicSounds.add(sound);
698
const volume = this.audioSettings.musicVolume * this.audioSettings.masterVolume;
699
sound.setVolume(this.audioSettings.muted ? 0 : volume);
700
return sound;
701
}
702
703
playSFX(key, config = {}) {
704
const sound = this.sound.add(key, config);
705
this.sfxSounds.add(sound);
706
const volume = this.audioSettings.sfxVolume * this.audioSettings.masterVolume;
707
sound.setVolume(this.audioSettings.muted ? 0 : volume);
708
sound.once('complete', () => {
709
this.sfxSounds.delete(sound);
710
sound.destroy();
711
});
712
sound.play();
713
return sound;
714
}
715
}
716
```
717
718
This comprehensive audio system provides all the tools needed to create rich, immersive soundscapes with efficient resource management, spatial audio, and user-configurable settings.