Skip to content

Patterns: writing rhythm as a function of time

pat("bd sd hh hh") is a pattern, and a pattern is a function you can ask what is sounding between here and there. Ask it about one cycle and you get four events. Ask about the second half and you get two. Ask about a hundred cycles and it will tell you, because nothing was ever stored; the events are computed from the query.

This is why patterns compose. A pattern can be reversed, slowed, sliced, stacked against another, or handed a continuous curve to read its volume from, and none of those operations has to know how long the music is or where it will be sampled. The design comes from TidalCycles, and where Field's operators share a name with Tidal's they mean the same thing; the places they do not are listed below.

The piano rolls on this page show the events each pattern produces, and the clips under the mini-notation forms play those events through the Tidal drum samples (Dirt-Samples). Sound shows how to play a pattern through them from a box.


Two things every pattern is made of

An event is a value with a span of time. A query is a span you ask about. The whole language is functions from queries to lists of events.

// Nothing is stored. Ask the same pattern three different questions.
val drums = pat("bd sd hh hh")

val answers = [
    "one cycle" to PatArc(PatQ.ZERO, PatQ.ONE),
    "second half" to PatArc(PatQ.of(1, 2), PatQ.ONE),
    "cycle 97" to PatArc(PatQ.of(97), PatQ.of(98)),
].joinToString("\n") { (label, arc) ->
    "%-12s %s".format(label, drums.events(arc).joinToString("  ") { "${it.value}@${it.arc.start}" })
}
println(answers)
display("<pre>" + answers + "</pre>")

pat-doc-first-query

Time is exact rational arithmetic (PatQ), never floating point. A sixteenth note is 1/16, not 0.0625, so a pattern subdivided into thirds and one subdivided into fifths still line up perfectly fifteen cycles later.


Mini-notation

The string inside pat(...) is a small language of its own, and it is the same mini-notation TidalCycles uses. A space divides, brackets group, and about a dozen suffixes say the things a space cannot. Each form gets a card below: the notation, a piano roll of exactly what it schedules, and a clip of it.

Colour is the instrument (bd blue, sd orange, cp pink, oh green, hh yellow), so two bars that look different sound different. Height is pitch, so the kick sits at the bottom of every lane and the hats at the top, here and in every roll further down the page. Each rectangle is one event of the pattern.

Steps, and the space between them

A space is the only separator the notation has, and it always means divide this level evenly. Everything below is a way of saying something other than "evenly".

bd sd

a piano roll of bd sd over 2 cycles

Two steps, half a cycle each.

bd sd hh cp

a piano roll of bd sd hh cp over 2 cycles

Four steps, a quarter each. The cycle is divided by however many steps it finds.

bd ~ sd ~

a piano roll of bd ~ sd ~ over 2 cycles

~ is a rest — a step that takes up its share of the cycle and plays nothing.

The rest is not a quiet event; it is no event. Four steps are written and two rectangles are drawn, which is why the roll has gaps rather than gaps at low opacity.

bd [sd sd]

a piano roll of bd [sd sd] over 2 cycles

Brackets group. The bracket is one step that happens to contain two, so the pair shares the half-cycle the single sd would have had.

This is the recursion the whole notation rests on: a group is a step, so anything you can write at the top level you can write inside brackets.

bd [sd [hh hh]]

a piano roll of bd [sd [hh hh]] over 2 cycles

And it nests as deep as you care to go — the inner pair splits the inner half of the outer half.

Two ways to group

Brackets are not the only way to carve up a cycle. One of these avoids the brackets and the other ignores the cycle's division entirely.

bd sd . hh hh hh

a piano roll of bd sd . hh hh hh over 2 cycles, drawn above [bd sd] [hh hh hh] so the two can be read against each other

A standalone . groups what precedes it against what follows, with no brackets.

Read the two lanes against each other: this is two halves, not five fifths. The dot is the cheapest way to write an uneven division, and the second lane is the spelling it stands for.

bd sd, hh*4

a piano roll of bd sd, hh*4 over 2 cycles

A comma stacks layers: both play through the same cycle, each divided on its own terms.

