Audio
Add sound effects, background music, and audio sprites to your Phaser games.
What you'll accomplish
Implement sound effects and background music in your game.
Prerequisites
- Working Phaser game with sprites and physics
Audio in Web Games
Web browsers have strict audio policies:
- Audio can't autoplay until user interaction
- Audio context must be unlocked by a click/touch
Phaser handles most of this automatically, but you should:
- Start music after a user click (like "Play" button)
- Have a mute button
Loading Audio
Load audio files in preload():
typescriptpreload(): void { // Single sound this.load.audio('jump', 'assets/sounds/jump.wav'); this.load.audio('coin', 'assets/sounds/coin.mp3'); // Background music this.load.audio('bgMusic', 'assets/music/background.mp3'); // Multiple formats for browser compatibility this.load.audio('explosion', [ 'assets/sounds/explosion.ogg', 'assets/sounds/explosion.mp3', ]); }
Note
Use .mp3 for broad compatibility. .ogg is smaller but not supported in Safari. Provide both for best results.
Playing Sounds
typescriptcreate(): void { // Play once this.sound.play('jump'); // With config this.sound.play('coin', { volume: 0.5, // 0 to 1 rate: 1, // Playback speed (1 = normal) detune: 0, // Pitch adjustment in cents loop: false, delay: 0, // Delay before playing (seconds) }); } // Play on collision collectCoin(): void { this.sound.play('coin', { volume: 0.7 }); this.score += 10; } // Play on jump update(): void { if (this.cursors.up.isDown && this.player.body.blocked.down) { this.player.body.setVelocityY(-400); this.sound.play('jump'); } }
Sound Objects
For more control, create sound objects:
typescriptprivate jumpSound!: Phaser.Sound.BaseSound; private coinSound!: Phaser.Sound.BaseSound; create(): void { // Create sound objects this.jumpSound = this.sound.add('jump'); this.coinSound = this.sound.add('coin', { volume: 0.5, rate: 1, }); } update(): void { if (this.cursors.up.isDown && this.player.body.blocked.down) { this.jumpSound.play(); } } collectCoin(): void { // Slightly randomize pitch each time this.coinSound.play({ detune: Phaser.Math.Between(-100, 100), }); }
Background Music
typescriptprivate music!: Phaser.Sound.BaseSound; create(): void { // Create and configure music this.music = this.sound.add('bgMusic', { volume: 0.3, loop: true, }); // Start after user interaction (e.g., in menu scene) this.input.once('pointerdown', () => { this.music.play(); }); } shutdown(): void { // Stop music when leaving scene this.music.stop(); }
Cross-Scene Music
For music that persists across scenes:
typescript// In your main menu scene create(): void { // Only start if not already playing if (!this.sound.get('bgMusic')) { this.music = this.sound.add('bgMusic', { loop: true, volume: 0.3 }); this.music.play(); } } // In game over scene - stop the music create(): void { const music = this.sound.get('bgMusic'); if (music) { music.stop(); } }
Mute Control
Provide audio controls for players:
typescriptcreate(): void { // Mute button const muteBtn = this.add.text(750, 20, '🔊', { fontSize: '32px' }) .setInteractive({ useHandCursor: true }); muteBtn.on('pointerdown', () => { this.sound.mute = !this.sound.mute; muteBtn.setText(this.sound.mute ? '🔇' : '🔊'); }); } // Or use keyboard create(): void { this.input.keyboard?.on('keydown-M', () => { this.sound.mute = !this.sound.mute; }); }
Volume Control
typescriptcreate(): void { // Global volume (affects all sounds) this.sound.volume = 0.5; // Separate music and SFX volumes this.musicVolume = 0.3; this.sfxVolume = 0.7; // Apply to individual sounds this.music = this.sound.add('bgMusic', { volume: this.musicVolume }); this.jumpSound = this.sound.add('jump', { volume: this.sfxVolume }); } setMusicVolume(value: number): void { this.musicVolume = value; if (this.music) { (this.music as Phaser.Sound.WebAudioSound).setVolume(value); } }
Audio Sprites
Pack multiple sounds into one file with timing data:
json// assets/sounds/sfx.json { "spritemap": { "jump": { "start": 0, "end": 0.5 }, "coin": { "start": 0.5, "end": 1.0 }, "explosion": { "start": 1.0, "end": 2.5 }, "powerup": { "start": 2.5, "end": 3.2 } } }
typescriptpreload(): void { this.load.audioSprite('sfx', 'assets/sounds/sfx.json', [ 'assets/sounds/sfx.ogg', 'assets/sounds/sfx.mp3', ]); } create(): void { // Play a sprite sound this.sound.playAudioSprite('sfx', 'jump'); this.sound.playAudioSprite('sfx', 'coin', { volume: 0.5 }); }
Spatial Audio
For games with positional sound:
typescriptcreate(): void { // Create sound with spatial properties const enemySound = this.sound.add('enemy', { volume: 1, loop: true, }); enemySound.play(); } update(): void { // Adjust volume based on distance const distance = Phaser.Math.Distance.Between( this.player.x, this.player.y, this.enemy.x, this.enemy.y ); const maxDistance = 500; const volume = Math.max(0, 1 - distance / maxDistance); (this.enemySound as Phaser.Sound.WebAudioSound).setVolume(volume); // Pan based on position const pan = (this.enemy.x - this.player.x) / 400; (this.enemySound as Phaser.Sound.WebAudioSound).setPan( Phaser.Math.Clamp(pan, -1, 1) ); }
Audio Events
React to audio events:
typescriptcreate(): void { const music = this.sound.add('bgMusic'); music.on('play', () => console.log('Started playing')); music.on('pause', () => console.log('Paused')); music.on('resume', () => console.log('Resumed')); music.on('stop', () => console.log('Stopped')); music.on('complete', () => console.log('Finished')); music.on('looped', () => console.log('Loop iteration')); music.play(); }
Sound Manager Properties
typescript// Check if audio is available if (this.sound.locked) { // Audio context is locked, waiting for user interaction } // Check if muted const isMuted = this.sound.mute; // Get all playing sounds const playingSounds = this.sound.getAllPlaying(); // Stop all sounds this.sound.stopAll(); // Pause all sounds this.sound.pauseAll(); // Resume all sounds this.sound.resumeAll();
Free Sound Resources
| Source | Type | License |
|---|---|---|
| Freesound | Sound effects | Various CC |
| OpenGameArt | Music & SFX | Various |
| Kenney | Complete packs | CC0 |
| JSFXR | Retro sound generator | - |
Common Issues
Frequently Asked Questions
- Check browser console for errors
- Ensure audio starts after user interaction
- Check file path and format
- Try wrapping in a click handler:
typescriptthis.input.once('pointerdown', () => { this.sound.play('music'); });
Use Web Audio (Phaser's default) and preload all sounds. Consider shorter sound files. Use audio sprites to reduce HTTP requests.
Track if sound is already playing:
typescriptif (!this.jumpSound.isPlaying) { this.jumpSound.play(); }
The scene stops all its sounds on shutdown. Either:
- Play music from a persistent scene
- Use
this.sound.get('music')to check if already playing - Mark the sound as not scene-owned
Verify Your Understanding
You've completed this lesson if you can:
Next: Build and deploy your Phaser game.