8.12.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).
8.12.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.
8.12.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.
8.12.5.3. Part C: A small library of classic rhythms
Most of the patterns Toussaint catalogs are one euclid call:
|
Pattern |
|---|---|
|
Three-against-four feel |
|
Cuban tresillo / Bossa kick |
|
Cumbia bell, West-African |
|
Dense, almost-straight |
|
Persian Khafif-e-ramal |
|
Bulgarian, ruchenitza |
|
Bossa Nova clave (rotated form) |
Try them by changing the integers:
let pat <- atom("hh") |> euclid(7, 8)
play(pat, 4.0)
8.12.5.4. Part D: Polyrhythm via stack
stack layers patterns simultaneously. Stack two Euclidean patterns
with different k and you have a polyrhythm:
let pat <- stack([
atom("bd") |> euclid(3, 8),
atom("hh") |> euclid(5, 8)
])
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.
8.12.5.5. Part E: euclidRot(pat, k, n, rot) — rotate the onsets
euclidRot is euclid with the Bjorklund pattern rotated left by
rot steps. The density is unchanged — still k onsets over n
steps — but the accents land on a different set of grid positions, which
shifts the feel without adding or removing hits. euclid(3, 8) hits
steps 0, 3, 6; euclidRot(3, 8, 2) shifts that window two steps:
let kick <- atom("bd") |> euclid(3, 8)
play(kick, 4.0)
let hat <- atom("hh") |> euclidRot(5, 8, 2)
play(hat, 4.0)
Query one cycle by hand to see the onset positions move:
var rotd <- atom("bd") |> euclidRot(3, 8, 2)
var haps <- invoke(rotd, TimeSpan(start = 0.0lf, stop = 1.0lf))
for (h in haps) {
print("{h.whole.start} ")
}
delete haps
8.12.5.6. 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 and "bd(3,8,2)" adds rotation. daStrudel
supports both — tutorial 03
(Section 5) demonstrates s("bd(3,8)") and s("bd(3,8,2)"), which
the parser expands directly to the euclid() / euclidRot()
function forms shown above. The function forms are the explicit
equivalents and read well in a pipe:
s("bd sd") |> euclid(5, 8) // same as s("bd sd(5,8)")
atom("hh") |> euclidRot(5, 8, 2) // same as s("hh(5,8,2)")
8.12.5.7. 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 combinators — jux, 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: Arrays — array<Pattern> and emplace semantics