Skip to content

RhythmPattern

rhythm_pattern

Rhythm pattern generators.

The RhythmPattern namespace produces binary rhythm patterns (lists of 1s and 0s) from various encodings: integers (binary digits), hex strings, onset spacing, random/probability vectors, and Euclidean distribution.

A pattern is just a list[int] where each element is 0 (rest) or 1 (onset).

Example

from tonal_py import RhythmPattern RhythmPattern.euclid(8, 3) # 3 beats spread over 8 steps [1, 0, 0, 1, 0, 0, 1, 0] RhythmPattern.binary(13) # 13 = 0b1101 [1, 1, 0, 1] RhythmPattern.onsets(1, 2, 1) # 1, then 2 zeros, then 1, then 1 zero [1, 0, 1, 0, 0, 1, 0]

Source parity: @tonaljs/rhythm-pattern

binary

binary(*numbers: int) -> RhythmPattern

Build a pattern from the binary digits of one or more integers.

Parameters:

Name Type Description Default
*numbers int

Integers whose binary representations will be concatenated.

()

Returns:

Type Description
RhythmPattern

Pattern as a list of 1s and 0s.

Example

from tonal_py import RhythmPattern RhythmPattern.binary(13) [1, 1, 0, 1] RhythmPattern.binary(13, 5) [1, 1, 0, 1, 1, 0, 1]

Source code in src/tonal_py/rhythm_pattern.py
def binary(*numbers: int) -> RhythmPattern:
    """Build a pattern from the binary digits of one or more integers.

    Args:
        *numbers: Integers whose binary representations will be concatenated.

    Returns:
        Pattern as a list of 1s and 0s.

    Example:
        >>> from tonal_py import RhythmPattern
        >>> RhythmPattern.binary(13)
        [1, 1, 0, 1]
        >>> RhythmPattern.binary(13, 5)
        [1, 1, 0, 1, 1, 0, 1]
    """
    pattern: RhythmPattern = []
    for n in numbers:
        for digit in bin(n)[2:]:  # strip '0b' prefix
            pattern.append(int(digit))
    return pattern

hex

hex(hex_number: str) -> RhythmPattern

Build a pattern from a hex string. Each hex digit becomes 4 bits.

Invalid hex characters become 0000.

Parameters:

Name Type Description Default
hex_number str

A string of hex digits.

required

Returns:

Type Description
RhythmPattern

Pattern as a list of 1s and 0s.

Example

from tonal_py import RhythmPattern RhythmPattern.hex("a") [1, 0, 1, 0] RhythmPattern.hex("ff") [1, 1, 1, 1, 1, 1, 1, 1]

Source code in src/tonal_py/rhythm_pattern.py
def hex(hex_number: str) -> RhythmPattern:  # noqa: A001 (mirrors JS export)
    """Build a pattern from a hex string. Each hex digit becomes 4 bits.

    Invalid hex characters become `0000`.

    Args:
        hex_number: A string of hex digits.

    Returns:
        Pattern as a list of 1s and 0s.

    Example:
        >>> from tonal_py import RhythmPattern
        >>> RhythmPattern.hex("a")
        [1, 0, 1, 0]
        >>> RhythmPattern.hex("ff")
        [1, 1, 1, 1, 1, 1, 1, 1]
    """
    pattern: RhythmPattern = []
    for c in hex_number:
        try:
            digit = int(c, 16)
            bits = format(digit, "04b")
        except ValueError:
            bits = "0000"
        for d in bits:
            pattern.append(1 if d == "1" else 0)
    return pattern

onsets

onsets(*numbers: int) -> RhythmPattern

Build a pattern from onset spacings (1, then N zeros, repeat).

Parameters:

Name Type Description Default
*numbers int

Number of zeros between consecutive onsets.

()

Returns:

Type Description
RhythmPattern

Pattern that starts with 1, followed by numbers[0] zeros,

RhythmPattern

then 1, then numbers[1] zeros, ...

Example

from tonal_py import RhythmPattern RhythmPattern.onsets(1, 2, 2, 1) [1, 0, 1, 0, 0, 1, 0, 0, 1, 0]

Source code in src/tonal_py/rhythm_pattern.py
def onsets(*numbers: int) -> RhythmPattern:
    """Build a pattern from onset spacings (1, then N zeros, repeat).

    Args:
        *numbers: Number of zeros between consecutive onsets.

    Returns:
        Pattern that starts with `1`, followed by `numbers[0]` zeros,
        then `1`, then `numbers[1]` zeros, ...

    Example:
        >>> from tonal_py import RhythmPattern
        >>> RhythmPattern.onsets(1, 2, 2, 1)
        [1, 0, 1, 0, 0, 1, 0, 0, 1, 0]
    """
    pattern: RhythmPattern = []
    for n in numbers:
        pattern.append(1)
        for _ in range(n):
            pattern.append(0)
    return pattern

random

random(length: int, probability: float = 0.5, rnd: Callable[[], float] | None = None) -> RhythmPattern

Build a random rhythm pattern of fixed length.

Each step is 1 if rnd() >= probability, else 0.

Parameters:

Name Type Description Default
length int

Number of steps.

required
probability float

Threshold (default 0.5).

0.5
rnd Callable[[], float] | None

Optional RNG function returning a float in [0, 1). Defaults to random.random — pass a deterministic generator for tests.

None

Returns:

Type Description
RhythmPattern

Pattern as a list of 1s and 0s.

Example

from tonal_py import RhythmPattern

Deterministic RNG for testing

seq = iter([0.9, 0.1, 0.9, 0.1]) RhythmPattern.random(4, 0.5, rnd=lambda: next(seq)) [1, 0, 1, 0]

