Contents

Glide & Interpolation

Pattern fields like @freq and @note jump step-wise from one event to the next — they’re sample-and-hold buffers. glide smooths those jumps over a configurable duration: 100 ms between c4 and c5 takes 100 ms, and 100 ms between c2 and c6 also takes 100 ms. That’s the headline difference from slew, which limits how fast the signal can change per second — so a wide interval takes proportionally longer.

slew vs glide

Aspectslew(target, rate)glide(sig, time, …)
Time meaningunits per secondtotal ramp duration
Interval indep.No — fast slides on big jumpsYes — same time always
ShapeLinearLinear / ease / cosine
Value-spaceLinearLinear or log
StateSlewStateInterpTimeState
Use caseReach for
Note pitch / portamentoglide
Velocity / pattern field smoothingglide
Param slider response (knob lag)glide
Audio-rate signal smoothingslew or LP
Limiting how fast a CV can changeslew
Anti-click ramp on amp envelopesslew or LP

Rule of thumb: if you can answer “how many seconds should the slide take?” reach for glide. If you can only answer “how fast can it change per second?” reach for slew.

Two paths to musical glide

Linear interpolation in Hz sounds non-uniform over wide intervals — a c4→c5 glide rushes through the low frequencies and slows at the top, because equal frequency steps don’t sound equal-sized to the ear. There are two ways to get a pitch-perceptual (log) glide; both produce identical audio:

Path A — space: "log" on glide

n"c2 c6" |> saw(glide(@freq, 0.2, "linear", "log")) |> out(@)

Internally pipes the target through log + scale and pow(2, …). Use this when you only have a frequency signal — e.g. a param, an LFO, or a non-pattern source where MIDI note isn’t naturally available.

Path B — glide the MIDI note, then mtof

n"c2 c6" |> saw(mtof(glide(@note, 0.2))) |> out(@)

@note carries the MIDI note number (linear in semitones), so a plain linear glide is already log-pitch. More transparent, fewer characters. Recommended for pattern sources — but both paths produce identical audio.

Curve shapes

Four shapes ship in v1, selected via the curve argument or the corresponding interp* builtin:

curveBuiltinShape t → shaped_tFeel
"linear"interptEven motion through the slide
"ease_in"interp_ease_inStarts slow; “settles into” the target
"ease_out"interp_ease_out1 − (1 − t)²Starts fast; “springs toward” the target
"cosine"interp_cos½(1 − cos πt)Smooth in and out; classic S-curve

The interp* family is the lower-level primitive. glide is the recommended high-level entry point because it adds value-space conversion and gives you a sensible default (50 ms linear).

Channel width follows the input

glide, every interp* builtin, and slew preserve the channel width of their primary input. Mono in → mono out, so a pattern field like @freq flows straight into a mono parameter slot:

n"c4 c5" |> saw(glide(@freq, 0.1)) |> out(@)

Feed a stereo signal and you get independent per-channel ramps — useful for stereo CV processing:

s = stereo(saw(218), saw(222))
glide(s, 0.05) |> out(@)

See Mono & Stereo Signals for the bigger picture.

Edge cases

  • time = 0 is passthrough — output tracks the target sample-exact, no ramp.
  • Repeated identical notes don’t retrigger a ramp. Change detection uses an exact float compare on the target, and a held note emits the same value every sample. If you want every note onset to glide regardless, future versions may add an explicit trig: option; for now, perturb the source.
  • Mid-ramp retargeting anchors smoothly: the new ramp starts from the currently emitted value, not from the original ramp start. No value jumps when notes change rapidly.
  • NaN / infinite targets hold the last good value. Useful when upstream sources may briefly go invalid.
  • Audio-rate noise as a target retargets every sample. The output behaves like a heavy lowpass — almost certainly not what you wanted. Pre-sample-and-hold the source first (sah with a trigger) or use slew instead.

See also