Skip to content

Effects

An effect processes a track’s audio after the instrument — an echo, a filter, a reverb. In DAW terms these are insert effects: they sit in the track’s signal chain, each one feeding the next, before the result reaches the master.

The built-in effects live in the std/effects module. Import the ones you need:

use "std/effects" { Delay, Lowpass, Reverb };

Here’s a beat with an echo on it:

use "std/instruments" { Sampler, Kit };
use "std/effects" { Delay };
let drums = AudioTrack("drums");
drums.load_instrument(Sampler(Kit("CR-78")));
drums << [bd sd bd sd];
drums.load_effect(Delay(0.25, 0.5));
PLAY;

Delay(0.25, 0.5) builds a delay effect; load_effect adds it to the track’s chain. The drums now repeat with a quarter-second echo.

load_effect appends to the chain, and order matters — each effect processes what the one before it produced. Add a delay then a lowpass, and the filter smooths the echoes; swap the order and you’d echo an already-filtered signal:

use "std/instruments" { Sampler, Kit };
use "std/effects" { Delay, Lowpass };
let drums = AudioTrack("drums");
drums.load_instrument(Sampler(Kit("CR-78")));
drums << [bd sd bd sd];
drums.load_effect(Delay(0.25, 0.5)); // signal -> delay -> lowpass -> out
drums.load_effect(Lowpass(800));
PLAY;

To load several at once, hand load_effects an array (Resonon arrays use #[...]):

use "std/instruments" { Sampler, Kit };
use "std/effects" { Highpass, Delay };
let hats = AudioTrack("hats");
hats.load_instrument(Sampler(Kit("CR-78")));
hats << [hh*8];
hats.load_effects(#[Highpass(2000), Delay(0.125, 0.3)]);
PLAY;

show(track) prints the full chain in order if you lose track of what’s loaded.

Delay(time, feedback) is an echo. time is the delay in seconds; feedback (0–1) is how much of the output loops back in, so higher values mean more, longer repeats. Delay(0.25, 0.5) echoes every quarter second at half feedback.

The filters all take a cutoff frequency in Hz, plus an optional resonance (Q) as a second argument. Resonance defaults to 0.707 — a flat, neutral response — and rising values narrow and emphasise the cutoff into a sharper peak.

  • Lowpass(cutoff) keeps lows, cuts highs — warmer, darker.
  • Highpass(cutoff) keeps highs, cuts lows — thinner, airier.
  • Bandpass(cutoff) keeps a band around the cutoff, cutting either side.
  • Notch(cutoff) is the inverse — removes a band, e.g. Notch(60) to kill mains hum.
  • Allpass(cutoff, q) passes everything but shifts phase — a phaser building block.
  • Peak(cutoff) is a bell: a boost or emphasis around the cutoff.
use "std/instruments" { Sampler, Kit };
use "std/effects" { Lowpass, Highpass };
let drums = AudioTrack("drums");
drums.load_instrument(Sampler(Kit("CR-78")));
drums << [bd sd bd sd];
drums.load_effect(Lowpass(800, 3.0)); // resonant lowpass at 800 Hz, Q 3.0
drums.load_effect(Highpass(120)); // clear out the sub-rumble
PLAY;

Two reverbs add space. Reverb(size, damping) is a room/hall: size (0–1) sets how large the space feels, damping (0–1) how quickly the highs decay. The PlateReverb models a lusher, more diffuse plate with its own size and tone controls.

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];
drums.load_effect(Reverb(0.8, 0.3)); // big room, gentle damping
PLAY;

For wet/dry control — keeping the dry signal and sending only a copy through the reverb — use a send to a dedicated effect track, covered in Routing, Buses & Sends.

Overdrive(amount) and Distortion(amount) add harmonics and grit, from gentle warmth to hard clipping as amount rises:

use "std/instruments" { Sampler, Kit };
use "std/effects" { Overdrive };
let drums = AudioTrack("drums");
drums.load_instrument(Sampler(Kit("CR-78")));
drums << [bd sd bd sd];
drums.load_effect(Overdrive(0.5));
PLAY;

You’ll often want to adjust an effect after creating it. Keep a reference to it, then set parameters by name. There are two ways, and they do the same thing for a static value:

use "std/effects" { Delay };
let echo = Delay(0.25, 0.5);
echo.param_set("Time", 0.4); // method form
echo.param("Time") << 0.15; // operator form

param_set returns the effect, so calls chain. The << form is the one to remember, though, because it accepts more than a fixed number: feed it a moving signal and the parameter sweeps over time.

echo.param("Feedback") << Sine(0.2).range(0.2, 0.7); // feedback drifts

That’s modulation — the bridge from static effects to living, moving ones — and it’s the subject of the next-but-one chapter.

You can load effects, chain them in order, and dial their parameters. Next, wire tracks together, then make those parameters move.