Z zvuk
Concept

Mixer

The named bus graph that routes every voice from source to speakers.

TL;DR

The Mixer is your declarative routing model. You name your buses (music, sfx, voice, etc.), give each one a level and behavior, and the engine builds the audio graph for you. The mixer is what separates "I called .play()" from "I shipped a mix."

Mental model

Sources fan in through their default bus. Each bus has its own level, optional FX inserts, optional concurrency cap, and an optional sidechain key. Bus outputs sum at the master, which applies headroom and sends to the destination.

bus graph
SOURCES music.mp3 crowd.webm coin.webm click.webm voice.m4a BUSES music level 0.8 · concurrency 4 fx: reverb sfx level 1.0 · concurrency 32 steal: oldest voice level 1.0 · ducks music fx: ducker(from: voice) MASTER Master headroom -3 dB 🔊 dest sidechain (voice → music)
Three buses, FX inserts, sidechain key (voice → music). Master sums and applies headroom.

API surface

Declare buses up front ts
const engine = createEngine({
  buses: {
    music: { level: 0.8, concurrency: { max: 4 } },
    sfx:   { level: 1.0, concurrency: { max: 32, steal: 'oldest' } },
    voice: { level: 1.0, sidechain: { from: 'music', amount: 0.5, attack: 80, release: 400 } },
  },
  master: { headroom: -3 },
});
Talk to a bus by name ts
engine.bus('music').level = 0.5;
engine.bus('sfx').fadeTo(0, 800);
engine.bus('voice').muted = true;

Live demo

Sliders, mute, real voice counter — live engine, real samples.

engine.state = cold
0 voices

Recipes

Insert an FX chain on a bus

ts ts
import { Compressor, Reverb } from 'zvuk';

const comp = new Compressor(engine.context, { threshold: -18, ratio: 4 });
const reverb = new Reverb(engine.context, { wet: 0.3, decay: { seconds: 1.4 } });

engine.bus('music').addFx(reverb);
engine.bus('sfx').addFx(comp);   // FX run between bus.input and bus.output

Pitfalls

Don't add buses dynamically after createEngine.
Bus topology is declared once. If you need a temporary effect, route into an existing bus and bypass when not in use, or add a fresh FX insert.
Don't write directly to ctx.destination.
Going around the master skips headroom and breaks snapshots. Always route through a declared bus.

Related