Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

01 | Rhythm and Time | 02 | Rhythmic Vocabulary

from klotho import Tree, RhythmTree as RT, plot, play
from fractions import Fraction
import numpy as np

Taxonomy of Proportions

RTs are very flexible in terms of the range of possible rhythms. Because the choices are seemingly endless (and, in a mathematical sense, they literally are), it’s worth making some general observations about types of rhythms so we can have a working vocabulary when thinking about rhythm.

We can generalize pretty much any fundamental rhythmic idea into one of a few basic types:

Pulse | Periodic | Irregular | Accelerating | Decelerating

Anything else is usually some combination of these basic types. Let’s explore...

Basic Types

Pulse

All subdivisions are equal — a steady, even division of time.

for n in [3,5,11,17]:
    S = (1,)*n
    rt = RT(subdivisions=S)
    plot(rt, layout='ratios', animate=True, beat='1/4', bpm=126)
print("...and so on...")
Loading...
Loading...
Loading...
Loading...
...and so on...

Periodic (Cycling)

A short motif of unequal durations repeated end-to-end. Not a pulse (the durations differ), but not random either — it has a recurring pattern.

for motif in [(1, 3), (2, 5, 1), (1, 2, 3)]:
    S = motif * 4
    rt = RT(subdivisions=S)
    plot(rt, layout='ratios', animate=True, beat='1/4', bpm=92)
print("...and so on...")
Loading...
Loading...
Loading...
...and so on...

Irregular

Subdivisions with no consistent pattern — each duration is independently chosen.

Note: You could call this “random” (and the below examples do use random values), but notice that as soon as you loop any of these, they lose this “random” quality and become more like the above Periodic / Cyclic types. A passage would need to continue with random (i.e., unpredictable) proportions to retain this Irregular quality.

for i in range(4):
    l = [n for n in range(1,9)]
    np.random.shuffle(l)
    S = tuple(l)[:i + 4]
    rt = RT(subdivisions=S)
    plot(rt, layout='ratios', animate=True, beat='1/4', bpm=126)
print("...and so on...")
Loading...
Loading...
Loading...
Loading...
...and so on...

Accelerating / Decelerating

Durations that monotonically shrink (accelerating — events get closer together) or grow (decelerating — events spread apart).

S = tuple(n for n in range(1, 17)[::-1])
rt = RT(subdivisions=S)
plot(rt, layout='ratios', animate=True, beat='1/4', bpm=92)
Loading...
S = tuple(n for n in range(1, 17))
rt = RT(subdivisions=S)
plot(rt, layout='ratios', animate=True, beat='1/4', bpm=92)
Loading...

...and that’s kinda all we need. From here, we can combine any of the above types at different levels of the tree:

Mixed Types

Irregular-Pulse

Unequal top-level groups, each subdivided into a steady pulse.

for i in range(4):
    S = [1, 2, 3, 5, 7, 11, 13]
    np.random.shuffle(S)
    S = tuple((s, (1,)*np.random.randint(3, 8 if s > 2 else 4)) for s in S)
    rt = RT(meas=2, subdivisions=S)
    plot(rt, layout='containers', animate=True, beat='1/4', bpm=62)
    print()
print("...and so on...")
Loading...

Loading...

Loading...

Loading...

...and so on...

Pulse-Accel/Decel

Equal top-level groups, each containing an accelerating or decelerating subdivision.

S = tuple((1, tuple(range(1, 11)[::-1])) for _ in range(3))
rt = RT(subdivisions=S)
plot(rt, layout='containers', animate=True, beat='1/4', bpm=92)
Loading...
S = tuple((1, tuple(range(1, 11))) for _ in range(3))
rt = RT(subdivisions=S)
plot(rt, layout='containers', animate=True, beat='1/4', bpm=92)
Loading...

Irregular-Accel/Decel

Unequal top-level groups, each with an accelerating or decelerating interior.

S = tuple((np.random.randint(1, 5), tuple(range(1, np.random.randint(5, 12))[::-1])) for _ in range(4))
rt = RT(subdivisions=S)
plot(rt, layout='containers', animate=True, beat='1/4', bpm=84)
Loading...
S = tuple((np.random.randint(1, 5), tuple(range(1, np.random.randint(5, 12)))) for _ in range(4))
rt = RT(subdivisions=S)
plot(rt, layout='containers', animate=True, beat='1/4', bpm=84)
Loading...

Accel/Decel-Pulse

Top-level groups that accelerate or decelerate, each filled with an even pulse.

S = tuple((n, (1,)*5) for n in range(1, 8)[::-1])
rt = RT(subdivisions=S)
plot(rt, layout='containers', animate=True, beat='1/4', bpm=84)
Loading...
S = tuple((n, (1,)*5) for n in range(1, 8))
rt = RT(subdivisions=S)
plot(rt, layout='containers', animate=True, beat='1/4', bpm=84)
Loading...

Accel-Decel / Decel-Accel

Acceleration at one level, deceleration at the other — the two directional types working against each other.

S = tuple((n, tuple(range(1, 8))) for n in range(1, 8)[::-1])
rt = RT(subdivisions=S)
plot(rt, layout='containers', animate=True, beat='1/4', bpm=72)
Loading...
S = tuple((n, tuple(range(1, 8)[::-1])) for n in range(1, 8))
rt = RT(subdivisions=S)
plot(rt, layout='containers', animate=True, beat='1/4', bpm=64)
Loading...

Irregular-Cycling

Unequal top-level groups, each subdivided by a repeating rhythmic motif.

S = tuple((np.random.randint(1, 6), (1, 3) * 3) for _ in range(6))
rt = RT(subdivisions=S)
plot(rt, layout='containers', animate=True, beat='1/4', bpm=54)
Loading...

Accel/Decel-Cycling

Accelerating or decelerating top-level groups, each containing a cycling subdivision.

S = tuple((n, (1, 2, 3) * 2) for n in range(1, 7)[::-1])
rt = RT(subdivisions=S)
plot(rt, layout='containers', animate=True, beat='1/4', bpm=54)
Loading...
S = tuple((n, (1, 2, 3) * 2) for n in range(1, 9))
rt = RT(subdivisions=S)
plot(rt, layout='containers', animate=True, beat='1/4', bpm=36)
Loading...

These combinations can nest further. There’s nothing stopping us from combining three (or more) types at successive levels of the tree:

Irregular-Accel-Pulse

Three levels deep: irregular grouping at the top, acceleration in the middle, pulse at the leaves.

S = tuple(
    (np.random.randint(1, 5),
     tuple((n, (1,)*4) for n in range(1, np.random.randint(4, 8))[::-1]))
    for _ in range(3)
)
rt = RT(span=2, subdivisions=S)
plot(rt, layout='containers', animate=True, beat='1/4', bpm=72)
Loading...

...and so on...

The point is that these basic rhythmic typesPulse, Cycling, Irregular, and Accelerating/Decelerating — can be freely composed at any level of the tree. Each level of nesting adds a new dimension of rhythmic structure, and the combinatorial possibilities grow rapidly. We can, of course, also inject rests into any of these.

This is another benefit of working with rhythm in tree form: we can apply targeted design decisions at specific temporal strata. The top level might be irregular while the subdivisions within each group follow a steady pulse, or the macro pacing might accelerate while the inner detail cycles through a repeated motif. Each level of the tree is an independent design choice.

If we think about rhythm in terms of these simple basic types rather than getting lost in the specific numbers, we have a vocabulary for creating rhythms — a way to describe what we want before worrying about the exact values.