Source code in src/tonal_py/rhythm_pattern.py
def random(
    length: int, probability: float = 0.5, rnd: Callable[[], float] | None = None
) -> RhythmPattern:
    """Build a random rhythm pattern of fixed length.

    Each step is `1` if `rnd() >= probability`, else `0`.

    Args:
        length: Number of steps.
        probability: Threshold (default 0.5).
        rnd: Optional RNG function returning a float in [0, 1). Defaults
            to `random.random` — pass a deterministic generator for tests.

    Returns:
        Pattern as a list of 1s and 0s.

    Example:
        >>> from tonal_py import RhythmPattern
        >>> # Deterministic RNG for testing
        >>> seq = iter([0.9, 0.1, 0.9, 0.1])
        >>> RhythmPattern.random(4, 0.5, rnd=lambda: next(seq))
        [1, 0, 1, 0]
    """
    rng = rnd if rnd is not None else _random.random
    return [1 if rng() >= probability else 0 for _ in range(length)]

probability

probability(probabilities: list[float], rnd: Callable[[], float] | None = None) -> RhythmPattern

Build a pattern with per-step probability thresholds.

Step i is 1 if rnd() <= probabilities[i].

Parameters:

Name Type Description Default
probabilities list[float]

One probability per step (length determines pattern length).

required
rnd Callable[[], float] | None

Optional RNG function. Defaults to random.random.

None

Returns:

Type Description
RhythmPattern

Pattern as a list of 1s and 0s.

Example

from tonal_py import RhythmPattern seq = iter([0.1, 0.5, 0.9]) RhythmPattern.probability([0.5, 0.5, 0.5], rnd=lambda: next(seq)) [1, 1, 0]

Source code in src/tonal_py/rhythm_pattern.py
def probability(
    probabilities: list[float], rnd: Callable[[], float] | None = None
) -> RhythmPattern:
    """Build a pattern with per-step probability thresholds.

    Step `i` is `1` if `rnd() <= probabilities[i]`.

    Args:
        probabilities: One probability per step (length determines pattern length).
        rnd: Optional RNG function. Defaults to `random.random`.

    Returns:
        Pattern as a list of 1s and 0s.

    Example:
        >>> from tonal_py import RhythmPattern
        >>> seq = iter([0.1, 0.5, 0.9])
        >>> RhythmPattern.probability([0.5, 0.5, 0.5], rnd=lambda: next(seq))
        [1, 1, 0]
    """
    rng = rnd if rnd is not None else _random.random
    return [1 if rng() <= p else 0 for p in probabilities]

rotate

rotate(pattern: RhythmPattern, rotations: int) -> RhythmPattern

Rotate a pattern right by rotations steps (negative shifts left).

Example

from tonal_py import RhythmPattern RhythmPattern.rotate([1, 0, 0, 1, 0, 0, 1, 0], 1) [0, 1, 0, 0, 1, 0, 0, 1] RhythmPattern.rotate([1, 0, 0, 1, 0, 0, 1, 0], -1) [0, 0, 1, 0, 0, 1, 0, 1]

Source code in src/tonal_py/rhythm_pattern.py
def rotate(pattern: RhythmPattern, rotations: int) -> RhythmPattern:
    """Rotate a pattern right by `rotations` steps (negative shifts left).

    Example:
        >>> from tonal_py import RhythmPattern
        >>> RhythmPattern.rotate([1, 0, 0, 1, 0, 0, 1, 0], 1)
        [0, 1, 0, 0, 1, 0, 0, 1]
        >>> RhythmPattern.rotate([1, 0, 0, 1, 0, 0, 1, 0], -1)
        [0, 0, 1, 0, 0, 1, 0, 1]
    """
    length = len(pattern)
    if length == 0:
        return []
    rotated: RhythmPattern = [0] * length
    for i in range(length):
        pos = ((i - rotations) % length + length) % length
        rotated[i] = pattern[pos]
    return rotated

euclid

euclid(steps: int, beats: int) -> RhythmPattern

Euclidean rhythm — distribute beats as evenly as possible across steps.

The Bjorklund algorithm produces a pattern with the most uniform spacing of beats. Many traditional rhythms (cinquillo, tresillo, etc.) are euclidean rhythms.

Parameters:

Name Type Description Default
steps int

Total length of the pattern.

required
beats int

Number of onsets to distribute.

required

Returns:

Type Description
RhythmPattern

Pattern as a list of 1s and 0s.

Example

from tonal_py import RhythmPattern RhythmPattern.euclid(8, 3) # tresillo [1, 0, 0, 1, 0, 0, 1, 0] RhythmPattern.euclid(16, 5) [1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0]

Source code in src/tonal_py/rhythm_pattern.py
def euclid(steps: int, beats: int) -> RhythmPattern:
    """Euclidean rhythm — distribute `beats` as evenly as possible across `steps`.

    The Bjorklund algorithm produces a pattern with the most uniform
    spacing of beats. Many traditional rhythms (cinquillo, tresillo, etc.)
    are euclidean rhythms.

    Args:
        steps: Total length of the pattern.
        beats: Number of onsets to distribute.

    Returns:
        Pattern as a list of 1s and 0s.

    Example:
        >>> from tonal_py import RhythmPattern
        >>> RhythmPattern.euclid(8, 3)        # tresillo
        [1, 0, 0, 1, 0, 0, 1, 0]
        >>> RhythmPattern.euclid(16, 5)
        [1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0]
    """
    pattern: RhythmPattern = [0] * steps
    d = -1
    for i in range(steps):
        v = math.floor(i * (beats / steps))
        pattern[i] = 1 if v != d else 0
        d = v
    return pattern