# Composing on the grid

You have a grid, a key, a clock. Time to fill it with notes. The Sequencer's edit surface has two tools (Paint and Select), per-step velocity, per-step hold (gate length), a Chord Palette, and the Suggest Progression flow. Plus undo / redo so you can experiment freely.

## Two tools, one toggle

Flip between Paint and Select with the footer toggle (or the keyboard shortcut shown in the tooltip).

### Paint

- **Click a cell** → add a note at that pitch + step.
- **Click a filled cell** → remove it.
- **Vertical drag while painting** → set velocity. The cell tints based on velocity (faint = low, saturated = high).
- **Cmd/Ctrl-drag horizontally** → **smear**. Copies the source cell into every REST cell across the drag range. Already-filled cells are left alone (smear never overwrites — clear explicitly first if needed).

> 🔘 **MCP**: `sequencer.step.set { step, note? | chord? | rest: true | clear: true, velocity?, track_id?, instance_id? }` for a single cell. `sequencer.step.smear { from, to, track_id? }` for the smear gesture. `sequencer.step.duplicate { step, track_id? }` for "same as previous step".

### Select

- **Click a note** → select it.
- **Shift-click** → extend the selection.
- **Arrow keys** — move selection up / down (±1 semitone), Shift+Up / Down (± octave).
- **Escape** — clear selection.

The Suggest-Progression flow auto-selects the inserted notes so the next gesture (e.g. arrow-down) shifts them all together. Same for chord-palette drops.

> 🔘 **MCP**: `sequencer.notes.transpose { semitones: -12..12, steps?: [indices], track_id? }`. Pass `steps` to limit the move to specific step indices (e.g. just the steps a progression filled); omit to transpose every note in the track. The move is clamped to the C0..C8 grid range so chords keep their shape and just stop at the edge instead of collapsing voices.

## Per-step velocity

Every step carries a velocity 1..127, defaulting to 100. Set via:

- **Paint-drag**: vertical drag while painting.
- **Right-click → Velocity** preset (Quiet / Medium / Loud / Accent / Custom).
- **MCP**: `sequencer.step.set { velocity }` or `sequencer.step.gate.set` (see below) for already-placed steps.

Velocity rides through the dispatch path to the receiver's `[midi:keyon]` callback, so a velocity-sensitive Faust DSP (`gain = hslider("gain[midi:keyon]", 0.5, 0, 1, 0.01)`) responds correctly.

## Per-step hold (gate length)

Each painted step has an optional **hold multiplier** — how long the note rings relative to the step duration:

| Hold | Behaviour |
|---|---|
| `0.25` | Staccato — very short. |
| `1.0` | Default — release at the next step boundary. |
| `2.0` | Tied — rings into the next step. |
| `4.0` | Held one beat (4 × 16th). |
| `16.0` | Held a full bar. |
| `> pattern length` | Legato across the wrap. Same pitch on the next loop extends the held note instead of retriggering. |

Set via right-click → Hold preset (Staccato / Default / Tied / Beat / Half-bar / Whole bar / Custom) or:

> 🔘 **MCP**: `sequencer.step.gate.set { step, hold, track_id? }` for an already-placed step. To author hold inline during step creation, use `sequencer.step.set { note, hold }`.

The per-step length / hold flows through the seq-clock to the dispatch path so the gate-off event lands at the right sample.

## Per-step **length** (pattern-level)

Distinct from hold: the **step's WALL-CLOCK duration**. Defaults to 16th-note (one tick of the master clock). Can be overridden per-step, but the override is **pattern-level** — every track's column-N advances on the SAME wall-clock, by construction of the single seq-clock.

> 🔘 **MCP**: `sequencer.step.length.set { step, ms: 250 }` to override step `step`'s duration; `ms: null` reverts to default. `sequencer.step.length.clear-all` resets every step's length override in one shot.

Useful for: triplet feels (set every third step to a longer ms), swing (alternate ms on odd / even steps), polymeter trickery.