Two steps in one layer and four in the other. Nothing is renegotiated between them — a stack is simply two patterns queried together.

Speed, repetition, and width

Four postfix modifiers, and two markers that spell two of them without the punctuation. Every one of them attaches to the step on its left, including a bracketed group.

bd*4

a piano roll of bd*4 over 2 cycles

*n plays the step n times as fast — four kicks where one was written.

[bd sd]*2

a piano roll of [bd sd]*2 over 2 cycles, drawn above bd sd bd sd so the two can be read against each other

*n on a group speeds up the whole group, so the pair happens twice.

bd/2

a piano roll of bd/2 over 4 cycles

/n is the other direction: half as fast, so this one spans two cycles and you hear it twice in four.

Drawn over four cycles because a form that is slower than the bar has nothing to show inside one. Note that pat is still a function of time here — the event simply has an arc two cycles wide.

bd!3 sd

a piano roll of bd!3 sd over 2 cycles, drawn above bd bd bd sd so the two can be read against each other

!n repeats the step, and each copy takes a slot of its own — so this is four steps, not two.

bd ! ! sd

a piano roll of bd ! ! sd over 2 cycles, drawn above bd!3 sd so the two can be read against each other

A bare ! repeats whatever preceded it, once per !.

Repeats accumulate rather than nest: two !s after bd give three kicks, which is what Tidal does. Nesting them would give four.

bd@3 sd

a piano roll of bd@3 sd over 2 cycles

@n widens the step instead of repeating it: bd claims three of the four slots and sounds once.

bd _ _ sd

a piano roll of bd _ _ sd over 2 cycles, drawn above bd@3 sd so the two can be read against each other

A standalone _ extends the previous step by one slot — the same rhythm, written the way a notation-first person would write it.

! and _ are the pair worth keeping straight, and the rolls make the difference plain: bd ! ! sd sounds three kicks, bd _ _ sd sounds one long one.

Forms that make the next cycle different

Up to here the notation has described a single bar that then repeats. These four are the point at which it stops doing that, and they are the reason a pattern is a function of time rather than a list of notes. Each is drawn over four cycles.

<bd sd>

a piano roll of <bd sd> over 4 cycles

Angle brackets take one alternative per cycle: kick, snare, kick, snare.

<bd sd cp> hh

a piano roll of <bd sd cp> hh over 4 cycles

Alternation is a step like any other, so it can sit inside a sequence — the first half rotates through three while the hat stays put.

Three alternatives against a four-cycle figure, so the pattern takes three cycles to come back round and the fourth cycle here is the first one again.

bd*8?

a piano roll of bd*8? over 2 cycles

? drops the step — half the time, by default. It is random but not arbitrary: the same cycle always drops the same notes.

hh*8?0.3

a piano roll of hh*8?0.3 over 2 cycles

…and ?p sets the probability. Thirty per cent of sixteen hats, deterministically.

Deterministic matters more than it sounds: a pattern you can re-query is a pattern you can render offline, scrub backwards, or draw — which is how the clip under this roll was made, and why it matches the picture note for note.

[bd|sd|cp]

a piano roll of [bd|sd|cp] over 4 cycles

| picks one of the alternatives per cycle at random — where <> cycles through them in order.

The brackets are only for show here — a whole string is a group like any other, so bare bd|sd|cp parses to exactly this. Deterministic in the same way ? is, so this figure is not a different picture every time the docs build runs.

Polymeter

Braces divide by a step count rather than by the cycle, which is the one thing brackets and commas cannot express.

{bd sd, hh hh hh}

a piano roll of {bd sd, hh hh hh} over 4 cycles

Layers of different lengths, all stepping at the first layer's rate — two steps a cycle here, so the three hats take a cycle and a half.

This is not [bd sd, hh hh hh]. A comma would squeeze three hats into one cycle; the braces keep the hats at the kick's step size and let them run over.

{bd hh cp}%8

a piano roll of {bd hh cp}%8 over 4 cycles

%n sets that step rate explicitly: three atoms laid out eight steps to the cycle.

