The audio module has two classes:
SoundManager — loads audio files and owns the AudioContext. One instance per game.Sound — a single loaded audio clip. Supports play, pause, resume, stop, and volume.The AudioContext is unlocked automatically on the first user interaction (click, tap, or keypress),
complying with browser autoplay policies.
import { SoundManager } from "craters";
const audio = new SoundManager();
load() fetches the file, decodes it via the Web Audio API, and resolves with a Sound instance.
const jump = await audio.load("sounds/jump.mp3") as Sound;
const music = await audio.load("sounds/theme.ogg") as Sound;
Load all sounds upfront (e.g. in a loading screen) so they are ready when needed.
// Play once
jump.play();
// Loop (e.g. background music)
music.play(true);
// Play once, then run a callback on completion
jump.play(false, () => console.log("jump sound finished"));
Calling play() while the sound is already playing stops the previous instance first, so
rapid repeated calls (e.g. footsteps) work correctly without overlapping.
music.stop(); // stop and release the audio nodes
music.pause(); // suspend the AudioContext (affects all sounds)
music.resume(); // resume the AudioContext
Note:
pause()andresume()suspend the entireAudioContext, not just this individual sound. Preferstop()for one-shot sounds and use pause/resume for a global game pause.
music.setVolume(0.5); // 50% volume (range 0.0 – 1.0)
const vol = music.getVolume(); // 0.5
Volume changes are applied immediately, even while the sound is playing.
import { SoundManager, RenderLoop, Input } from "craters";
const audio = new SoundManager();
const input = new Input();
input.bind(Input.KEY.SPACEBAR, "JUMP");
const jumpSfx = await audio.load("jump.mp3") as Sound;
const music = await audio.load("music.ogg") as Sound;
music.setVolume(0.4);
music.play(true); // start looping music
new RenderLoop((loop) => {
if (input.isPressed("JUMP") === 2) {
jumpSfx.play();
}
});
Browsers block audio from playing before the first user gesture. SoundManager handles
this by listening for touchstart, mousedown, and keydown and calling
audioContext.resume() on the first event. No extra setup is required.
If you need to check readiness manually:
audio.audioContext.state; // "suspended" | "running" | "closed"
SoundManagernew SoundManager() — creates AudioContext and registers unlock listenersload(resource: string): Promise<Sound> — fetches and decodes an audio fileaudioContext: AudioContext — the underlying Web Audio contextSoundplay(loop?, onEnded?) — play; loop defaults to falsestop() — stop playback and release audio nodespause() — suspend the AudioContext (global pause)resume() — resume the AudioContextsetVolume(value: number) — set volume 0.0 – 1.0getVolume(): number — current volume