## The diatonic Chord Palette

To the right of the key/scale picker, the footer shows a row of chord buttons — the diatonic chords for the current key + scale. For A minor: `i (Am)`, `ii° (Bdim)`, `III (C)`, `iv (Dm)`, `v (Em)`, `VI (F)`, `VII (G)`.

Click a chord button → the next grid click drops a 3-note stack (root + 3rd + 5th) on that step column. Velocity carries; drag-velocity works on the whole stack at once.

> 🔘 **MCP**: chord stacks land via `sequencer.step.set { ..., chord: [<midi>, <midi>, <midi>] }`. The diatonic palette is purely UI sugar over that surface — from MCP you'd hand-build the chord array.

## Suggest Progression

Click the lightbulb on a track chip (or the footer's Suggest button). A KB-search menu opens against the curated 8-progression theory corpus:

- `axis-major`, `axis-minor` — the Hooktheory "axis".
- `i-iv-v-major`, `i-vi-iv-v` — pop classics.
- `ii-v-i` — jazz turnaround.
- `minor-pop-loop`, `folk-minor`.
- `twelve-bar-blues`.

Free-text search is German + English aliased: "Pop-Schleife", "Sad Axis", "12 Bar Blues" all land. Pick a progression + a **distribution**:

- `bar` (default) — 1 chord per 4 steps. Max 4 chords on a 16-step pattern; longer progressions truncate with a note.
- `half-bar` — 1 chord per 2 steps. Max 8 chords.

Hitting a result drops the progression onto the active track. Existing steps at the target indices are replaced; other steps are left alone.

```yaml
actions:
  - action_id: surface.open
    input: { surface: sequencer-default#roll }
    label: "▶ 0. Open the Sequencer (watch the grid fill)"
  - action_id: sequencer.progressions.list
    input: {}
    save_as: progs
    label: "▶ 1. List documented progressions"
  - action_id: sequencer.key.set
    input: { tonic_name: A, scale_id: aeolian }
    label: "▶ 2. Set key to A minor"
  - action_id: sequencer.track.add
    input: { name: "Progression demo" }
    save_as: t
    label: "▶ 3. Add a demo track (leaves your tracks alone)"
  - action_id: sequencer.progression.insert
    input:
      progression_id: axis-minor
      distribution: bar
      track_id: "{{t.track_id}}"
    label: "▶ 4. Drop the axis-minor progression on it"
```

> 🟦 **HANDS-FREE PATH**: step 1 enumerates the corpus; step 2 sets the key the progression transposes to; steps 3–4 drop the chords onto a fresh demo track. With more than one track present, `sequencer.progression.insert` REQUIRES an explicit `track_id` (targeting convention — no silent "whatever is active" writes), which is why the chain creates its own target.

> 🔘 **MCP**: `sequencer.progression.insert { progression_id, distribution, key?, track_id? }`. `key` optionally transposes to a chromatic tonic (`"C" | "C#" | ... | "B"`); omit to keep the canonical voicing from the progression's example key (axis-major is in C, axis-minor in A, etc.).

## Clearing the grid

- **Right-click chip → Clear** for one track.
- **MCP** `sequencer.clear { track_id?, instance_id? }` — resets every step on the active (or specified) track to rest.

## Undo / Redo

Snapshot-based. Every meaningful edit (step set, track add, track delete, pattern-length change) commits a snapshot. Cmd/Ctrl+Z = undo, Cmd/Ctrl+Shift+Z = redo. Stack capped at ~100 entries per session.

> 🔘 **MCP**: `sequencer.undo {}` and `sequencer.redo {}`. Both return `"No history"` when the stack is empty. Optional `instance_id` — undo is per-instance.

NOT captured: cursor position (not a state mutation), playback transitions (transient), velocity-drag in-progress (commits on release).

Next section: patterns + scenes — the live-arrangement model that lets you launch sections without composing inline.
