Audio And Sound
Use this skill when adding audio or sound to a Phaser 4 game. Covers loading audio, playing sounds, music, volume, spatial audio, Web Audio API, and SoundManager. Triggers on: sound, audio, music, volume, mute.
- Skill ID
- phaserjs/phaser/audio-and-sound
- Publisher
- phaserjs
- Repository
- phaser
- Installs
- 0
- Files
- 1
- Synced
- Sep 23, 2026
Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.
phaserjs/phaser/audio-and-soundInstalls these files- SKILL.md
What this skill tells the agent
Audio and Sound
Phaser provides a unified Sound system via this.sound (a SoundManager) that abstracts over Web Audio API and HTML5 Audio. It handles loading, playback, volume, panning, looping, markers, audio sprites, spatial audio, and browser autoplay-policy unlocking.Key source paths: src/sound/BaseSoundManager.js, src/sound/BaseSound.js, src/sound/webaudio/, src/sound/html5/, src/sound/SoundManagerCreator.js, src/sound/events/, src/sound/typedefs/ Related skills: ../loading-assets/SKILL.md, ../game-setup-and-config/SKILL.md
Quick Start
class GameScene extends Phaser.Scene {
preload() {
this.load.audio('bgm', 'assets/music.mp3');
this.load.audio('coin', ['assets/coin.ogg', 'assets/coin.mp3']);
}
create() {
// Fire-and-forget (auto-destroys when complete)
this.sound.play('coin');
// Retained reference for ongoing control
this.music = this.sound.add('bgm', { loop: true, volume: 0.5 });
this.music.play();
}
}Assets loaded via this.load.audio() in preload() are ready by the time create() runs. Provide an array of URLs for cross-browser format fallback.
Core Concepts
WebAudio vs HTML5 Audio
Phaser auto-selects the best backend via SoundManagerCreator.create():
- If
config.audio.noAudiois true, or the device supports neither Web Audio nor HTML5 Audio, a NoAudioSoundManager is created (all calls are no-ops). - If the device supports Web Audio and
config.audio.disableWebAudiois not true, a WebAudioSoundManager is created (preferred). - Otherwise, an HTML5AudioSoundManager is created as fallback.
WebAudio advantages: precise timing, gapless looping, stereo panning (StereoPannerNode), spatial audio (PannerNode), per-sound gain nodes, decodeAudio() for runtime decoding.
HTML5 Audio limitations: no spatial audio, no real stereo panning (pan fires events but no audible effect), less precise looping, requires instances count at load time for simultaneous playback.
Force HTML5 or disable audio via game config: audio: { disableWebAudio: true } or audio: { noAudio: true }. Pass audio: { context: existingAudioContext } to reuse a WebAudio context in SPAs.
The SoundManager (this.sound)
Accessed via this.sound in any Scene. It is a single shared instance across the entire game. Key responsibilities:
- Adding, playing, and removing sound instances
- Global volume, mute, rate, and detune
- Automatic pause/resume when the browser tab loses/gains focus (
pauseOnBlur, defaulttrue) - Audio unlock handling for mobile browsers
- Spatial audio listener position (WebAudio only)
Sound Instances
Created via this.sound.add(key, config). Each instance has its own playback state, volume, rate, detune, loop, pan, and seek properties. A sound must exist in the audio cache (loaded via the Loader) before it can be added.
State flags: isPlaying (boolean), isPaused (boolean).
const sfx = this.sound.add('explosion', { volume: 0.8 });
sfx.play(); // returns boolean
sfx.pause(); // only works if isPlaying
sfx.resume(); // only works if isPaused
sfx.stop(); // resets to stopped state
sfx.destroy(); // marks for removal from managerCommon Patterns
Playing Sounds
Fire-and-forget -- this.sound.play(key, config?) adds, plays, and auto-destroys the sound on completion:
this.sound.play('explosion');
this.sound.play('powerup', { volume: 0.5, rate: 1.2 });Retained reference -- this.sound.add(key, config?) then call play() on the instance:
const laser = this.sound.add('laser');
laser.play();
// Later: laser.stop(), laser.volume = 0.3, etc.Volume, Rate, and Detune
Each property can be set per-sound or globally on the manager. Global and per-sound values combine (for rate/detune, they multiply via calculateRate()).
// Per-sound
sound.volume = 0.5; // 0 to 1
sound.setVolume(0.5); // chainable alternative
sound.rate = 1.5; // 0.5 = half speed, 2.0 = double speed
sound.setRate(1.5);
sound.detune = 200; // cents, -1200 to 1200
sound.setDetune(200);
// Global (affects all sounds)
this.sound.volume = 0.8;
this.sound.setVolume(0.8);
this.sound.rate = 1.0;
this.sound.setRate(1.0);
this.sound.detune = 0;
this.sound.setDetune(0);The effective playback rate is: sound.rate * manager.rate * detuneRate where detuneRate = Math.pow(1.0005777895065548, sound.detune + manager.detune).
Looping
// Via config at creation
const bgm = this.sound.add('music', { loop: true });
bgm.play();
// Toggle during playback
bgm.loop = false;
bgm.setLoop(false); // chainableThe LOOPED event fires each time the sound loops back to the start. The LOOP event fires when the loop property changes.
Seeking
sound.seek = 5.0; // jump to 5 seconds in
sound.setSeek(5.0); // chainable
console.log(sound.seek); // current playback position in secondsSetting seek on a stopped sound has no effect.
Stereo Panning
sound.pan = -1; // full left
sound.pan = 0; // center
sound.pan = 1; // full right
sound.setPan(0.5); // chainableUses StereoPannerNode, if it exists, on WebAudio. On HTML5 Audio, the pan property fires events but has no audible effect.
Audio Sprites and Markers
Audio sprites combine multiple sounds into a single audio file with a JSON config (generated by the audiosprite tool). The JSON must be loaded separately.
// In preload
this.load.audioSprite('sfx', 'assets/sfx.json', ['assets/sfx.ogg', 'assets/sfx.mp3']);
// In create
this.sound.playAudioSprite('sfx', 'explosion');
this.sound.playAudioSprite('sfx', 'coin', { volume: 0.5 });
// Or add for retained control
const sprite = this.sound.addAudioSprite('sfx');
sprite.play('explosion');The JSON spritemap entries are automatically converted to markers with name, start, duration, and optional loop.
Manual markers -- you can also add markers to any sound:
const sound = this.sound.add('longtrack');
sound.addMarker({ name: 'intro', start: 0, duration: 5 });
sound.addMarker({ name: 'loop', start: 5, duration: 20, config: { loop: true } });
sound.addMarker({ name: 'outro', start: 25, duration: 3 });
sound.play('intro');
// Later
sound.play('loop');Marker API on BaseSound: addMarker(marker), updateMarker(marker), removeMarker(markerName).
Background Music Pattern
this.bgm = this.sound.add('theme', { loop: true, volume: 0.4 });
this.bgm.play();
// Stop on scene shutdown: this.bgm.stop();The manager's pauseOnBlur (default true) automatically pauses all sounds when the tab loses focus.
Spatial Audio (WebAudio Only)
Spatial audio uses the Web Audio PannerNode to position sounds in 2D/3D space relative to a listener.
// Set the listener position (typically your camera or player)
this.sound.setListenerPosition(400, 300);
// Or update directly: this.sound.listenerPosition.set(x, y);
// Create a spatialized sound with a source config
const enemy = this.sound.add('roar', {
source: {
x: 800,
y: 300,
refDistance: 50,
maxDistance: 2000,
rolloffFactor: 1,
distanceModel: 'inverse',
panningModel: 'equalpower',
follow: enemySprite // auto-track a Game Object's x/y
}
});
enemy.play();You can set sound.x and sound.y directly on a WebAudioSound to reposition it at any time. If follow is set to an object with x/y properties, the spatial position updates automatically each frame.
setListenerPosition() defaults to the center of the game canvas if called with no arguments.
Muting
// Per-sound
sound.mute = true;
sound.setMute(true);
// Global
this.sound.mute = true;
this.sound.setMute(true);Querying Sounds
More from phaserjs
Use this skill in a project
Open any RiverX project, search the Skills panel for phaserjs/phaser/audio-and-sound, and install it in one click.
