or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

actions.mdanimation.mdaudio.mdcameras.mddata-management.mdevents.mdgame-objects.mdindex.mdinput.mdloading.mdmath-geometry.mdphysics.mdrendering.mdscenes.mdtweens.mdutilities.md

audio.mddocs/

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.