Routing, Buses & Sends
So far every track has run straight to the master on its own. Routing is how you change that — feeding tracks through a shared effect, grouping them into a submix, or splitting a copy off to a parallel processor. In DAW terms this chapter is the mixer’s routing matrix: output buses, aux sends, and sidechain inputs.
Two operators do the wiring. >> sets a track’s one output destination; send_to
splits off a copy to somewhere else. Here’s a reverb send — drums stay dry on the
master, with a copy feeding a reverb bus:
use "std/instruments" { Sampler, Kit };use "std/effects" { Reverb };
let drums = AudioTrack("drums");drums.load_instrument(Sampler(Kit("CR-78")));drums << [bd sd bd sd];
let reverb_bus = AudioTrack("reverb");reverb_bus.load_effect(Reverb(0.8, 0.3));
drums.send_to(reverb_bus, 0.3); // 30% of the drums also hit the reverb
PLAY;Now the line-by-line.
Routing to Master with >>
Section titled “Routing to Master with >>”Every track has exactly one output. By default it’s master, which is why sound
reaches your speakers without any wiring. The >> operator sets that output
explicitly:
drums >> master;>> replaces the output route — a track has one destination, and routing again
re-points it. You can chain hops, and each hop carries the signal onward at unity
gain:
kick >> drum_bus >> master;Submix Buses
Section titled “Submix Buses”Any AudioTrack can act as a bus: a track with no instrument of its own that
exists to collect and process other tracks. Route several sources into it with >>,
then load an effect on the bus to treat them as one:
use "std/instruments" { Sampler, Kit };use "std/effects" { Lowpass };
let drum_bus = AudioTrack("drum_bus");
let kick = AudioTrack("kick");let snare = AudioTrack("snare");let hats = AudioTrack("hats");
kick.load_instrument(Sampler(Kit("CR-78")));snare.load_instrument(Sampler(Kit("CR-78")));hats.load_instrument(Sampler(Kit("CR-78")));
kick >> drum_bus; // all three feed the bus...snare >> drum_bus;hats >> drum_bus;
drum_bus.load_effect(Lowpass(4000)); // ...and the filter shapes the whole kit
kick << [bd*4];snare << [_ sd _ sd];hats << [hh*8];
PLAY;The bus reaches master automatically, so the filtered drum group lands in the mix
with one fader to ride.
A send routes a copy of a track’s audio somewhere else while the original keeps flowing to its normal destination. This is how you share one reverb or delay across several tracks: build the effect on a return track, then send each source to it.
send_to — parallel
Section titled “send_to — parallel”send_to(destination, amount) adds a copy at the given level. The dry signal still
goes to the master at full volume, so the send is additive — it layers wet on top
of dry:
use "std/instruments" { Sampler, Kit };use "std/effects" { Delay };
let delay_bus = AudioTrack("delay_bus");delay_bus.load_effect(Delay(0.3, 0.5));
let perc = AudioTrack("perc");perc.load_instrument(Sampler(Kit("CR-78")));perc << [rs _ cl _];
perc.send_to(delay_bus, 0.3); // dry perc + 30% into the delay return
PLAY;One track can feed several returns — a short slap and a long throw, each at its own level:
perc.send_to(short_delay, 0.2);perc.send_to(long_delay, 0.4);xsend_to — exclusive
Section titled “xsend_to — exclusive”xsend_to(destination, amount) is the exclusive send: it routes to the
destination while pulling the dry signal back to 1.0 - amount. At 0.7 you get 70%
through the bus and 30% dry, so total energy stays roughly constant — a crossfade from
dry to wet rather than a layer on top.
use "std/instruments" { Sampler, Kit };use "std/effects" { Reverb };
let wet_bus = AudioTrack("wet");wet_bus.load_effect(Reverb(0.9, 0.2));
let drums = AudioTrack("drums");drums.load_instrument(Sampler(Kit("CR-78")));drums << [bd sd bd sd];
drums.xsend_to(wet_bus, 0.7); // 70% wet, 30% dry to master
PLAY;Reach for send_to for parallel effects and reverb/delay returns, and xsend_to when
you want to route a track through a bus without doubling its level. xsend_to can’t
target master directly — its whole job is to divert the dry path.
Moving send amounts
Section titled “Moving send amounts”The amount isn’t limited to a fixed number. Hand send_to a signal or a pattern and
the send level moves on its own — an LFO swelling the delay, or stepped values per
beat:
use "std/instruments" { Sampler, Kit };use "std/effects" { Delay };use "std/signals" { Sine };
let delay_bus = AudioTrack("delay_bus");delay_bus.load_effect(Delay(0.3, 0.5));
let perc = AudioTrack("perc");perc.load_instrument(Sampler(Kit("CR-78")));perc << [rs _ cl _];
perc.send_to(delay_bus, Sine(0.5).range(0.0, 0.6)); // send swells with a slow LFO
PLAY;To automate a send you’ve already created, grab its level with send_level(dest) and
drive it like any other parameter:
drums.send_to(reverb_bus, 0.3);drums.send_level(reverb_bus) << automation(#[0, 0.3], #[8, 0.8]);Signals and automation are the subject of the next chapter.
Sidechains
Section titled “Sidechains”Some effects have a second audio input — a sidechain. A classic use is ducking:
a compressor on the drums that clamps down whenever the bass plays. Feed one track
into an effect’s named input port with connect_input:
use "std/instruments" { Sampler, Kit };
dsp effect Ducker { input main: stereo; input sidechain: mono; output: stereo;
param amount: 0.8 range(0, 1); state env: 0.0;
fn process(main_l, main_r, sc) -> (out_l, out_r) { let level = __native("abs", sc); env = env + (level - env) * 0.01; let gain = 1.0 - amount * env; return (main_l * gain, main_r * gain); }}
let drums = AudioTrack("drums");drums.load_instrument(Sampler(Kit("CR-78")));drums << [bd sd bd sd];
let bass = AudioTrack("bass");bass.load_instrument(Sampler(Kit("CR-78")));bass << [bd _ bd _];
let duck = Ducker();drums.load_effect(duck);duck.connect_input("sidechain", bass); // bass triggers ducking on the drums
PLAY;The port name matches a declared input on a dsp effect. For an external plugin it’s
the plugin’s own sidechain bus name — often "Sidechain" or "Aux Input". Unlike a
send, connect_input is read-only: it feeds the source into the effect’s detector
without mixing that audio into the output. A mono port auto-downmixes a stereo source
to (L + R) / 2, and Resonon orders the graph for you, so the source just can’t form a
routing cycle with the destination.
Inspecting Routing
Section titled “Inspecting Routing”When the wiring gets dense, routing() prints the whole graph; pass one or more tracks
to narrow it, or call .routing() on a track:
routing(); // every trackrouting(drums, bass); // just these twodrums.routing(); // method formTo start over, reset_routing() clears a track’s routes and send modulations back to
the default master route. It’s chainable, so you can clear and re-wire in one line:
drums.reset_routing().send_to(reverb_bus, 0.5);Next Steps
Section titled “Next Steps”You can route tracks to buses, split parallel sends, and feed sidechains. Next, make those send amounts and effect parameters move on their own.
- Signals & Automation — drive parameters and send levels with LFOs, ramps, and breakpoint envelopes
- Plugins — load external VST3 and CLAP effects and instruments
- The Audio Graph — how routing, buses, and the master fit into the whole signal graph