Audio
YAGE wraps @pixi/sound for audio playback.
The @yagejs/audio package provides channel-based mixing, a SoundComponent for
entity-bound audio, and a sound() asset factory for preloading.
AudioPlugin Setup
Section titled “AudioPlugin Setup”import { AudioPlugin } from "@yagejs/audio";
engine.use(new AudioPlugin({ channels: { sfx: { volume: 1.0 }, music: { volume: 0.7 }, },}));Sound Assets
Section titled “Sound Assets”Use the sound() factory to create preloadable asset handles:
import { sound } from "@yagejs/audio";
const CoinSfx = sound("assets/coin.wav");const BgMusic = sound("assets/music.mp3");Add them to your scene’s preload array so they load before onEnter:
class GameScene extends Scene { readonly preload = [CoinSfx, BgMusic];}Playing Sounds
Section titled “Playing Sounds”Resolve the AudioManager from the DI container and call play:
import { AudioManagerKey } from "@yagejs/audio";
const audio = this.use(AudioManagerKey);
// Play a sound effectaudio.play(CoinSfx.path, { channel: "sfx" });
// Play background music (looping)audio.play(BgMusic.path, { channel: "music", loop: true, volume: 0.5 });Play options:
audio.play(alias, { channel: "sfx", // which channel (default: "sfx") volume: 1, // instance volume 0–1 (default: 1) loop: false, // loop playback (default: false) speed: 1, // playback rate (default: 1)});Additional play methods:
// Only play if not already playing via playOnceaudio.playOnce(BgMusic.path, { channel: "music", loop: true });
// Pick a random sound from an arrayaudio.playRandom(["step1.wav", "step2.wav", "step3.wav"], { channel: "sfx" });Sound Handles
Section titled “Sound Handles”play returns a SoundHandle for controlling playback:
const handle = audio.play(BgMusic.path, { channel: "music", loop: true });
handle.playing; // booleanhandle.volume = 0.5; // adjust volumehandle.speed = 1.5; // adjust speedhandle.paused = true; // pause/resumehandle.muted = true; // mute/unmutehandle.stop(); // stop playbackYou can also stop via the manager:
audio.stop(handle);Channels
Section titled “Channels”Channels group sounds for independent volume control. The default channels are
"sfx" and "music", but you can define any channels in the config.
// Volume controlaudio.setChannelVolume("music", 0.3);audio.getChannelVolume("music"); // 0.3
// Mute/unmute a channelaudio.muteChannel("sfx");audio.unmuteChannel("sfx");
// Pause/resume a channelaudio.pauseChannel("music");audio.resumeChannel("music");
// Stop all sounds in a channelaudio.stopChannel("sfx");Global Mute
Section titled “Global Mute”audio.muteAll();audio.unmuteAll();audio.stopAll();Audio Unlock
Section titled “Audio Unlock”Web browsers suspend the AudioContext when a page loads and require a user
gesture (click, tap, or keypress) before any audio can play. @pixi/sound
attaches a one-shot gesture listener that resumes the context on the first
interaction, so calling audio.play(...) after a click just works.
What this means in practice: any audio you schedule on page load is silent
until the user first interacts with the page. That is expected browser
behavior, not a YAGE bug — the classic symptom is “my title music doesn’t
play until I click somewhere.” To handle the gap cleanly, use onUnlock:
const audio = this.use(AudioManagerKey);
audio.onUnlock(() => { audio.play("music/title", { channel: "music", loop: true });});onUnlock fires synchronously if the context is already running, or once on
the next gesture that resumes it. It returns a disposer, so you can cancel a
pending registration:
const dispose = audio.onUnlock(() => startMusic());// …laterdispose();// or: audio.offUnlock(startMusic);Expose the pending state to the UI if you want a “tap to enable audio” hint:
if (!audio.isUnlocked()) { // render a prompt; it's safe to dismiss on first click — // @pixi/sound will have resumed the context by then}isUnlocked() reflects AudioContext.state === "running" and is never
flipped by the mute/pause-on-blur flags below — it is strictly the
browser-capability check.
Tab Visibility
Section titled “Tab Visibility”Most web games go silent when the user backgrounds the tab. AudioManager
suspends the underlying AudioContext when the window loses focus and
resumes it on return:
engine.use(new AudioPlugin({ autoMuteOnBlur: true, // default: true}));
// Or toggle at runtime:audio.autoMuteOnBlur = false;autoMuteOnBlur is delegated to @pixi/sound’s built-in
WebAudioContext.autoPause, which listens for window.blur/focus events
and pauses/resumes the entire AudioContext. Per-channel mutes and volumes
are untouched. Toggling the flag while the window is currently unfocused
takes effect immediately (the manager reconciles paused for you);
otherwise the new policy applies on the next blur event.
Pausing scenes on tab blur is a scene-lifecycle concern, not an audio one —
see SceneManager.autoPauseOnBlur.
SoundComponent
Section titled “SoundComponent”SoundComponent binds a sound to an entity. The sound is automatically stopped
when the entity is destroyed.
import { SoundComponent } from "@yagejs/audio";
entity.add(new SoundComponent({ alias: BgMusic.path, channel: "music", loop: true, volume: 0.8, playOnAdd: true, // start playing when the component is added}));Control playback from the component:
const sc = entity.get(SoundComponent);const handle = sc.play();sc.stop();sc.handle; // current SoundHandle or nullThis is useful for ambient sounds attached to world entities — the sound lifecycle is tied to the entity lifecycle.