Read it across the bar line rather than within one. Three does not divide eight, so the figure phases — cycle 1 starts where cycle 0 left off, and it takes three cycles to return. That is the whole reason to write a polymeter instead of a fast sequence.

Rhythms the notation generates for you

Two bracket suffixes that do not spell a rhythm out but construct one, from a step count and a rule.

bd(3,8)

a piano roll of bd(3,8) over 2 cycles

Euclidean: spread three onsets as evenly as eight slots allow. Three against eight is x..x..x. — gaps of 3, 3 and 2, the tresillo.

This is Tidal's spelling, bead for bead, and so is every other (pulses, steps) pair. That matters because a rotation of the same figure is a different groove against the downbeat, so a shared notation has to agree on one.

bd(3,8,2)

a piano roll of bd(3,8,2) over 2 cycles

A third number rotates the figure, which is how you ask for the spelling you had in mind rather than the one the algorithm starts on.

A positive offset rotates the figure earlier. This is the same three onsets as the card above, moved two slots round the cycle.

bd(3,8), cp(5,8)

a piano roll of bd(3,8), cp(5,8) over 2 cycles

Two Euclidean figures over the same eight slots. Three against five is where the interlocking gets interesting.

hh(k16)

a piano roll of hh(k16) over 2 cycles

k asks for a Kolakoski rhythm instead — sixteen steps of the self-describing word, whose own run lengths spell the word back. g reads its gaps.

Unlike Euclid, turning the step count up extends a Kolakoski figure rather than re-placing it, so the first half of k32 is k16. Field's own figure for that is further down this page.

A value on a step

The last suffix is not about time at all.

bd:3 sd:1

a piano roll of bd:3 sd:1 over 2 cycles

:n is the sample index — which bd out of the folder of kicks.

It arrives two ways at once: the atom is still bd:3 for anything reading names, and the event carries n = 3 for the parameter algebra. The colour still says kick, because bd:3 is a kick.

Every card above is one line in a box, and the same renderer draws whatever you are editing:

showPatMini("polymeter-sub") // the published card
showPatRollOf("mine" to pat("bd(5,8,2), hh*8?0.2"), cycles = 4) // anything else, same scale
showPatAudio(pat("bd(5,8,2), hh*8?0.2"), cycles = 4, cps = 2.0) // and hear it

Continuous signals

sine(), saw(), tri(), square(), perlin() and rand() are patterns too. They just answer every query with exactly one event, sampled at the query's exact midpoint. They have no rhythm of their own, so you either give them one with segment(n) or hand them to something that already has one.

:::static
// Six signals through segment(16), two cycles. The polyline is the value each cell
// latched; the rectangle height is the same number.
showPatRoll("signals-segment")
:::fold open the output pane
displayOpen("main")
:::endfold

Piano roll of six continuous signals discretised through segment(16)

The midpoint convention is what makes the picture readable: sine() peaks a quarter of the way in, square() switches exactly on the half, saw() climbs monotonically.

A parameter can be a function

The useful consequence: a signal can drive a parameter without changing the rhythm.

:::static
/*
Lane 1 is a cutoff staircase typed out by hand, one literal per note — the whole of what
a pattern language without signals can do. Lane 2 is appBoth, which shatters four kicks
into sixteen. Lanes 3-5 are `with(cutoff(curve))` over 4, 8 and 16 notes: the same curve,
sampled at whatever density the rhythm happens to be, note count untouched.
*/
showPatRoll("continuous-parameter")
:::fold open the output pane
displayOpen("main")
:::endfold

Piano roll comparing a hand-typed cutoff staircase, appBoth fragmentation, and a continuous parameter read at three note densities

Hear it: sixteen hats a cycle, two cycles flat and then two with the gain following a sine. Same notes, same count, one parameter now a function of time. The hats are short noise bursts from the built-in patVoice rather than samples: a sampled hat rings for most of a second, and sixteen a cycle would blur into one sound with no level to follow.


Whose rhythm survives

When two patterns meet, the rhythm has to come from somewhere. There are exactly three answers, and Field spells them the way Tidal does; the bar marks the side the structure comes from.

