The Arrangement Model
A looping pattern is the right shape for live coding, but a finished piece has a form — an intro, a verse, a chorus. Resonon’s arrangement model is the arrangement view of a DAW written as values: a timeline is one lane on a ruler, and a section is a slice of the whole piece — every track’s clips, its automation and its picture under one length. This page explains the rules those values follow, which are chosen so that a song can be edited while it plays without anything being lost.
A timeline is a pattern
Section titled “A timeline is a pattern”A timeline isn’t sound on its own; it is a pattern source. You send it to a track with << like any
other pattern, and when it reaches its end it loops. Each slot queries its pattern one cycle at a
time across the slot’s duration, so a one-cycle figure repeats and a multi-cycle pattern advances
through its own cycles. Any cycle no slot covers is silent — gaps are how breaks and drops are
written, not an oversight.
A section adds the thing a set of timelines can’t guarantee: one length for every lane. Because the length belongs to the section and not to each track’s lane, the tracks cannot drift out of alignment, and a section boundary is a boundary for the whole piece.
The grid is what you snap to
Section titled “The grid is what you snap to”A timeline’s length is a ruler in whole cycles, but nothing is placed by the ruler: a slot may
begin and end anywhere. .at(2.5, 1.25) means exactly that. Positions a musician means stay exact
— a third of a cycle is a third, not 0.3333 — so a run of triplet placements cannot drift.
This matters most for material that was recorded. A take lands where latency compensation measured it, which is almost never on a cycle line; an arrangement that only knew the grid would have to round that measurement away.
A later entry masks, it doesn’t cut
Section titled “A later entry masks, it doesn’t cut”When two entries cover the same cycle, the later one wins — but the entry underneath is masked,
not cut. It is still there, hidden for exactly as long as the mask lasts, and when the mask ends it
resumes in its own phase rather than restarting. show() marks that with an offset:
timeline — 16 cycles, 2 entries layers: base, main, fills 0..4 [bd bd sd bd] main 4..6 [hh hh] fills 6..16 [bd bd sd bd] +6c mainAfter the fill, the groove picks up at its own cycle 6 instead of starting over. The same holds across a section boundary: a fill punched from the end of a verse into a chorus plays through the seam, and the chorus resumes where it would have been.
Nothing is destroyed, which is what makes this safe to live-code. Lay down a groove, punch variations over it, and take them away again without retyping the groove.
A continuous lane needs the edges spelled out. On an automation lane, the curve beneath a mask holds its level right up to the mask edge and steps there; it doesn’t slide towards the mask across the preceding cycles. Clear the mask and the curve underneath comes back exactly as authored.
Writing is an upsert
Section titled “Writing is an upsert”Re-running a line must not stack another copy of what it wrote. A write therefore upserts by
start: editing .at(4, 2) to .at(4, 1) and running it again reshapes that same mask. .clear()
on a position lifts the mask and brings back exactly what it was hiding.
Layers
Section titled “Layers”Masks live on layers, stacked bottom to top:
baseis the structure.arrange()lays sections down here, and a baresong[drums] << clipreplaces it.mainis where a plain.at()write goes.- Named layers —
.layer("fills")— are stacked on top when first written to, and group punches you want to lift together with.mute()and bring back, in phase, with.unmute().
Because the structure and the punches live on different layers, rewriting the song underneath leaves
your punches alone, and .clear() removes what you punched while keeping what arrange() built. A
hand-built timeline() has an empty base — nothing laid a structure down — so every .at() write
lands on main, and .clear() empties it outright.
Only a write creates a layer. .layer("fills") on its own is a name and a target, so naming a layer
that was never written to is an error rather than a silent no-op: a typo fails where you can see it.
The stack order is declarative — .set_layer_order(#[...]) states it, and running the line again
changes nothing.
What you see is what you hear
Section titled “What you see is what you hear”show() renders the resolved stack — one row per audible interval, tagged with the layer that won
it. That is the answer to “what will this sound like”, which the list of writes cannot give once
masks are involved. A muted layer contributes no rows, and an entry that is completely hidden is not
an interval at all.
.entries() answers the other question — “what did I write here” — in authoring order, and flags an
entry that is completely hidden as masked. That is the one thing the resolved view cannot tell you,
and the reason a covered entry stays findable instead of mysterious.
When an edit is heard
Section titled “When an edit is heard”Every lane edit on a playing song lands live, with no second play() and no rewind. “Live” means a
different moment on each kind of lane:
| Lane | Takes effect | Why |
|---|---|---|
Notes (song[drums]) | next cycle boundary | the lane swaps its pattern once per cycle, so notes already sounding are never cut off mid-cycle |
Automation (.volume) | next audio block | the merged curve re-binds and the engine picks it up on its next callback |
Screen (song[screen]) | next frame | the renderer re-selects the covering span every frame |
So a note edit — including .mute() and .clear() on a note layer — is quantized to the cycle, and
you hear the rest of the current cycle first. Automation and visuals are effectively immediate.
Related
Section titled “Related”- Arrange a Song from Sections — the same model, task by task
- Arrange a Song — a guided first arrangement
- Signals & Time — a looping modulator versus a placed automation lane