Chapter 4 · Your First Generators: Euclidean and Spread Rhythms

In Chapter 3 you placed every note by hand; here you hand the rhythm-making over to generators — single numbers that bloom into whole patterns — and learn to layer, ratchet, swing, and chain them.

So far a rhythm has been a list you typed out: [0, 4, 8, 12] for the kick, range(16) for the hats. That’s perfect when you know exactly what you want. But often you don’t want exact steps — you want a feel: “spread five hits evenly across the bar,” “make the hats busier as the section builds.” A generator lets you say that with one number. You give it a count and it works out the steps, distributing them as evenly as the maths allows. The same idea — describe the density, let Subsequence fill the grid — runs through this whole chapter.

4.1 Euclidean rhythms from one number

The Euclidean rhythm is the workhorse generator. You give it a number of pulses — how many hits to place — and it spreads them as evenly as possible across the bar’s grid. That single idea sits under a startling share of the world’s drum patterns: spread a handful of hits as evenly as a grid allows and you keep landing on rhythms that traditions everywhere already use — West African bell lines, the Cuban tresillo, the four-on-the-floor kick. They are all “N hits, spread evenly.”

See also

Origins. Euclid wrote this algorithm down around 300 BC; in 2003 it was repurposed to evenly space the pulses of a neutron accelerator, and in 2005 Godfried Toussaint showed the same even-spacing patterns underlie traditional rhythms the world over. The full story — and the pedigree of every other generator — is in Appendix F.

The verb is p.euclidean:

p.euclidean(pitch, pulses, velocity=..., duration=...)
  • pitch — the drum (a name like "kick_1") or a MIDI number, exactly as in hit_steps.

  • pulses — how many hits to place across the bar. This is the one number that does the work.

  • velocity — how hard, the same as everywhere: a single number, or a (low, high) tuple for a per-hit random draw (§1.5).

  • duration — how long each hit rings, in beats (default a tight 0.1).

The hits land on the pattern’s grid — sixteen sixteenth-note steps in a 4-beat bar, just like hit_steps. So pulses=4 puts a hit every four steps, which is exactly the four-on-the-floor kick you’ve been writing by hand:

import subsequence
import subsequence.constants.instruments.gm_drums as gm_drums

composition = subsequence.Composition(bpm=120)

@composition.pattern(channel=10, beats=4, drum_note_map=gm_drums.GM_DRUM_MAP)
def drums(p):
    p.euclidean("kick_1", pulses=4, velocity=100)   # = [0, 4, 8, 12] — four on the floor

composition.render(bars=4, filename="euclid.mid")

euclidean("kick_1", pulses=4) produces hits on steps 0, 4, 8, 12 — the very list you typed in Chapter 1, now derived from a single 4. The pay-off is that changing the feel is now changing one number. Ask for five pulses instead of four and Subsequence can’t space them perfectly evenly (five doesn’t divide sixteen), so it does the next-best thing — the famous slightly-lopsided spread that underlies a thousand grooves:

@composition.pattern(channel=10, beats=4, drum_note_map=gm_drums.GM_DRUM_MAP)
def drums(p):
    p.euclidean("kick_1", pulses=5, velocity=100)   # lands on steps 0, 3, 6, 9, 12

composition.render(bars=4, filename="euclid.mid")

A few pulse counts worth knowing by ear, on the standard 16-step bar:

Euclidean rhythms in a 16-step bar

pulses

Lands on steps

Sounds like

3

0, 5, 10

three even-ish hits straddling the bar — a loping 3-against-16 cross-rhythm

4

0, 4, 8, 12

four on the floor

5

0, 3, 6, 9, 12

five driving hits, the lopsided spread under countless grooves

7

0, 2, 4, 7, 9, 11, 14

a busy, rolling line good for shakers and hats

Note

Why “Euclidean”? The spread is computed by the same algorithm Euclid used to find a greatest common divisor — the most even way to interleave N hits among M slots. You don’t need the maths to use it; the musical rule of thumb is all you need: pulses is how many, and the more evenly they can’t divide the bar, the more interesting the syncopation. Even divisors (2, 4, 8) give plain rhythms; odd counts (3, 5, 7) give the syncopated ones. The exact spread also depends on the grid euclidean works over — here the pattern’s sixteen steps — so pulses=3 is the 3-against-16 spread [0, 5, 10], not the eight-step tresillo [0, 3, 6].

