Skip to content

Polymetric & Euclidean

This chapter covers two ways to generate rhythm rather than write it out by hand. Polymetric patterns let groups of different lengths cycle at different rates, phasing against each other like Steve Reich loops. Euclidean rhythms distribute hits as evenly as possible across steps, producing many of the world’s classic rhythmic cells from two numbers.

Curly braces {...} give each element one step. Without a subdivision modifier, one step equals one full cycle:

let slow = {C4 D4 E4}; // One note per cycle — spans 3 cycles
let fast = [C4 D4 E4]; // All three notes in one cycle

%N packs N steps into each cycle. The resulting speed of a group is governed by one ratio:

speed_factor = N / L // N = steps per cycle, L = element count
PatternNLFactorEffectEvents/cycle
{C4 D4 E4}131/3Slow: spans 3 cycles1
{C4 D4 E4}%3331Same as [C4 D4 E4]3
{C4 D4 E4}%6632Double speed6

When N and L don’t divide evenly, the content wraps — and this is where it gets musical. {C4 D4}%3 has factor 3/2: three onsets per cycle at 0, 1/3, 2/3, but only two notes of content. Cycle 0 plays C4 D4 C4; cycle 1 picks up where it left off with D4 C4 D4. The content shifts by one position each cycle, phasing against the meter until it realigns after L cycles.

Comma-separated groups inside {...} are the heart of polymetric writing. All groups land on the same onset grid, but each group’s content cycles at its own rate:

let poly = {C4 E4 G4, F4 A4};

Without an explicit %N, N defaults to the first group’s length — here 3. Each group is sped up by its own N/L factor so every voice produces exactly N onsets per cycle:

TimeVoice 1 (L=3, factor 1)Voice 2 (L=2, factor 3/2)
0C4F4
1/3E4A4
2/3G4F4 — content wraps

Voice 1 repeats identically every cycle. Voice 2’s two-note content takes only 2/3 of a cycle per pass, so it drifts one position per cycle and realigns with voice 1 every 2 cycles. That slow churn of two loops sliding past each other is the polymetric sound.

An explicit %N sets the grid density for all groups at once:

let dense = {C4 D4 E4, F4 G4}%6; // 6 onsets/cycle; factors 2 and 3

Both layer voices, but they produce fundamentally different structures:

Polymetric {a b c, d e}Parallel [a b c, d e]
Voice 1 onsets0, 1/3, 2/30, 1/3, 2/3
Voice 2 onsets0, 1/3, 2/30, 1/2
GridShared (N-step)Independent per voice
EffectContent phasingOnset conflict
use "std/instruments" { Sampler, Kit };
let drums = AudioTrack("drums");
drums.load_instrument(Sampler(Kit("TR-808")));
// 4-step grid; the 3-hat group phases against the kick-snare
drums << {bd sd bd sd, ch ch oh};
PLAY;

Polymetric groups compose with everything else — modifiers and nested structures work inside {}, and a {...}%N group can sit inside a sequence as a single element:

let mixed = [{C4 D4 E4}%3 G4]; // Polymetric fills the first half
let rests = {_ C4 D4}%3; // Rests take steps too

Give the euclidean algorithm a number of hits and a number of steps, and it spaces the hits as evenly as possible. Three hits over eight steps:

x . . x . . x .

That’s the Cuban tresillo — the 3+3+2 cell underneath son, reggaeton, EDM, and half of pop music. Many traditional rhythms fall out of this one algorithm:

PatternHit patternName
(3,4)x x x .Cumbia
(3,8)x . . x . . x .Tresillo
(5,8)x . x x . x x .Cinquillo
(5,16)x . . x . . x . . x . . x . . .Bossa nova
(7,16)x . . x . x . x . . x . x . x .Samba

Attach (hits, steps) to any element. The element sounds on the hit positions and rests on the others, all within its own slot:

let tresillo = [bd(3,8)]; // Tresillo kick filling the cycle
let mixed = [C4(3,8) E4 G4]; // Euclidean C4, plain E4 and G4

A third parameter rotates the hit pattern left by that many steps, wrapping around — same spacing, different downbeat:

Rotation(3,8) becomesOnsets
0x . . x . . x .0, 3, 6
2. x . . x . x .1, 4, 6
3x . . x . x . .0, 3, 5

Rotation wraps modulo the step count, so rotation 10 on 8 steps equals rotation 2. Layering rotations of the same cell creates interlocking parts:

use "std/instruments" { Sampler, Kit };
let drums = AudioTrack("drums");
drums.load_instrument(Sampler(Kit("CR-78")));
// Three tresillo rotations interlocking
drums << [bd(3,8,0), rs(3,8,2), hh(3,8,5)];
PLAY;

The inline notation has function and method counterparts that work on whole patterns:

let a = [C4(3,8)]; // Inline — single element inside []
let b = euclid(3, 8, [C4]); // Function — any pattern as source
let c = [C4].euclid(3, 8); // Method — chains with other methods

All three produce identical output; each takes an optional rotation as its last argument. Use inline inside patterns, the method when chaining transformations, the function for explicitness.

With a multi-note source, the notes are dealt onto the hit positions in order — and wrap if there are more hits than notes:

let dealt = euclid(3, 8, [C4 E4 G4]); // One note per hit
let wrapped = euclid(5, 8, [C4 E4]); // C4 E4 C4 E4 C4

Different hit counts over the same step count give voices that interlock without ever quite lining up:

use "std/instruments" { Sampler, Kit };
let drums = AudioTrack("drums");
drums.load_instrument(Sampler(Kit("TR-808")));
// Sparse, hypnotic — minimal techno
drums << [bd(3,16), ch(7,16), sd(2,16,4)];
PLAY;

And nothing says euclidean rhythms are just for drums:

let melody = MidiTrack(1);
melody << [C4(3,8) E4(5,8) G4(7,8)];
PLAY;

You can now generate rhythm three ways: by hand, by phasing meters, and by algorithm. Next, transform any of them in code.