operator structure pat("0 1") against pat("10 20 30")
`|+|` both Produces four intersection events. Both rhythms contribute boundaries, so neither side alone determines the structure.
`|+` left Produces two events with the left pattern’s structure. Use it when the left side should keep its rhythm.
`+|` right Produces three events with the right pattern’s structure. Use it when the right side should keep its rhythm.
:::static
showPatRoll("structure-choice")
:::fold open the output pane
displayOpen("main")
:::endfold

Piano roll showing the three structure-choice combinators producing four, two and three events

Kotlin will not accept /, < or > in an identifier, so division is `|over|` and the take-a-value family spells its arrow as an L or an R. The bar always keeps its Tidal meaning.


Rhythm transforms

Each lane applies one transform to the same four-step source:

:::static
// pat("a b c d") and seven transforms over four cycles, so the per-cycle ones are visible.
showPatRoll("rhythm-transforms")
:::fold open the output pane
displayOpen("main")
:::endfold

Piano roll of rev, euclid, iter, linger, shuffle, slow and fast against a four-step source

rev mirrors, euclid(3,8) spaces three onsets over eight slots, iter(4) advances a quarter each cycle, shuffle(4) permutes differently every cycle. Read the slow(2) lane across the bar lines: a b, then c d, then round again.

A rhythm that describes itself

A Euclidean rhythm needs its density decided in advance: euclid(3,8) is the answer to "three onsets, eight slots". kolakoski(16) answers a different question. The terms of the word 1 2 2 1 1 2 1 2 2 1 … are read as alternating on- and off-runs, so the figure's own run lengths spell out the word that made it — sixteen steps of it are x..xx.x..x..xx.x. In mini-notation it is a k inside the Euclidean brackets, and g asks for the other reading:

struct(kolakoski(16), pat("bd")) // the figure, as a bass drum
pat("bd(k16) hh*4") // the same, in a string
pat("bd(g16)") // each term as the gap to the next onset instead
:::static
// Two Kolakoski step counts, the gap reading, two Euclidean rhythms for contrast, and the
// alphabet {2,3} — all over two cycles.
showPatRoll("kolakoski")
:::fold open the output pane
displayOpen("main")
:::endfold

Piano roll of kolakoski at 16 and 32 steps, its gap reading, euclid(3,8), euclid(3,16) and the {2,3} alphabet

Read the first half of the kolakoski(32) lane against the whole of kolakoski(16): the same figure at half the width. Turning the step count up extends a Kolakoski rhythm instead of re-placing it, so the step count is a dial you can turn while the music plays. The two euclid lanes are there as the counter-example — euclid(3,16)'s first half is not euclid(3,8), because the step count is part of the question a Euclidean rhythm answers.

The gap reading — kolakoski(16, KolakoskiReading.GAPS), or (g16) in a string — reads the same terms as distances between onsets rather than run lengths, which fills the bar in to xx.x.xxx.xx.x.xx: eleven onsets in sixteen steps against the other reading's eight, and never more than one rest between hits.

The alphabet is a parameter too. kolakoski(16, a = 2, b = 3) is the self-describing word over {2,3} — 2 2 3 3 2 2 2 3 3 3 … — which has no run shorter than two, so that last lane never leaves a lone hit. kolakoskiInv and kolakoskiFull complete the family the way euclidInv and euclidFull do, and ->kolakoski is in RectSL for laying the figure out in space rather than in time.

Counting with one, placing with the other

The two figures answer different questions, which means they compose. Read the Kolakoski word as a sequence of densities rather than durations — each term saying how many hits go into the next group of slots — and let the Euclidean algorithm decide where inside the group those hits sit:

val counts = kolakoskiWord(8) // 1 2 2 1 1 2 1 2 — sums to 12
val hybrid = pat(counts.joinToString(" ") { "[bd($it,3)]" }) // [bd(1,3)] [bd(2,3)] …

Eight groups of three slots is a twenty-four slot bar carrying twelve onsets, which is exactly the density of euclid(12,24) — so the two lanes differ only in distribution. The Euclidean one is a flat alternation, because that is what maximal evenness over that density is. The hybrid bunches: the groups the word gives two hits to play them a slot apart, the ones it gives one sit alone, and the pattern of which is which is the word describing itself. Locally maximally even, globally self-describing.

