Skip to content

Automation

Automation is a pure curve over beat-time. You attach one to a track parameter with track.automate(param, curve) inside a section body; the runtime samples the curve and compiles it to parameter ramps (filter sweeps, gain fades, stereo drift) at schedule time.

import { value, ramp, sweep, sine, bars } from "@barline/core";

The automatable parameters are cutoff, resonance, gain, pan, and pitch (the set is extensible by packages via declaration merging). cutoff and resonance lazily create a dedicated filter on the track the first time they’re automated, so they’re audible.

Generated devices also expose stable parameter and macro paths:

bass.automate("instrument.filterA", sine(900, 300, bars(4)));
bass.automate("instrument.macro.color", sweep(0.2, 0.9, bars(8)));
drums.automate("effect.1.drive", ramp(0.1, 0.7, bars(2)));
drums.automate("effect.1.macro.dirt", sine(0.5, 0.2, bars(1)));

Macro curves use normalized 0..1 positions and honor the macro’s published polarity and response curve, including logarithmic frequency controls. Direct parameter paths use their published units (Hz, dB, seconds, and so on). The runtime clamps both forms to the generated safe range.

Effect indices are source-order positions in the track’s static chain. The runtime resolves paths against the generated device manifest, applies log ramps to log-scaled controls, and warns without changing the graph when a target is unknown, hidden, or not automatable.

kick.automate("gain", value(0.8)); // hold a fixed value

Interpolates linearly from from to to across over beats, then holds.

kick.automate("gain", ramp(0.7, 1, bars(16))); // fade the kick up over the section
hats.automate("gain", ramp(0.4, 1, bars(16))); // bring the hats in
rumble.automate("gain", ramp(0.8, 0.15, bars(16))); // wind the bass down in the outro

sweep(from, to, over, curve = "exp") — a filter sweep

Section titled “sweep(from, to, over, curve = "exp") — a filter sweep”

Like ramp, but exponential by default — the natural shape for a frequency sweep, where the ear hears ratios, not differences. Pass "lin" for linear, or let it fall back to linear automatically across a zero or sign change.

rumble.automate("cutoff", sweep(250, 2500, bars(16), "exp")); // the classic opening filter
rumble.automate("cutoff", sweep(400, 6000, bars(16))); // exp is the default

Oscillates around center by ±amp, one full cycle every period beats. Great for stereo drift and slow filter wobble.

ride.automate("pan", sine(0, 0.5, bars(4))); // slow stereo drift
acid.automate("cutoff", sine(1400, 900, bars(8))); // a wandering filter

A curve built from one of these four constructors with plain numeric literals (or bars(n) / beats(n) wrappers) is editable from the Clip detail panel — each argument shows as a scrub/select control that writes back into the literal in your code. A computed curve, or two constructor calls on one line, locks the row with a reason instead. Custom curve objects (any object with an at(pos): number method) work too, but carry no call-site metadata, so they always render read-only.