Because it places notes the same way hit_steps does, everything you already know still applies — humanise with a velocity tuple, stack several euclidean calls to layer voices, and reach for plain hit_steps wherever a part needs an exact beat the even spread wouldn’t choose. Here a Euclidean kick drives a busy, humanised Euclidean hat, while the snare keeps the backbeat by hand — Euclidean’s even two-pulse spread would land on beats 1 and 3, not the 2 and 4 a backbeat wants:

@composition.pattern(channel=10, beats=4, drum_note_map=gm_drums.GM_DRUM_MAP)
def drums(p):
    p.euclidean("kick_1", pulses=4, velocity=105)               # steady floor
    p.hit_steps("snare_1", [4, 12], velocity=95)                # backbeat, placed by hand
    p.euclidean("hi_hat_closed", pulses=11, velocity=(55, 80))  # busy, humanised

composition.render(bars=4, filename="euclid.mid")

Tip

The most musical move with Euclidean rhythms is to make the pulse count react to where you are in the song, using p.cycle or p.bar_cycle from Chapter 2. pulses = 4 + p.bar_cycle(4).last * 4 plays four hats for three bars and eight on the turnaround — a build-up written in one line.

Rotating the rhythm: the necklace move. Picture a Euclidean rhythm as beads on a necklace — the same pattern can begin at a different bead. To rotate one, reach past the builder to its raw on/off list: subsequence.sequence_utils.generate_euclidean_sequence(steps, pulses) returns the 1/0 mask, and displace(mask, amount) phase-shifts it, wrapping (positive = later, negative = earlier):

>>> import subsequence.sequence_utils as utils
>>> kick = utils.generate_euclidean_sequence(8, 3)   # 3 pulses over 8 steps
>>> kick
[1, 0, 0, 1, 0, 0, 1, 0]
>>> utils.displace(kick, 1)                          # the whole necklace, one step later
[0, 1, 0, 0, 1, 0, 0, 1]

Rotating a Euclidean necklace against the downbeat is how its clave-like variants appear, and phasing two copies a step apart is the classic Steve-Reich move. displace only reorders the values you give it — distinct from p.rotate (§12.9), which shifts notes already placed on the grid.

4.2 Bresenham and weighted spread (bresenham_poly)

Euclidean is not the only way to spread N hits evenly. p.bresenham is a close cousin — same call shape, same idea of distributing pulses across the grid — but it uses the Bresenham line algorithm (the one screens use to draw a straight diagonal as stair-steps), which gives a subtly different, often end-weighted distribution. Where Euclidean tends to put a pulse on the downbeat, Bresenham tends to push the run toward the end of the bar:

@composition.pattern(channel=10, beats=4, drum_note_map=gm_drums.GM_DRUM_MAP)
def drums(p):
    p.bresenham("hi_hat_closed", pulses=5, velocity=(55, 80))  # lands on 3, 6, 9, 12, 15

composition.render(bars=4, filename="bres.mid")

Five Bresenham pulses land on steps 3, 6, 9, 12, 15 — the same even spacing as the Euclidean five, but shifted so the line ends on the last sixteenth instead of starting on the downbeat. That trailing-edge feel makes Bresenham a natural choice for a part that should lead into the next bar — a hat or shaker that pushes the groove forward — while Euclidean, anchored on beat one, suits the parts that hold it down.

Same five pulses, two generators

Generator

Lands on steps

Feel

p.euclidean(..., pulses=5)

0, 3, 6, 9, 12

anchored on the downbeat — holds the groove

p.bresenham(..., pulses=5)

3, 6, 9, 12, 15

leans on the last step — pushes into the next bar

Weighted spread with bresenham_poly

The real power tool here is p.bresenham_poly. Instead of one voice and a pulse count, you hand it several voices at once, each with a density weight, and it shares the grid out between them — assigning every step to exactly one voice, so the voices never collide. The result is an interlocking texture, the kind of woven hand-percussion groove that’s fiddly to place by hand:

p.bresenham_poly(parts={pitch: weight, ...}, velocity=...)
  • parts — a dictionary mapping each drum to a density weight. Higher weight means more hits. A weight of 0.5 aims for a hit roughly every other step; 0.25, every fourth.

  • velocity — either one number for all voices, or a dictionary giving each voice its own velocity (a voice missing from the dict falls back to the default).

@composition.pattern(channel=10, beats=4, drum_note_map=gm_drums.GM_DRUM_MAP)
def drums(p):
    p.bresenham_poly(
        parts={"hi_hat_closed": 0.5, "shaker": 0.25, "tambourine": 0.125},
        velocity={"hi_hat_closed": 70, "shaker": 55, "tambourine": 45},
    )

composition.render(bars=4, filename="poly.mid")