:::static
// Twelve onsets in twenty-four slots, three ways: the hybrid, the maximally even
// figure of the same density, and the word read as run lengths instead.
val counts = kolakoskiWord(8)
val hybrid = pat(counts.joinToString(" ") { "[bd($it,3)]" })
showPatRollOf(
    "kolakoski x euclid" to hybrid,
    "euclid(12,24)" to struct(euclid(12, 24), pat("bd")),
    "kolakoski(24)" to struct(kolakoski(24), pat("bd")),
    title = "12 onsets in 24 slots, three ways",
    cycles = 2,
)
:::fold open the output pane
displayOpen("main")
:::endfold

Piano roll of the hybrid above euclid(12,24) and kolakoski(24) at the same density: the hybrid bunches its onsets into pairs where the Euclidean lane alternates evenly

The same rule run recursively is a rhythmic hierarchy: let the word say how many children a pulse has, let bjorklund place them on the parent's own slot grid, and let each child run until the next one so a level always tiles the bar. A count of one is then a pulse that holds rather than splits, and the level sizes come out as the word's own partial sums — one, one, two, three, four, seven. The whole scene, both figures and the audio, is the kolakoski_euclid transcript, openable from the welcome screen.

The shortest way to write a rhythm down

patCondense runs the parser backwards. Give it a rhythm and it hands back the shortest string that plays it.

patCondense("x ~ ~ x ~ x ~ ~").notation // x(3,8)
patCondense(kolakoskiMask(16)).notation // x(k16)
patCondense("bd [hh hh] sd [cp cp]").notation // bd hh*2 sd cp*2
pat("bd sd hh cp").rev().condense().notation // cp hh sd bd

The search covers the grammar the rest of this page describes: atoms and ~, _ and @n, [ ] at every divisor of a span, !n, *n, (p,s,r), (kN) and (gN), , and < >. It scores a candidate by the length of the string and accepts it only if it is exact, and exact is settled by playing it: every candidate goes back through pat and its events are compared to the target's. The one-slot-per-step spelling is always in the running, so the answer is never longer than that.

A rhythm with nothing in it comes back as itself. patCondense("a b c d e c a d") is a b c d e c a d, because no operator in the grammar shortens it.

:::static
// Four pairs: a rhythm above, its condensation below, played. Each pair has to coincide.
showPatRoll("condense")
:::fold open the output pane
displayOpen("main")
:::endfold

Piano roll of four rhythms against their condensations: x(3,8), x(k16), bd hh*2 sd cp*2, and one unchanged

Three kinds of target are refused rather than approximated, and PatCondensation.refusal says which. A pattern that changes at random from cycle to cycle, such as [a|b|c] d or bd*8?0.4, has no period to condense. A pattern whose events cross a bar line has no bar to spell them in. And a pattern carrying parameters no atom can write down, like the pan that jux adds, would lose them.

Besides notation, a PatCondensation carries flat (the slot-by-slot spelling), compression (the ratio of the two lengths), structure (the constructs in the answer, read back off its parse) and limits (the search bounds, and which of them the run used).

This is also where the transcription path ends. patTranscribe recovers a step grid from audio and writes it out one slot per step; patCondense(transcription) turns that into a figure. The loop it belongs to is drawn in Audio render and analysis.

Time spans cycles, and events can cross bar lines

slow, fast, timeScale, density and mini-notation's /n are defined in absolute time, so pat("a b c d").slow(2) really does take two cycles: a b in the first, c d in the second.

The consequence is that an event can straddle a bar line. pat("a b c").slow(2) is three steps over two cycles, so b runs from 2/3 to 4/3, straight through it. A player asks for one cycle at a time, so it sees b twice, clipped both times, and would strike it twice if the events did not know better.

They do. Every event carries both the part you can see and the whole it was clipped from:

val crossing = pat("a b c").slow(2)

