Claude
Skills
Sign in
Back

audio-and-sound

Included with Lifetime
$97 forever

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.

Image & Video

What this skill does


# 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

```js
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()`:

1. If `config.audio.noAudio` is true, or the device supports neither Web Audio nor HTML5 Audio, a **NoAudioSoundManager** is created (all calls are no-ops).
2. If the device supports Web Audio and `config.audio.disableWebAudio` is not true, a **WebAudioSoundManager** is created (preferred).
3. 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`, default `true`)
- 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).

```js
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 manager
```

## Common Patterns

### Playing Sounds

**Fire-and-forget** -- `this.sound.play(key, config?)` adds, plays, and auto-destroys the sound on completion:

```js
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:

```js
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()`).

```js
// 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

```js
// Via config at creation
const bgm = this.sound.add('music', { loop: true });
bgm.play();

// Toggle during playback
bgm.loop = false;
bgm.setLoop(false);  // chainable
```

The `LOOPED` event fires each time the sound loops back to the start. The `LOOP` event fires when the loop property changes.

### Seeking

```js
sound.seek = 5.0;        // jump to 5 seconds in
sound.setSeek(5.0);      // chainable
console.log(sound.seek);  // current playback position in seconds
```

Setting seek on a stopped sound has no effect.

### Stereo Panning

```js
sound.pan = -1;   // full left
sound.pan = 0;    // center
sound.pan = 1;    // full right
sound.setPan(0.5); // chainable
```

Uses `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.

```js
// 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:

```js
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

```js
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.

```js
// 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

```js
// Per-sound
sound.mute = true;
sound.setMute(true);

// Global
this.sound.mute = true;
this.sound.setMute(true);
```

### Querying Sounds

```js
this.sound.get('coin');          // first sound with key, or nu
Files: 1
Size: 19.0 KB
Complexity: 23/100
Category: Image & Video

Related in Image & Video