Guide
Building your mix
The shape of a five-bus production mix. Pick the buses, declare them at construction, route everything through them.
Pick your buses up front
Most game-style apps land on the same five-or-six buses. Don't over-think this on day one — you can always rename later.
- music — looped background tracks; one voice at a time, occasionally crossfaded.
- sfx — gameplay one-shots, dense, polyphonic.
- voice — VO, dialogue, alerts; usually exclusive (one at a time).
- ambience — looped environmental textures.
- ui — clicks, hovers, modal opens.
Bare-bones declaration
createEngine({
buses: {
music: { level: 0.7 },
sfx: { level: 1.0, concurrency: { max: 32 } },
voice: { level: 1.0, concurrency: { max: 1, steal: 'none' } },
ambience: { level: 0.4 },
ui: { level: 0.6 },
},
master: { headroom: -3 },
}); What it looks like
Add FX inserts
import { Compressor, Reverb } from 'zvuk';
engine.bus('music').addFx(new Reverb(engine.context, { wet: 0.2 }));
engine.bus('voice').addFx(new Compressor(engine.context, {
threshold: -18, ratio: 4, attack: 0.005, release: 0.15,
})); Reverb on music is for atmosphere. Compressor on voice levels out dialogue without losing presence. Don't put reverb on SFX — individual hits sound smeared.
Master headroom
// Master gets -3 dB of headroom. Lifts the ceiling for transient SFX
// without clipping. If you hear pumping or clicks, drop to -6.
createEngine({ master: { headroom: -3 } }); Live mixer
engine.state = cold
0 voices
What about ducking?
Sidechain a target bus from a source bus — when the source is loud, the target dips. See the ducking guide.
Pitfalls
Don't declare buses you don't use.
Each bus is two GainNodes. Negligible cost individually, but ten unused
buses is a yard sale.
Don't expose the master gain to UI.
Master is for mix headroom and mute (e.g. tab visibility). User volume
belongs on a bus or a parameter — most users want a music slider, not
a global knob.