val lines = buildList {
    // One wide query: three notes, and b genuinely spans the bar line.
    add(
        "drawn    " + crossing.events(PatArc(PatQ.ZERO, PatQ.of(2))).joinToString("  ") { "${it.value}[${it.arc}]" },
    )
    // Cycle by cycle, b comes back twice — but only one fragment carries its onset.
    for (cycle in 0..1) {
        add(
            "cycle $cycle  " +
                crossing.events(PatArc(PatQ.of(cycle), PatQ.of(cycle + 1))).joinToString("  ") {
                    "${it.value}[${it.arc}]" + if (it.hasOnset) "" else " (continues)"
                },
        )
    }
    // And what actually gets played: three notes, each at its full length.
    add(
        "played   " + crossing.eventSequence(0).take(3).toList().joinToString("  ") { "${it.value}[${it.arc}]" },
    )
}
    .joinToString("\n")
println(lines)
display("<pre>" + lines + "</pre>")

pat-doc-whole-part

hasOnset is the difference, and eventSequence, which every scheduling path uses, yields only the fragment that has one, widened back to the note's real length. A signal event has no whole at all (null), because a continuous curve has no note to begin; segment(n) is what gives it one.


Groove

Swing here is exact rational time, not a nudge in milliseconds: swing(4) puts every off-beat exactly a third of a subdivision late, and the pattern stays exactly quantised.

:::static
showPatRoll("groove")
:::fold open the output pane
displayOpen("main")
:::endfold

Piano roll of swing, press, squeeze, ncat, euclidFull and sometimesEventBy

Eight hats a cycle, the same noise bursts: two cycles dead straight, then two swung.

chunk(n, f) applies a transform to a different 1/n of the bar each cycle, walking through all of them before it repeats. inside(n, f) is its cycle-invariant cousin: it slows down, transforms, and speeds back up, doing the same thing every cycle.


Harmony and voice leading

Chords are written root'name (c'maj7, a'min7), and everything that applies to a pattern applies to a pattern of chords.

voiceLead() chooses each chord's inversion and octave to minimise how far the voices have to move, without changing the harmony.

:::static
showPatRoll("voice-leading")
:::fold open the output pane
displayOpen("main")
:::endfold

Pitched piano roll: a chord progression as written, then voice-led, then arpeggiated

Spelled from their own roots the progression spans 60–79 and leaps; voice-led it sits inside 53–64 with near-horizontal lines where common tones are held. 83 semitones of movement becomes 9, and the pitch-class sets are identical.


A rhythm as a curve — and as a graph you can fit

A pattern schedules events. Sometimes you want those events to trigger something other than a note, such as a continuous curve: an envelope to drive a parameter, a gesture to animate something with. patFn does that: each event becomes a little kernel, and the value at any time is the blend of the kernels around it. The kernel is chosen per atom, and its numbers are written in the atom itself:

Pattern atom example Shape and parameters
pat("g:(1,0.12)") Places value 1 under a Gaussian influence kernel with spread 0.12 in the function’s time coordinate. Overlapping atoms contribute to a weighted average.
pat("tent:(1,0.08)") Uses a linearly falling tent kernel of radius 0.08 around the event. Outside the radius it contributes no weight.
pat("box:(1,0.08)") Uses a constant-weight box kernel of radius 0.08. Its boundary is abrupt rather than smoothly tapered.
pat("wendland:(1,0.08) spline:(0,0.08)") Uses compactly supported smooth kernels around the events. Radius controls how far each event influences the weighted curve.
pat("env:(1,0.01,0.2)") Uses an attack/release envelope for value 1. Attack and release control the rising and falling influence around the event.
pat("adsr:(1,0.01,0.08,0.4,0.2)") Uses attack, decay, sustain and release parameters in that order. Sustain is a level; the other parameters describe envelope timing.

So patFn("g:(0.9,0.18) g:(0.1,0.18)") is a two-bump gesture that repeats every cycle, and a Functions.Fn like any other, which means the whole Functions library applies to it.

From the inside it is a graph node, op = "pat", and every number in the pattern text is one of its leaves.

val gesture = patFn("g:(0.3,0.12) adsr:(0.8,0.01,0.08,0.4,0.2)", cps = 2.0, range = 0.9)
val node = gesture.graphNode()

