builtins

Polyphony

Voice allocation for patterns. poly() runs an instrument function per voice and sums the outputs. mono() and legato() are single-voice variants with differen…

Voice allocation for patterns. poly() runs an instrument function per voice and sums the outputs. mono() and legato() are single-voice variants with different retrigger behavior. spread() distributes an array across N voices for unison stacks.

poly

Polyphonic Voice Manager - Allocates voices for a pattern and runs an instrument function per voice.

ParamTypeDefaultDescription
inputpattern-Pattern or chord(...) producing events (notes or chords)
instrumentfunction-A function (freq, gate, vel) -> signal run per voice
voicesnumber64Voice count (1-128, must be a literal)

poly() reads pattern events at runtime and assigns each note to its own voice slot. The instrument function receives per-voice frequency, gate, and velocity, and the outputs of all active voices are summed. When the same note appears in consecutive events, the voice slot is reused (preserving phase continuity); when all voices are busy, the oldest is stolen.

The instrument must be a 3-parameter function; the names don’t matter but the order is fixed: (freq, gate, vel).

// Polyphonic chord progression with the default 64 voices
stab = (freq, gate, vel) ->
    saw(freq) * ar(gate, 0.05, 0.4) * vel
    |> lp(@, 1100)

chord("C Em Am G") |> poly(@, stab) |> out(@)
// Lower voice count if you want predictable stealing
pat("c4 e4 g4 b4") |> poly(@, (f, g, v) -> osc("sin", f) * v, 8) |> out(@)

mono

Monophonic Voice Manager - Single-voice manager with retrigger on every new note.

ParamTypeDefaultDescription
inputpattern-Pattern producing events
instrumentfunction-A function (freq, gate, vel) -> signal

mono() is poly() with one voice and last-note priority. Every new note retriggers the gate so envelopes restart cleanly, the classic hardware-mono behavior.

mono(stereo_signal) is a different builtin (stereo-to-mono downmix). The compiler routes based on argument type: a function instrument gets the voice manager; a stereo signal gets the downmix.

// Mono lead with retrigger on every note
pat("c4 e4 g4 c5") |> mono((f, g, v) -> saw(f) * adsr(g, 0.01, 0.1, 0.6, 0.3)) |> out(@)

legato

Legato Voice Manager - Single-voice with no retrigger between connected notes.

ParamTypeDefaultDescription
inputpattern-Pattern producing events
instrumentfunction-A function (freq, gate, vel) -> signal

Like mono(), but the gate stays high while notes overlap, so envelopes don’t restart on every note. Frequency and velocity update but the AR/ADSR keeps decaying through the phrase. Best for legato leads and bass lines.

// Smooth bassline, gate stays high across notes
pat("c2 e2 g2 c3") |> legato((f, g, v) -> saw(f) * adsr(g, 0.01, 0.2, 0.8, 0.4)) |> out(@)

spread

Spread - Distribute an array across N voices evenly.

ParamTypeDefaultDescription
nnumber-Number of voices to spread across
sourcearray-Source array of values

spread() is a compile-time helper that takes an array and distributes its values across n slots, repeating or reducing as needed. Useful for unison voices and detuned stacks.

// Detuned saw stack, 5 oscillators across the array
osc("saw", spread(5, [220, 220.7, 219.3, 221.4, 218.6])) |> out(@)

voice

A voice is one independent instance of the instrument function. poly() runs N voices in parallel; mono() and legato() use one. Voice slots are pre-allocated at compile time; runtime allocation is just a slot lookup.

voices

The voice count parameter to poly() (default 64). It must be a literal so the compiler can statically allocate. Lower voice counts give predictable voice stealing; higher counts handle complex patterns without dropouts.

Related: sequencing, chord, pat