The three voices share the sixteen steps with no overlaps: the hi-hat takes about half, the shaker a quarter, the tambourine an eighth — a single, dovetailed groove out of one call. If the weights add up to less than 1.0, the leftover steps become rests (silence), so you can leave the texture sparse on purpose.

Important

bresenham_poly is for the background weave, not the anchors. Because it re-shares every step whenever the weights change, a voice’s hits shift position from bar to bar if you ramp its density — wonderful for evolving hats and shakers, distracting for a signature clap or cowbell. The reliable recipe: lay the interlocking texture with bresenham_poly, then place the parts that must stay put — kick, snare — with plain hit_steps or a fixed-pulse euclidean on top.

@composition.pattern(channel=10, beats=4, drum_note_map=gm_drums.GM_DRUM_MAP)
def drums(p):
    # Evolving background weave — these voices may shift bar to bar.
    p.bresenham_poly(
        parts={"hi_hat_closed": 0.5, "shaker": 0.2},
        velocity={"hi_hat_closed": 65, "shaker": 50},
    )
    # Fixed anchors on top — placed by hand so they never move.
    p.hit_steps("kick_1", [0, 8], velocity=110)
    p.hit_steps("snare_1", [4, 12], velocity=100)

composition.render(bars=4, filename="poly.mid")

4.3 Ratchets and rolls

A ratchet (or roll, or drum buzz) is the move where a single hit becomes a rapid burst — tk-tk instead of tk. In Subsequence it’s a transform: you place your notes first, then call p.ratchet to subdivide them into bursts. Each note is replaced by subdivisions evenly-spaced sub-hits inside its own time slot.

p.ratchet(subdivisions, pitch=..., velocity_start=..., velocity_end=...)
  • subdivisions — how many sub-hits each note becomes (default 2).

  • pitch — restrict the ratchet to one drum; leave it off and every note is ratcheted (handy on a melodic line, dangerous on a full kit).

  • velocity_start / velocity_end — multipliers on the original velocity across the burst, so you can shape a crescendo or a decay.

Because it works on notes already placed, the order is: generate the rhythm, then ratchet. Here a Euclidean hat line gets every hit turned into a triplet flutter — note the chaining, which the next section unpacks:

@composition.pattern(channel=10, beats=4, drum_note_map=gm_drums.GM_DRUM_MAP)
def drums(p):
    p.euclidean("hi_hat_closed", pulses=8, velocity=70).ratchet(3, pitch="hi_hat_closed")

composition.render(bars=4, filename="ratchet.mid")

The classic use is a crescendo snare roll into a turnaround. Place one snare hit near the end of a four-bar phrase, then ratchet it into four rising sub-hits so it swells into the downbeat of the next phrase. velocity_start=0.3, velocity_end=1.0 makes it grow; the shape="ease_in" curve makes the growth accelerate:

@composition.pattern(channel=10, beats=4, drum_note_map=gm_drums.GM_DRUM_MAP)
def drums(p):
    p.euclidean("kick_1", pulses=4, velocity=100)
    p.hit_steps("snare_1", [4, 12], velocity=95)               # backbeat, by hand
    p.hit_steps("hi_hat_closed", range(16), velocity=(55, 75))

    # On the last bar of every four, swell a roll into the next phrase.
    if p.bar_cycle(4).last:
        p.hit_steps("snare_1", [14], velocity=100, duration=0.5)
        p.ratchet(4, pitch="snare_1", velocity_start=0.3, velocity_end=1.0, shape="ease_in")

composition.render(bars=8, filename="roll.mid")

Warning

Ratchet only what you mean to. Called bare — p.ratchet(2) — it subdivides every note in the pattern, kick and snare and hats alike, which usually turns a groove to mush. On a drum kit, almost always pass pitch= to target one voice. On a single-voice melodic or bass pattern, the bare form is exactly right. And place ratchet after your notes but before swing (next), so the swing still nudges the parent hit.

Reference

ratchet()

4.4 Swing and timing feel (not pitch)

Everything so far has placed notes on the exact grid. Swing is what takes them off it — by a hair — to give a stiff pattern the pushed, human shuffle of a real drummer. It’s the first timing transform we meet, and the distinction it draws is one to hold onto for the rest of the guide:

Important

Swing is timing, not pitch. It nudges when notes play — sliding every other grid position a little late — and never touches which notes play. This is the opposite end of the instrument from snap_to_scale (Chapter 5), which changes a note’s pitch and never its timing. Two different dials: swing for feel, snap_to_scale for notes. Keep them straight and you’ll never reach for the wrong one.

