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 | Sequences and Blocks

from klotho import RhythmTree as RT, plot, play
from klotho.chronos import TemporalUnit as UT, TemporalUnitSequence as UTS, TemporalBlock as BT
from fractions import Fraction
Loading...

A single UT is a self-contained rhythmic unit. But music unfolds over time and across voices. We need ways to arrange temporal units into larger structures:

  • Sequences — UTs placed one after another in time (horizontal).

  • Blocks — UTs stacked simultaneously (vertical / polyphonic).

Sequences

A TemporalUnitSequence (UTS) is an ordered sequence of temporal units. Each UT plays in succession, and each can have its own tempo, time signature, and internal subdivision.

ut1 = UT(tempus='4/4', prolatio=(1, 1, 1, 1), beat='1/4', bpm=120)
ut2 = UT(tempus='3/4', prolatio=(2, 1, 3), beat='1/4', bpm=90)
ut3 = UT(tempus='5/8', prolatio=(1, 1, 1, 1, 1), beat='1/8', bpm=160)

uts = UTS([ut1, ut2, ut3])
print(uts)
  Tempus Type      Tempo      Start        End   Duration
0    4/4    S  1/4 = 120  00s:000ms  02s:000ms  02s:000ms
1    3/4    S   1/4 = 90  02s:000ms  04s:000ms  02s:000ms
2    5/8    S  1/8 = 160  04s:000ms  05s:875ms  01s:875ms

Visualization placeholder — UTS plotter in development.

Notice that each UT in the sequence can have a completely different tempo, time signature, and subdivision structure. The sequence simply plays them in order — ut1 finishes, then ut2 begins, then ut3.

Inspecting the Sequence

A UTS tracks the absolute time offsets for each UT. We can iterate over it to see how the units are laid out:

for i, ut in enumerate(uts):
    print(f"UT {i}: tempus={ut.tempus}, bpm={ut.bpm}, offset={ut.offset:.3f}s, duration={ut.duration:.3f}s")
UT 0: tempus=4/4, bpm=120, offset=0.000s, duration=2.000s
UT 1: tempus=3/4, bpm=90, offset=2.000s, duration=2.000s
UT 2: tempus=5/8, bpm=160, offset=4.000s, duration=1.875s

Each UT’s offset is automatically computed from the cumulative durations of the preceding units. The total duration of the sequence is the sum of all its parts.

Building Sequences Incrementally

You can also build sequences by appending units one at a time:

ut_a = UT(tempus='2/4', prolatio=(3, 1), beat='1/4', bpm=100)
ut_b = UT(tempus='2/4', prolatio=(1, 2, 1), beat='1/4', bpm=100)
ut_c = UT(tempus='2/4', prolatio=(1, 1, 1, 1), beat='1/4', bpm=100)

phrase = UTS([ut_a, ut_b, ut_c])
print(phrase)
print(f"\nTotal duration: {phrase.duration:.3f}s")
  Tempus Type      Tempo      Start        End   Duration
0    2/4    S  1/4 = 100  00s:000ms  01s:199ms  01s:199ms
1    2/4    S  1/4 = 100  01s:199ms  02s:399ms  01s:199ms
2    2/4    S  1/4 = 100  02s:399ms  03s:599ms  01s:199ms

Total duration: 3.600s

Blocks

A TemporalBlock (BT) is a collection of temporal units played simultaneously — polyphonic layering. Each UT in the block starts at the same point in time, and their individual tempos and subdivisions run in parallel.

This is how you create rhythmic counterpoint: multiple independent rhythmic voices sounding at once, each with its own internal logic.

bt = BT([ut1, ut2])
print(bt)
Rows:     2
Axis:     -1
Duration: 02s:000ms
Time:     00s:000ms - 02s:000ms
--------------------------------------------------

Visualization placeholder — BT plotter in development.

The block overlays ut1 (4/4 at 120 bpm) with ut2 (3/4 at 90 bpm). Because each UT has its own tempo and meter, the result is a polyrhythmic texture — two independent rhythmic streams sounding simultaneously.

Nesting

Sequences and blocks can be composed freely: a sequence of blocks, a block of sequences, or any recursive nesting thereof. These are the structural primitives that connect the rhythmic trees we’ve been building to the larger architecture of a musical work. blocks that connect the rhythmic structures we’ve been exploring to the larger architecture of a musical work.


bt_layer = BT([ut_a, ut_b])
uts_of_blocks = UTS([bt_layer])

print("Block of two UTs:")
print(bt_layer)
print(f"\nBlock duration: {bt_layer.duration:.3f}s")
Block of two UTs:
Rows:     2
Axis:     -1
Duration: 01s:199ms
Time:     00s:000ms - 01s:199ms
--------------------------------------------------


Block duration: 1.200s

Summary

  • A TemporalUnitSequence (UTS) arranges UTs in succession. Each UT can have its own tempo, time signature, and subdivision. Offsets are computed automatically.

  • A TemporalBlock (BT) layers UTs simultaneously for polyrhythmic textures.

  • UTS and BT can be nested freely — blocks of sequences, sequences of blocks, or deeper structures.

  • Plotters and audio playback for raw UTS/BT are still in development. Once implemented, they’ll work like the UT plotter: plot(uts).play().