val report =
    """
op        ${node.op}
source    ${node.strings["source"]}
knobs     ${node.children.joinToString("  ") { it.role }}
values    ${gesture.extractParameters().joinToString("  ")}

${gesture.graphText()}
"""
        .trimIndent()

println(report)
display("<pre>" + report + "</pre>")

The pat node: its op, its spliced source text, its role-tagged knobs and their values

Each atom's numbers are addressed by the role they play (atom0.sigma, atom1.attack) rather than by position, and cps is a knob too. That matters because Field's numeric leaves are exactly what the differentiable fitter tunes. You can hand a rhythm to fitGradient and have it recover the gesture's own numbers.

Below, the target is a two-bump gesture and the seed has the right structure with the wrong numbers: wrong heights, wrong widths. Adam moves the leaves until the curves coincide, and the fitted result comes back out as pattern text you can paste into a box:

with(Functions) {
    val target = patFn("g:(0.9,0.18) g:(0.1,0.18)", cps = 1.0, range = 0.9)
    val seed = patFn("g:(0.7,0.25) g:(0.3,0.12)", cps = 1.0, range = 0.9)

    val run = fitGradient(target, seed, domain = 0.0..2.0, iterations = 200, lr = 0.02, samples = 96)

    // A pat Fn runs to +infinity, so the plot is told which window to sweep.
    plot(target, 0.0..2.0, channel = "1 target", title = "target  g:(0.9,0.18) g:(0.1,0.18)")
    plot(run.fitted, 0.0..2.0, channel = "2 fitted", title = "fitted from a wrong-numbered seed")

    val report =
        """
    seed    ${seed.graphNode().strings["source"]}
    fitted  ${run.fitted.graphNode().strings["source"]}
    RMSE    ${run.initialError.f4} -> ${run.finalError.f4}
    """
            .trimIndent()
    println(report)
    display("<pre>" + report + "</pre>")
}

Two plots, target and fitted, lying on top of each other, above the fitted pattern text and the RMSE before and after

Both heights and both widths come back to three decimal places. What the fit cannot do is re-assign values between atoms: seed it with the two heights swapped and it stalls at about half the initial error, because getting to the answer needs a permutation and there is no downhill path through one. Tuning numbers is fitGradient's job; choosing which atom holds which number is evolve's.

Two smaller things. A number can be a %binding instead of a literal (g:(%loudness,0.2)), resolved from the box's variables each cycle: live, but for that reason not fittable, so such a pattern stays an ordinary opaque function. And because the knobs are graph children, you can graft a signal into one instead of a constant, which gives you a kernel width that drifts across the piece.


Where Field differs from Tidal

Four names to know if you are coming from Tidal:

Field Tidal note
cat fastcat fits every argument into one cycle
slowcat cat one pattern per cycle — what <a b> is built on
within(window, p) compress squeezes a pattern into a window
withinCycle(window, f) within applies f inside a window, leaves the rest

fastcat exists as an alias for cat when you want the code to say which one it means.


Playing a pattern

eventSequence gives you the events in playing order. scheduled(cps) turns them into wall-clock times. respond {} calls you back as each one arrives.

pat("bd sd hh hh").respond(cps = 2.0) { event ->
    println("${event.start}s  ${event.value}  gain=${event.num("gain")}")
}

The three-line respond box in Field's editor. Below it, one printed line per event, an eighth of a second apart: bd, sd, hh, hh, then bd again, each with gain=0.0.

At two cycles a second, four steps land an eighth of a second apart. The onsets are times on Field's shared pattern clock, so they count from when that clock started rather than from 0. The pattern sets no gain, so num("gain") reads 0.

Because a note is scheduled at its onset and carries its whole length, a pattern that crosses bar lines plays correctly without the consumer knowing anything about cycles.


Where to go next

  • The Pattern language starting point in the welcome screen opens the page's boxes (signals, structure choice, parameters, voice leading, groove and mini-notation), each with a live roll and most with audio. Edit any string and re-run.
  • Sound plays patterns through samples and synths.
  • Audio render and analysis renders a pattern to a file and transcribes audio back into one.