Skip to content

Routing

use "std/instruments" { Sampler, Kit };
let drums = AudioTrack("drums");
drums.load_instrument(Sampler(Kit("cr78")));
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);
PLAY;

The >> operator routes audio from one node to another:

drums >> master;

master is the built-in constant for the main audio output. All audio must eventually route to master to be heard.

Tracks without explicit >> routing go to master automatically:

let hats = AudioTrack("hats");
hats.load_instrument(Sampler(Kit("cr78")));
hats << [hh*8];
// No >> routing needed -- goes to master automatically

Chain multiple nodes with >>. Each hop is unity gain (1.0):

kick >> drum_bus >> master;

Use .send(amount) to create a Send value for >> chains. The amount accepts a Number, Signal, or Pattern:

drums >> reverb_bus.send(0.3) >> master;
drums >> delay_bus.send(Sine(4).range(0.1, 0.5)) >> master;

Routes a copy of audio to another track. The dry signal still flows to its normal destination at full level:

let delay_bus = AudioTrack("delay_bus");
delay_bus.load_effect(Delay(0.3, 0.5));
melody.send_to(delay_bus, 0.3); // 30% to delay return

Multiple sends from one track:

perc.send_to(short_delay, 0.2);
perc.send_to(long_delay, 0.4);

Exclusive send — routes audio to a destination while reducing the dry signal to master. Useful for routing entirely through a bus:

drums.xsend_to(drum_bus, 1.0);
Behaviorsend_toxsend_to
Dry to masterAlways 1.01.0 - amount
Total energyIncreasesPreserved
Can target masterYesNo
Use caseParallel FX, reverb sendsFull submix routing

Send amounts accept Numbers, Signals, or Patterns. Signal and pattern values update at block rate:

perc.send_to(short_delay, Sine(4).range(0.1, 0.5));
perc.send_to(long_delay, [0.1 0.3 0.5 0.3]);

Any AudioTrack can serve as a submix bus. Route multiple tracks with >>:

let drum_bus = AudioTrack("drum_bus");
kicks >> drum_bus;
snares >> drum_bus;
hats >> drum_bus;
drum_bus.load_effect(Delay(0.1, 0.2));

Effects with sidechain inputs can receive audio from other tracks via .connect_input(). This works with both custom DSP effects and external VST3/CLAP plugins that have sidechain buses.

dsp effect Compressor {
input main: stereo;
input sidechain: mono;
output: stereo;
param threshold: 0.5 range(0, 1);
state env: 0.0;
fn process(main_l, main_r, sc) -> (out_l, out_r) {
let level = abs(sc);
if level > threshold {
env = env + (level - env) * 0.01;
} else {
env = env + (level - env) * 0.001;
}
let gain = 1.0 / (1.0 + env);
return (main_l * gain, main_r * gain);
}
}
let drums = AudioTrack("drums");
let bass = AudioTrack("bass");
let comp = Compressor();
drums.load_effect(comp);
comp.connect_input("sidechain", bass); // Bass triggers compression on drums

For external plugins, the sidechain port name comes from the plugin itself (typically "Sidechain" or "Aux Input"):

let drums = AudioTrack("drums");
let kick = AudioTrack("kick");
let comp = Effect("My Compressor");
drums.load_effect(comp);
comp.connect_input("Sidechain", kick); // Kick triggers sidechain on plugin compressor
Featuresend_to / >>connect_input
PurposeMix audio into a destination trackFeed audio into an effect’s sidechain port
TargetTrack or masterNamed port on an effect
Audio mixingAdditive (summed)Read-only (doesn’t modify source)
OrderingAutomatic via topological sortAutomatic via topological sort
  • The source track must not create a routing cycle with the destination
  • Mono sidechain ports auto-downmix stereo sources to (L + R) / 2
  • For DSP effects, the port name must match a declared input (e.g., "sidechain")
  • For external plugins, the port name must match the plugin’s sidechain bus name (e.g., "Sidechain")
  • .connect_input() returns the effect for chaining

Print the full routing graph for all tracks:

routing();

Print routing info for specific tracks. Accepts one or more track arguments:

routing(drums); // Single track
routing(drums, bass); // Multiple tracks

Call .routing() on a track to print its routing info:

drums.routing();

Clears all routes and send modulations, restoring the default master route at 1.0. Chainable:

drums.reset_routing();
drums.reset_routing().send_to(reverb_bus, 0.5); // Clear and re-route

Returns a SendParam reference for an existing send:

drums.send_to(reverb_bus, 0.3);
let rev_send = drums.send_level(reverb_bus);
rev_send << automation(#[0, 0.3], #[8, 0.8]);

Requires an existing send route to the destination.

let delay_bus = AudioTrack("delay_bus");
delay_bus.load_effect(Delay(0.3, 0.4));
let clap = AudioTrack("clap");
clap.load_instrument(Sampler(Kit("cr78")));
clap << [_ _ cp _];
clap.send_to(delay_bus, 0.3);
let cowbell = AudioTrack("cowbell");
cowbell.load_instrument(Sampler(Kit("cr78")));
cowbell << [_ cb _ cb];
cowbell.send_to(delay_bus, 0.3);
let parallel_bus = AudioTrack("parallel");
parallel_bus.load_effect(Distortion(0.8, 0.6));
let drums = AudioTrack("drums");
drums.load_instrument(Sampler(Kit("cr78")));
drums << [bd sd bd sd];
drums.send_to(parallel_bus, 0.3);
// Result: dry drums + 30% distorted drums blended

Use xsend_to to crossfade between dry and wet signal paths:

let wet_bus = AudioTrack("wet");
wet_bus.load_effect(Reverb(0.9, 0.2));
let drums = AudioTrack("drums");
drums.load_instrument(Sampler(Kit("cr78")));
drums << [bd sd bd sd];
// At 0.7: 70% wet, 30% dry to master
drums.xsend_to(wet_bus, 0.7);

Automate send levels over time with signals or automation breakpoints:

let delay_bus = AudioTrack("delay_bus");
delay_bus.load_effect(Delay(0.3, 0.5));
let drums = AudioTrack("drums");
drums.load_instrument(Sampler(Kit("cr78")));
drums << [bd sd bd sd];
// LFO-modulated send amount
drums.send_to(delay_bus, Sine(0.5).range(0.0, 0.6));