The verb is p.swing:

p.swing(percent, grid=0.25)
  • percent — the swing amount. 50 is dead straight (no swing); 57 is a gentle shuffle (the Ableton default); 67 is hard triplet swing. The useful range is roughly 50–75.

  • grid — which notes get pushed, as a beat value. 0.25 swings sixteenth notes (the common choice); 0.5 swings eighths.

Like the articulation verbs in Chapter 3, swing is a post-placement transform: write the line straight, then swing the whole thing. Here a plain sixteenth-note hat line gets a moderate shuffle:

@composition.pattern(channel=10, beats=4, drum_note_map=gm_drums.GM_DRUM_MAP)
def drums(p):
    p.euclidean("kick_1", pulses=4, velocity=100)
    p.hit_steps("snare_1", [4, 12], velocity=95)              # backbeat, by hand
    p.hit_steps("hi_hat_closed", range(16), velocity=(55, 80))
    p.swing(57)            # gentle 16th-note shuffle over the whole bar

composition.render(bars=4, filename="swing.mid")

Render that straight (delete the p.swing line) and then with it, and the difference is unmistakable: the off-grid hats lean back, and the loop stops sounding quantised. Two refinements you’ll reach for:

@composition.pattern(channel=10, beats=4, drum_note_map=gm_drums.GM_DRUM_MAP)
def drums(p):
    p.hit_steps("hi_hat_closed", range(16), velocity=70)
    p.swing(67, grid=0.25)   # hard, triplet-ish shuffle on the sixteenths

composition.render(bars=2, filename="swing.mid")

Tip

Swing is a whole-pattern feel, so apply it once, last, after every note is placed — including after ratchet, so the rolls ride along with the swing. If only some patterns should swing (say the hats but not the kick), put p.swing in each pattern you want to feel it, and leave it out of the ones that should stay locked to the grid.

Reference

swing()

4.5 “Learn one verb, predict the rest”; builder chaining

You’ve now met a dozen p. verbs across four chapters, and they all share a shape on purpose. Once you’ve internalised it, a method you’ve never used before behaves the way you’d guess — which is the whole design goal.

The placement and transform verbs return the builder. Look back at any of the signatures: p.euclidean(...) -> PatternBuilder, p.ratchet(...) -> PatternBuilder, p.swing(...) -> PatternBuilder. Each one does its job and hands p back to you. That means you can chain them — write one verb after another on the same line, left to right, in the order they apply:

@composition.pattern(channel=10, beats=4, drum_note_map=gm_drums.GM_DRUM_MAP)
def drums(p):
    # Generate, then ratchet, then swing — read left to right as a sentence.
    p.euclidean("hi_hat_closed", pulses=8, velocity=70).ratchet(2, pitch="hi_hat_closed").swing(57)

composition.render(bars=4, filename="chain.mid")

That one line reads as a recipe: place eight even hats, turn each into a double, then shuffle the lot. Chaining is purely a convenience — it does exactly the same thing as writing the three calls on separate lines, and you should break the chain across lines the moment it stops being readable:

@composition.pattern(channel=10, beats=4, drum_note_map=gm_drums.GM_DRUM_MAP)
def drums(p):
    p.euclidean("hi_hat_closed", pulses=8, velocity=70)
    p.ratchet(2, pitch="hi_hat_closed")
    p.swing(57)

composition.render(bars=4, filename="chain.mid")

The deeper point is the shared shape of the verbs. Once you know it, you can predict an unfamiliar one:

```{list-table} The common shape of a p. verb

header-rows:

1

widths:

30 70

    • Every verb…

    • …so when you meet a new one

    • leads with its most musical argument (a placement verb’s pitch, a transform’s amount)

    • you can guess the argument order before reading the docs

    • accepts velocity= as a number or a (low, high) tuple

    • per-note humanising works the same everywhere it’s offered

    • places notes (euclidean, hit_steps) or transforms placed notes (ratchet, swing, legato)

    • you know whether to call it first (placement) or last (transform)

    • returns the builder, so it chains

    • you can string several together, or not — your call


```{note}
**Placement first, transforms last.** This is the one ordering rule that makes the
chain read correctly. *Placement* verbs (`euclidean`, `bresenham`, `hit_steps`,
`note`, `sequence`) put notes down; *transform* verbs (`ratchet`, `swing`,
`legato`, `detached`, `velocity_shape`) reshape whatever is already there. Put the
placements first and the transforms after, and a chain does what it reads like it
does.

See also

