
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.
- Installs
- 947
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/phaserjs/phaser --skill audio-and-sound- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads audio-and-sound automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
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 manager
Common 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); // chainable
The 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 seconds
Setting 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); // 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.
// 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
this.sound.get('coin'); // first sound with key, or null
this.sound.getAll('coin'); // all sounds with key
this.sound.getAll(); // every sound in the manager
this.sound.getAllPlaying(); // all currently playing sounds
this.sound.isPlaying('coin'); // true if any 'coin' sound is playing
this.sound.isPlaying(); // true if any sound is playing
Removing and Stopping
this.sound.stopAll(); // stop all sounds, fires STOP_ALL
this.sound.stopByKey('coin'); // stop all sounds with key, returns count
this.sound.pauseAll(); // pause all, fires PAUSE_ALL
this.sound.resumeAll(); // resume all, fires RESUME_ALL
this.sound.remove(soundInstance); // destroy + remove specific sound
this.sound.removeByKey('coin'); // destroy + remove all with key, returns count
this.sound.removeAll(); // destroy + remove everything
Decoding Audio at Runtime (WebAudio Only)
this.sound.decodeAudio('key', base64StringOrArrayBuffer);
// Or batch: this.sound.decodeAudio([{ key: 'sfx1', data: buf1 }, { key: 'sfx2', data: buf2 }]);
this.sound.on('decoded', (key) => { /* one done */ });
this.sound.on('decodedall', () => { /* all done */ });
Configuration Reference
SoundConfig
| Property | Type | Default | Description |
|---|---|---|---|
mute | boolean | false | Whether the sound is muted |
volume | number | 1 | Volume, 0 (silence) to 1 (full) |
rate | number | 1 | Playback speed (0.5 = half, 2.0 = double) |
detune | number | 0 | Detuning in cents (-1200 to 1200) |
seek | number | 0 | Start playback position in seconds |
loop | boolean | false | Whether the sound should loop |
delay | number | 0 | Delay before playback starts, in seconds |
pan | number | 0 | Stereo pan, -1 (left) to 1 (right) |
source | SpatialSoundConfig | null | Spatial audio configuration (WebAudio only) |
SpatialSoundConfig
Position: x (0), y (0), z (0) -- source position in world space.
Orientation: orientationX (0), orientationY (0), orientationZ (-1) -- source direction vector.
Models: panningModel ('equalpower' or 'HRTF'), distanceModel ('linear', 'inverse', 'exponential').
Distance: refDistance (1), maxDistance (10000), rolloffFactor (1).
Cone: coneInnerAngle (360), coneOuterAngle (0), coneOuterGain (0).
Tracking: follow (null) -- a Vector2Like object whose x/y is auto-tracked each frame.
SoundMarker
| Property | Type | Default | Description |
|---|---|---|---|
name | string | (required) | Unique identifier for the marker |
start | number | 0 | Start position in seconds |
duration | number | (remaining) | Playback duration in seconds |
config | SoundConfig | {} | Default settings for this marker |
Events
Sound Instance Events (emitted on a Sound object)
| Event Constant | String Value | Callback Args | When |
|---|---|---|---|
Events.PLAY | 'play' | (sound) | Sound starts playing |
Events.PAUSE | 'pause' | (sound) | Sound is paused |
Events.RESUME | 'resume' | (sound) | Sound resumes from pause |
Events.STOP | 'stop' | (sound) | Sound is stopped |
Events.COMPLETE | 'complete' | (sound) | Sound finishes (non-looping) |
Events.LOOPED | 'looped' | (sound) | Sound loops back to start |
Events.LOOP | 'loop' | (sound, value) | Loop property changes |
Events.MUTE | 'mute' | (sound, value) | Mute state changes |
Events.VOLUME | 'volume' | (sound, value) | Volume changes |
Events.RATE | 'rate' | (sound, value) | Rate changes |
Events.DETUNE | 'detune' | (sound, value) | Detune changes |
Events.SEEK | 'seek' | (sound, value) | Seek position changes |
Events.PAN | 'pan' | (sound, value) | Pan value changes |
Events.DESTROY | 'destroy' | (sound) | Sound is destroyed |
SoundManager Events (emitted on this.sound)
| Event Constant | String Value | Callback Args | When |
|---|---|---|---|
Events.PAUSE_ALL | 'pauseall' | (manager) | pauseAll() called |
Events.RESUME_ALL | 'resumeall' | (manager) | resumeAll() called |
Events.STOP_ALL | 'stopall' | (manager) | stopAll() called |
Events.GLOBAL_MUTE | 'globalmute' | (manager, value) | Global mute changes |
Events.GLOBAL_VOLUME | 'globalvolume' | (manager, value) | Global volume changes |
Events.GLOBAL_RATE | 'globalrate' | (manager, value) | Global rate changes |
Events.GLOBAL_DETUNE | 'globaldetune' | (manager, value) | Global detune changes |
Events.UNLOCKED | 'unlocked' | (manager) | Audio system unlocked after user interaction |
Events.DECODED | 'decoded' | (key) | Single audio key decoded (WebAudio) |
Events.DECODED_ALL | 'decodedall' | () | All queued audio decoded (WebAudio) |
// Example: listen for completion
const sfx = this.sound.add('bang');
sfx.on('complete', (sound) => {
console.log(sound.key, 'finished');
});
sfx.play();
API Quick Reference
BaseSoundManager (this.sound)
Methods: add(key, config?), addAudioSprite(key, config?), play(key, extra?), playAudioSprite(key, spriteName, config?), get(key), getAll(key?), getAllPlaying(), isPlaying(key?), remove(sound), removeByKey(key), removeAll(), stopAll(), stopByKey(key), pauseAll(), resumeAll(), setListenerPosition(x?, y?), setMute(value), setVolume(value), setRate(value), setDetune(value).
Properties: volume (0-1), mute (boolean), rate (number), detune (-1200 to 1200), pauseOnBlur (boolean, default true), locked (boolean, read-only), listenerPosition (Vector2), sounds (array, private).
BaseSound (sound instance)
Methods: play(markerName?, config?), pause(), resume(), stop(), destroy(), addMarker(marker), updateMarker(marker), removeMarker(name), setMute(value), setVolume(value), setRate(value), setDetune(value), setSeek(value), setLoop(value), setPan(value).
Properties: volume (0-1), mute (boolean), rate (number), detune (number), seek (seconds), loop (boolean), pan (-1 to 1), isPlaying (read-only), isPaused (read-only), duration (seconds), totalDuration (seconds), key (string), x / y (spatial position, WebAudio only).
All set* methods return this for chaining.
Gotchas
Browser Autoplay Policy
Browsers block audio until user interaction. Phaser handles this automatically:
- WebAudio:
AudioContextstarts suspended. Phaser listens for touchstart/touchend/mousedown/mouseup/keydown ondocument.bodyto callcontext.resume(). Thelockedproperty is true until unlocked;UNLOCKEDevent fires once resolved. - HTML5 Audio: Locked audio tags queue all actions until the first touch replays them.
You do not need to handle unlocking manually. To know when ready, listen for UNLOCKED:
if (this.sound.locked) {
this.sound.once('unlocked', () => {
this.sound.play('bgm');
});
} else {
this.sound.play('bgm');
}
Audio Format Support
No single format works everywhere. Provide multiple formats: this.load.audio('bgm', ['assets/bgm.ogg', 'assets/bgm.mp3']). MP3 has broadest support. OGG Vorbis lacks Safari support. AAC/M4A works well on Safari/iOS. WebM/Opus has excellent quality but limited older browser support.
HTML5 Audio Simultaneous Playback
HTML5 Audio uses a pool of <audio> tags. Specify instances when loading for simultaneous playback: this.load.audio('shot', 'assets/shot.mp3', { instances: 4 }). Default is 1. If all tags are in use and manager.override is true (default), the sound with the most progress is hijacked. WebAudio has no such limitation.
iOS/Safari Specifics
StereoPannerNodenot supported on iOS/Safari, sopanhas no audible effect (events still fire).- iOS 17/18+ can interrupt audio on background. Phaser handles this via
context.suspend()/context.resume()on theVISIBLEgame event. setListenerPosition()and spatial audio are WebAudio-only.
WebAudio Context Reuse
For SPAs that recreate the game without a full page reload, pass audio: { context: existingAudioContext } in the game config. You can also swap contexts at runtime via this.sound.setAudioContext(newContext) (WebAudio only).
Sound Manager is Shared (Global)
There is one SoundManager per game, not per scene. this.sound in every scene references the same manager. Sounds are not automatically cleaned up on scene shutdown -- you must stop/remove them yourself if needed. Looping sounds will continue playing across scene changes unless explicitly stopped.
Fire-and-Forget vs Persistent Sounds
this.sound.play(key) creates a sound that auto-destroys on completion -- you cannot control it after calling play. Use this.sound.add(key) when you need a persistent reference to pause, stop, adjust volume, or listen for events.
Spatial Audio is WebAudio Only
The source config for spatial audio is silently ignored when using HTML5 Audio. If your game must support HTML5 Audio fallback, do not rely on spatial positioning for gameplay-critical audio cues.
Web Audio Analyser (WebAudio Only)
Access the underlying AudioContext to create an AnalyserNode for frequency/waveform visualization:
const analyser = this.sound.context.createAnalyser();
analyser.fftSize = 256;
this.sound.masterVolumeNode.connect(analyser);
analyser.connect(this.sound.context.destination);
const dataArray = new Uint8Array(analyser.frequencyBinCount);
// In update loop
analyser.getByteFrequencyData(dataArray);
// Use dataArray values to drive visual effects
This only works with the WebAudioSoundManager. Check this.sound.context exists before using.
Source File Map
| File | Purpose |
|---|---|
src/sound/SoundManagerCreator.js | Factory: picks WebAudio, HTML5, or NoAudio manager |
src/sound/BaseSoundManager.js | Base manager: add, play, get, stop/pause/resume all, global volume/rate/detune |
src/sound/BaseSound.js | Base sound: play/pause/resume/stop, markers, config, calculateRate |
src/sound/webaudio/WebAudioSoundManager.js | WebAudio manager: AudioContext, gain nodes, unlock, spatial listener, decodeAudio |
src/sound/webaudio/WebAudioSound.js | WebAudio sound: buffer sources, spatial/panner nodes, seek, all properties |
src/sound/html5/HTML5AudioSoundManager.js | HTML5 manager: audio tag pooling, locked queue, override |
src/sound/html5/HTML5AudioSound.js | HTML5 sound: tag-based playback, limited feature set |
src/sound/noaudio/NoAudioSoundManager.js | No-op manager for environments without audio |
src/sound/events/ | All sound event constants (PLAY, STOP, COMPLETE, etc.) |
src/sound/typedefs/SoundConfig.js | SoundConfig type definition |
src/sound/typedefs/SoundMarker.js | SoundMarker type definition |
src/sound/typedefs/SpatialSoundConfig.js | SpatialSoundConfig type definition |
src/sound/typedefs/AudioSpriteSound.js | AudioSpriteSound type definition |
FAQ
What does the audio-and-sound skill do?
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.
How do I install audio-and-sound?
Run npx skills add https://github.com/phaserjs/phaser --skill audio-and-sound in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/audio-and-sound/ (personal) or .claude/skills/audio-and-sound/ (project).
Which AI agents can use audio-and-sound?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is audio-and-sound safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains audio-and-sound?
It is published by phaserjs in phaserjs/phaser. It has 947 recorded installs.
More from phaserjs/phaser
game-setup-and-config
phaserjs/phaser
Use this skill when creating a new Phaser 4 game instance or configuring GameConfig options. Covers renderer selection, canvas setup, scaling, pixel art, FPS settings, boot sequence, and all config sub-objects. Triggers on: new Phaser.Game, GameConfig, game setup, renderer, pixel art, FPS.
sprites-and-images
phaserjs/phaser
Use this skill when creating Sprites or Images in Phaser 4. Covers factory methods, texture/frame selection, position, scale, rotation, tint, flip, alpha, origin, depth, and the component mixin system. Triggers on: Sprite, Image, this.add.sprite, this.add.image, texture, setTint, setAlpha.
animations
phaserjs/phaser
Use this skill when creating or controlling sprite animations in Phaser 4. Covers spritesheets, atlases, AnimationManager, AnimationState, play/stop/chain, frame callbacks, and animation events. Triggers on: sprite animation, spritesheet, play animation, animation frames.
tilemaps
phaserjs/phaser
Use this skill when working with tilemaps in Phaser 4. Covers loading Tiled JSON maps, creating tilemap layers, tile collision, dynamic tiles, tile properties, and tilemap camera culling. Triggers on: Tilemap, Tiled, tilemap layer, tile collision, tile properties.
v4-new-features
phaserjs/phaser
Use this skill when learning about new features, game objects, components, and rendering capabilities added in Phaser 4. Covers Filters, RenderNodes, CaptureFrame, Gradient, Noise, SpriteGPULayer, TilemapGPULayer, Lighting component, RenderSteps, and new tint modes. Triggers on: new in v4, Phaser 4 features, RenderNode, SpriteGPULayer, CaptureFrame, Gradient game object, Noise game object, new tint modes. For migrating v3 code to v4, see the v3-to-v4-migration skill instead.
scenes
phaserjs/phaser
Use this skill when working with Phaser 4 scenes. Covers scene lifecycle methods, scene transitions, parallel scenes, scene communication, sleeping, pausing, restarting, and the SceneManager. Triggers on: Scene, scene lifecycle, preload, create, update, scene transition, SceneManager.
Popular media & creative skills
hyperframes
heygen-com/hyperframes
Read first to make, edit, animate or render any video with HyperFrames (HTML rendered to video), including launch films, explainers, music videos, charts, maps, vertical clips, title cards, captions and overlays. Holds the render contract, the work loop and what each kind of video needs.
media-use
heygen-com/hyperframes
Get or make any media for a HyperFrames video - music, sound effects, images, icons, logos, voiceover, avatar and generated video, transcripts, cutouts, colour grades and looks - with HeyGen or free local tools, and mix placed audio. Also for footage that looks dark, flat or dull, or should be stylized or hidden.
hyperframes-registry
heygen-com/hyperframes
About 450 ready-made HyperFrames blocks and components installed as editable source - transitions, overlays, titles, kinetic type, captions, charts, map overlays, 3D, camera moves. Use it before hand-building any named visual (CRT scanlines, glitch, film grain, a chart); also for hyperframes add and catalog.
hyperframes-creative
heygen-com/hyperframes
Non-animation creative direction for HyperFrames videos. Use for design spec (frame.md / design.md) handling, palettes, typography, narration, beat planning, audio-reactive visuals, composition patterns, and brand / style decisions. For atomic motion patterns and scene blueprints, use hyperframes-animation.
ai-video-generation
101-skills/superpowers
Generate AI videos with Google Veo, Seedance 2.0, HappyHorse, Wan, Grok and 40+ models via inference.sh CLI. Models: Veo 3.1, Veo 3, Seedance 2.0, HappyHorse 1.0, Wan 2.5, Grok Imagine Video, OmniHuman, Fabric, HunyuanVideo. Capabilities: text-to-video, image-to-video, reference-to-video, video editing, lipsync, avatar animation, video upscaling, foley sound. Use for: social media videos, marketing content, explainer videos, product demos, AI avatars. Triggers: video generation, ai video, text to video, image to video, veo, animate image, video from image, ai animation, video generator, generate video, t2v, i2v, ai video maker, create video with ai, runway alternative, pika alternative, sora alternative, kling alternative, seedance, happyhorse
ai-image-generation
101-skills/superpowers
Generate AI images with GPT-Image-2, FLUX, Gemini, Grok, Seedream, Reve and 50+ models via inference.sh CLI. Models: GPT-Image-2, FLUX Dev LoRA, FLUX.2 Klein LoRA, Gemini 3 Pro Image, Grok Imagine, Seedream 4.5, Reve, ImagineArt. Capabilities: text-to-image, image-to-image, inpainting, LoRA, image editing, upscaling, text rendering. Use for: AI art, product mockups, concept art, social media graphics, marketing visuals, illustrations. Triggers: flux, image generation, ai image, text to image, stable diffusion, generate image, ai art, midjourney alternative, dall-e alternative, text2img, t2i, image generator, ai picture, create image with ai, generative ai, ai illustration, grok image, gemini image, gpt image, openai image, chatgpt image