7.10.5. STRUDEL-05 — Euclidean Rhythms

Euclidean rhythms are produced by distributing k onsets as evenly as possible across n discrete steps. The algorithm — Bjorklund’s, named after the physicist who first wrote it down for a particle-collider problem — turns out to generate many traditional drum patterns from around the world. Toussaint’s 2005 paper The Euclidean Algorithm Generates Traditional Musical Rhythms catalogs the matches: the Cuban tresillo is (3, 8); the Cumbia bell is (5, 8); the West- African (4, 9) shows up in the Aka Pygmy mbira patterns; and so on.

In daStrudel this is one function: euclid(pat, k, n).

7.10.5.1. Part A: bjorklund(k, n) — the underlying rhythm

Before the combinator, here is the raw algorithm. bjorklund(3, 8) returns a length-8 array<bool> with three true entries distributed evenly:

let r <- bjorklund(3, 8)
for (b in r) {
    let glyph = b ? "x" : "."
    print("{glyph} ")
}
// output: x . . x . . x .

The implementation is a tight integer loop — no floating point, no fractional accumulation, just (i * k) / n integer division. This matters when you need to call it for big n values (per-event in a signal, for example) — it allocates one boolean array and is otherwise allocation-free.

7.10.5.2. Part B: euclid(pat, k, n)

euclid first speeds the pattern up by n (so each input cycle now contains n repetitions), then keeps only the haps falling on the k Bjorklund onsets. The classic Cuban tresillo:

let pat <- atom("bd") |> euclid(3, 8)
play(pat, 6.0)

Three kicks over eight evenly-spaced steps — x . . x . . x .. Replace atom("bd") with any Pattern and the same selection rule applies. s("bd sd") would alternate kick / snare across the kept onsets.

7.10.5.3. Part C: A small library of classic rhythms

Most of the patterns Toussaint catalogs are one euclid call:

(k, n)

Pattern

(3, 4)

Three-against-four feel

(3, 8)

Cuban tresillo / Bossa kick

(5, 8)

Cumbia bell, West-African

(7, 8)

Dense, almost-straight

(2, 5)

Persian Khafif-e-ramal

(3, 7)

Bulgarian, ruchenitza

(5, 16)

Bossa Nova clave (rotated form)

Try them by changing the integers:

let pat <- atom("hh") |> euclid(7, 8)
play(pat, 4.0)

7.10.5.4. Part D: Polyrhythm via stack

stack layers patterns simultaneously. Stack two Euclidean patterns with different k and you have a polyrhythm:

var kick <- atom("bd") |> euclid(3, 8)
var hat  <- atom("hh") |> euclid(5, 8)
var layers : array<Pattern>
layers |> emplace <| kick
layers |> emplace <| hat
let pat <- stack(layers)
play(pat, 6.0)

Both layers complete every cycle (n = 8 in both), but the 3 onsets of the kick and the 5 onsets of the hat sit at different grid positions, so the perceived feel is polyrhythmic. Use mismatched n values (e.g. (3, 8) against (2, 5)) and the cycle length becomes lcm(8, 5) = 40 — the patterns realign every 40 steps.

The var (not let) on kick and hat matters: stack takes array<Pattern> and emplace needs to move the lambda (Patterns are non-copyable lambdas) — that requires a mutable binding.

7.10.5.5. Note on the bd(3,8) mini-notation form

In the original Tidal/Strudel mini-notation, "bd(3,8)" is an inline shorthand for euclid. This daStrudel build does not parse parentheses inside mini-notation strings — the parser tokenises ( and ) but does not assemble them into the euclid form. Use the function directly:

s("bd sd") |> euclid(5, 8)
atom("hh") |> euclid(3, 8)

Rotation (Tidal’s bd(3,8,1), the JS Strudel euclidRot) is likewise not exposed as a standalone function in this build. To get a rotated rhythm you can reorder the elements inside the pattern fed to euclid, or chain rev / slow against a control pattern.

7.10.5.6. Where next

You now have the four building-block topics every strudel-style language needs: the data model (01), the mini-notation surface (02–03), the time algebra (04), and Euclidean rhythms (05). Tutorials 06+ introduce combinatorsjux, every, off, superimpose, palindrome, ply, echo, chunk — which turn one-line patterns into full musical phrases.

See also

Full source: tutorials/daStrudel/daStrudel_05_euclidean_rhythms.das

Previous tutorial: STRUDEL-04 — Time Manipulation

Related: Lambdas and Closures — Patterns are non-copyable lambdas (hence var + emplace)

Related: Arraysarray<Pattern> and emplace semantics