This “predict the rest” idea is the tip of a larger contract the whole API keeps — the front the chord verbs share, what (low, high) and seed= always mean, which methods chain and which return data. It’s gathered in one place in Appendix G: Learn One Verb, Predict the Rest.

4.6 Repeatability (seed= / rng=)

The moment a pattern uses randomness — a (low, high) velocity tuple, a probability that thins hits, a generator that makes a random choice — you face a question: should it sound the same every time you run it, or fresh every time? Both are useful, and Subsequence lets you choose, at three levels of scope.

A generator can make random choices in two distinct places, and it pays to know which is which. The first is the velocity tuple: velocity=(55, 80) is one independent random draw per note (§1.5) — sixteen hats give sixteen different velocities. The second is anything the generator itself rolls — most commonly probability, which thins a rhythm by giving each pulse a chance of not playing (probability=0.5 drops about half). A 16-pulse Euclidean line at probability=0.7 keeps a random-but-even ~70% of its hits, fresh each bar.

The whole-piece seed. Pass seed= when you build the Composition and the entire piece becomes reproducible — every random draw in every pattern (velocity tuples and generator choices alike) unfolds the same way each run, so the take you liked is the take you get back:

import subsequence
import subsequence.constants.instruments.gm_drums as gm_drums

composition = subsequence.Composition(bpm=120, seed=42)   # whole piece is now repeatable

@composition.pattern(channel=10, beats=4, drum_note_map=gm_drums.GM_DRUM_MAP)
def drums(p):
    p.euclidean("kick_1", pulses=4, velocity=100)
    p.hit_steps("hi_hat_closed", range(16), velocity=(55, 80))   # same 16 values every run

composition.render(bars=4, filename="seeded.mid")

The per-generator seed. Sometimes you want one generator’s own choices pinned while the rest of the piece stays free. Every generator takes its own seed= — a plain integer that fixes that call’s random decisions, regardless of the composition’s seed. Here the busy hat line is thinned to a fixed pattern of hits every run, while the shaker beside it is left free to thin differently each time:

@composition.pattern(channel=10, beats=4, drum_note_map=gm_drums.GM_DRUM_MAP)
def drums(p):
    # Thinned to the same hits every run — seed=7 pins this generator's choices.
    p.euclidean("hi_hat_closed", pulses=16, velocity=70, probability=0.6, seed=7)
    # …while this shaker is left free, thinning differently each run.
    p.euclidean("shaker", pulses=16, velocity=55, probability=0.4)

composition.render(bars=4, filename="seeded.mid")

Note

A per-generator seed= pins the generator’s own dice — probability thinning and the like — not the (low, high) velocity draw. Velocity tuples always draw from the pattern’s shared generator, so to make those repeatable you seed the Composition (above). In practice the whole-piece seed= is the one you’ll reach for nine times out of ten; a per-generator seed= is for pinning one part’s structure (which hits survive a thinning) while the rest stays loose.

The advanced form: rng=. For full control you can hand a generator its own random.Random object via rng=. This is the power-user escape hatch — it lets several calls share one stream, or lets you advance the stream yourself. You’ll rarely need it as a beginner; just know the precedence when more than one is in play:

Important

Precedence: rng= beats seed= beats the pattern’s own generator. If you pass rng=, it wins (and Subsequence warns you if you passed seed= too — pass only one). If you pass seed= alone, that call uses a fresh generator fixed to that number. If you pass neither, the call draws from the pattern’s own generator, which is reproducible whenever the composition was given a seed=. So: rng > seed > composition seed. Reach for the composition seed= to pin a whole piece, a per-generator seed= to pin one part, and rng= only when you genuinely need to share or steer the stream.

Two more things worth knowing. Patterns can already vary by p.cycle (Chapter 2), and that and the seed are independent: a seeded piece still evolves bar to bar — it just evolves the same way every run. And the same seed=/rng= controls appear on every generator in the library, not only the ones in this chapter — another payoff of the shared verb shape from §4.5.

Tip

The everyday workflow: compose with no seed at all, hitting re-run until a variation delights you; then drop a seed= on the Composition to lock that variation in place. We return to this — seeds, locks, and freezing a take — as a production workflow in a later chapter; here you have the two knobs that matter, the whole-piece seed and the per-generator one.


You can now grow a whole groove from a handful of density numbers — Euclidean and Bresenham spreads, interlocking bresenham_poly voices, ratcheted rolls, and a swing feel — chain the verbs fluently, and make any of it repeatable on demand. In Chapter 5 we turn the same generative thinking on pitch: working in scale degrees and keys, and snapping notes to a scale — the pitch-side counterpart to the timing tools you just learned.