Audio · interface

AudioGraphOptions

The audio graph: layered stems into a master filter, with parallel sends.

Explained in Your first game.

interface AudioGraphOptions
import type { AudioGraphOptions } from '@driftengine/audio';

In depth

stems[] → stemGain[] → musicGain ─┬→ lift(highpass→duck) → dry ─┬→ bus → lowpass → dest └→ slam(shelf→drive→clip) ──┘ one-shots → level ─────→ effectsGain ───────────────────┘ │ │ lift ─────────────────────────┬──┼→ convolver ────→ dest ├──┼→ longConvolver → dest └──┼→ feedbackDelay → dest

Music and effects have their own gain stage because players expect to turn them down independently — muting the score while keeping the game audible is the single most-used audio setting there is.

The sends are fed from the music alone. They hung off the shared bus first, which put reverb and delay on every sound the game made when only the score should carry them. A footstep with a six-second tail on it is not atmosphere, it is a bug, and the effects that carry the world's own sound need to stay dry and immediate to be legible. The master filter still applies to everything, which is deliberate: going under water muffles the world, not only the score.

Game code expresses musical intent — "louder, faster, brighter" — and never builds nodes. That boundary is what stops mixing decisions from ending up spread across gameplay code where nobody can find them.

Everything here degrades to silence rather than to a crash. A browser that blocks audio, an unsupported node type, a context that never resumes: all of them leave a playable game, because sound is not what the game is for.

Properties

NameTypeDescription
stemCountnumberHow many simultaneous music layers to allocate.
contextreadonlyoptionalBaseAudioContextBuild on this context instead of creating a live one.
More

For rendering a mix rather than hearing it: hand in an OfflineAudioContext and every node below is built on it, so startRendering produces the same mix the speakers would have made. Additive — omitting it is exactly the previous behaviour.

levelsreadonlyoptionalMixLevelsWhere the music and effects stages start. Unity for both when omitted.
More

The reason this exists rather than a setMusicVolume call after construction: a graph built to reproduce a mix has to be at that mix's levels from its own zero, and every parameter move here is a setTargetAtTime — which approaches its target over RAMP and so would open a rendered clip with a third of a second of glide down from unity to whatever the player actually chose. A level that does not change for the whole render is not a move; it is where the parameter starts.

It also cannot be scheduled on the wrong clock, which the other shape can: offline there is no "now", so a level set imperatively lands wherever the render happens to have got to. See at.

fetchImplreadonlyoptionalFetchLikeFetch every registered sound goes through, instead of the global fetch.
More

A game may need to gate its own asset requests — a signed URL, a token header — without the engine knowing why. Additive — omitting it is exactly the previous behaviour, and SoundRegistry already degrades any fetch failure to its synth fallback, so a caller's custom fetch can fail as loudly or as quietly as it likes without a new error path opening up here.

onUnavailablereadonlyoptional(reason: string) => voidTold why this browser gave no audio at all, when create returns null.
More

Null is deliberately coarse — it means "there is nothing to wake", and a caller reporting it learns only that somebody, somewhere, heard nothing. A game with telemetry needs the other half: a missing constructor and a context that threw are different bugs with different fixes, and the field that said neither was soundtrack_init_null. Never called when a graph is returned; a suspended context is not unavailable.

randomreadonlyoptional() => numberWhere randomness comes from, for the noise the reverb impulses are made of.
More

Defaults to Math.random, which is what a game wants: a hall built from a fresh sequence every session is a hall, and one built from a fixed one is a hall with a repeating texture in its tail. Supplied only where a render has to be reproducible sample for sample — a check script comparing two mixes cannot do that while every convolver is different.

Additive: omitting it is exactly the previous behaviour.