# PianoRoll

> The headless PianoRoll of @audio-ui/react: its parts, what each renders and owns, and their props.

```bash
npm install @audio-ui/react
```

```ts
import { PianoRoll } from "@audio-ui/react/piano-roll";
```

| Export | Kind | What it is |
| --- | --- | --- |
| `PianoRoll.Key` | function | One key, placed along the keyboard for its pitch. |
| `PianoRoll.KeyLabel` | function | The name of the key it sits in, such as "C4". |
| `PianoRoll.Grid` | function | The surface notes are drawn on: a timeline lane, with the roll's rows stacked down it. |
| `PianoRoll.Keys` | function | The keyboard down the side of the roll: a `Piano` laid out on the roll's rows, low end at the bottom. |
| `PianoRoll.Marquee` | function | The selection rectangle being dragged across the grid; renders nothing the rest of the time. |
| `PianoRoll.Note` | function | One note, placed at its start and pitch: on the timeline's units across and on its row down. |
| `PianoRoll.NoteHandle` | function | One edge of the note it sits in, dragged to change the note's length: the end makes it longer or shorter, the start trims it from the front while its end stays put. |
| `PianoRoll.Notes` | function | Renders `children` once per note, in the order they are read: by start, then from the highest pitch down. |
| `PianoRoll.Root` | function | Groups the parts of a piano roll and owns its notes, its selection and the pitches it draws. |
| `PianoRoll.Row` | function | The band a pitch takes across the grid, for shading black-key rows, the rows of the roll's scale and of its root, or ruling where an octave starts. |
| `PianoRoll.Rows` | function | Renders `children` once per row, highest pitch first: the place to put a `PianoRoll.Row` on the grid, or a `PianoRoll.Key` in the keys. |
| `PianoRoll.SelectionSpan` | function | The time the selected notes cover, from the earliest start to the latest end, as a band from top to bottom of what it sits in: the grid, the velocity lane or the ruler. |
| `PianoRoll.StretchHandle` | function | One edge of the selection's span, for its `SelectionSpan`: a drag stretches the selected notes in time from the other edge, on the grid unless Cmd/Ctrl is held. |
| `PianoRoll.Velocity` | function | One note's velocity, or its chance in a `level="chance"` lane, as a bar from the bottom of the lane: its height is the value, through the `--piano-roll-level` CSS variable. |
| `PianoRoll.VelocityLane` | function | A strip under the grid where each note's velocity, or with `level="chance"` its chance to play, stands as a bar placed at the note's start: a timeline lane, like the grid. |

## Anatomy

Import the registry components for the ready-made look, or compose the primitive's parts yourself.

```tsx
import { PianoRoll, Timeline } from "@audio-ui/react";

<Timeline.Root duration={16} snapStep={1}>
  <PianoRoll.Root>
    <PianoRoll.Keys>
      <PianoRoll.Rows>
        {(pitch) => (
          <PianoRoll.Key pitch={pitch}>
            <PianoRoll.KeyLabel />
          </PianoRoll.Key>
        )}
      </PianoRoll.Rows>
    </PianoRoll.Keys>
    <PianoRoll.Grid aria-label="Notes">
      <PianoRoll.Rows>{(pitch) => <PianoRoll.Row pitch={pitch} />}</PianoRoll.Rows>
      <PianoRoll.SelectionSpan>
        <PianoRoll.StretchHandle edge="start" />
        <PianoRoll.StretchHandle edge="end" />
      </PianoRoll.SelectionSpan>
      <PianoRoll.Notes>
        {(note) => (
          <PianoRoll.Note key={note.id} note={note}>
            <PianoRoll.NoteHandle edge="start" />
            <PianoRoll.NoteHandle edge="end" />
          </PianoRoll.Note>
        )}
      </PianoRoll.Notes>
      <PianoRoll.Marquee />
    </PianoRoll.Grid>
    <PianoRoll.VelocityLane aria-label="Velocity">
      <PianoRoll.Notes>
        {(note) => <PianoRoll.Velocity key={note.id} note={note} />}
      </PianoRoll.Notes>
    </PianoRoll.VelocityLane>
  </PianoRoll.Root>
</Timeline.Root>;
```

## API reference

### Root

Groups the parts of a piano roll and owns its notes and selection. Must be used within a `Timeline`. Renders a `<div>` element.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `Note[]` |  | The notes. |
| `defaultValue` | `Note[]` |  | Uncontrolled starting notes. |
| `selection` | `NoteId[]` |  | The ids of the selected notes. |
| `defaultSelection` | `NoteId[]` |  | Uncontrolled starting selection. |
| `minPitch` | `number` | `48` | Lowest MIDI pitch, inclusive: 60 is middle C. |
| `maxPitch` | `number` | `72` | Highest MIDI pitch, inclusive. |
| `minLength` | `number` |  | The shortest a note can be, in the timeline's units. Defaults to the timeline's grid step. |
| `mode` | `"draw" \| "select"` | `"select"` | What a press on empty grid does: draw a note, or start a selection rectangle (a double-click draws). |
| `drawStyle` | `"paint" \| "stretch"` | `"stretch"` | What a drag does after a press on the grid in draw mode: stretch the note drawn, or paint a note in each grid cell the pointer crosses, on the pitch under it, or with Alt on the first one. |
| `folded` | `boolean \| "scale"` | `false` | Whether only the pitches carrying notes get a row, and with `"scale"` those in `scale` too. A roll with nothing to fold onto keeps them all. |
| `scale` | `RollScale \| null` |  | The scale whose rows are marked `data-in-scale`, as its pitch classes and its root: `{ pitchClasses: [0, 2, 4, 5, 7, 9, 11], root: 0 }` for C major. |
| `activePitches` | `number[]` |  | Pitches lit from elsewhere, such as a MIDI input or the notes playing, on the keys and rows. |
| `disabled` | `boolean` |  |  |
| `createNoteId` | `() => NoteId` |  | Returns the id of a note drawn, added or duplicated. Defaults to `"note-<n>"`, skipping ids in use. |
| `getNoteLabel` | `(note: Note) => string` |  | Returns a note's accessible name. Defaults to its pitch and where it starts and ends, such as "C4, 4 to 6". |
| `onValueChange` | `(value: Note[], eventDetails) => void` |  | Fires while notes are drawn, moved, resized, duplicated or removed. |
| `onValueCommitted` | `(value: Note[], eventDetails) => void` |  | Fires when a gesture that changed the notes ends, and after each edit from the keyboard. |
| `onSelectionChange` | `(selection: NoteId[], eventDetails) => void` |  | Fires when notes are selected or deselected. |
| `onNoteOn` | `(pitch: number, eventDetails) => void` |  | Fires when a pitch should sound: a key pressed, a note drawn, picked up or moved to a new pitch. `eventDetails.cancel()` keeps it silent. |
| `onNoteOff` | `(pitch: number, eventDetails) => void` |  | Fires when a pitch stops sounding. |

**Data attributes**

| Attribute | Description |
| --- | --- |
| `data-disabled` | Present when disabled. |
| `data-editing` | Present while a gesture edits the notes. |
| `data-folded` | Present when folded. |
| `data-has-selection` | Present while notes are selected. |
| `data-mode` | `"draw"` or `"select"`. |

### Keys

The keyboard down the side: a `Piano` laid out on the roll's rows, low end at the bottom. Pressing a key sounds it through the roll's `onNoteOn`; with Shift, pressing it (or Shift + Enter) selects every note on its row, or deselects them when they all are. Put a `PianoRoll.Key` per row inside, with `PianoRoll.Rows`. Renders a `<div>` element.

### Rows

Renders `children` once per row, from the highest pitch down: the place to put a `PianoRoll.Key` or a `PianoRoll.Row`. Renders no element of its own.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `children` | `(pitch: number) => React.ReactNode` |  |  |

### Key and KeyLabel

The same parts as [Piano](/docs/components/base/piano): a key of the keyboard, and its label.

### Grid

The surface notes are drawn on: a timeline lane, with the roll's rows stacked down it. A press on empty grid draws a note (`mode="draw"`), whose end then follows the pointer, or starts a selection rectangle, Shift adding to what is selected; in that mode a double-click draws. Focused, Enter adds a note at the playback position, Escape clears the selection and Cmd/Ctrl+A selects every note. From the grid or a note, Cmd/Ctrl+D repeats the selection right after itself, Cmd/Ctrl+C, X and V copy, cut and paste at the playback position, and Q snaps the starts of the selection (every note, with none selected) to the grid. Renders a `<div>` element.

### Row

The band a pitch takes across the grid, for shading black-key rows, the rows of the roll's scale, or ruling where an octave starts. It lets presses through to the grid and is hidden from assistive technology. Renders a `<div>` element.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `pitch` | `number` |  | The MIDI pitch of the row. |

**Data attributes**

| Attribute | Description |
| --- | --- |
| `data-active` | Whether the pitch is sounding. |
| `data-color` | `"black"` or `"white"`. |
| `data-folded` | Whether the roll is folded. |
| `data-in-scale` | Whether the pitch is in the roll's `scale`. |
| `data-out-of-scale` | Whether the roll has a `scale` and the pitch is not in it. |
| `data-scale-root` | Whether the pitch is the root of the roll's `scale`: a C in C major. |
| `data-octave` | Whether the row is a C, where an octave starts. |

### Notes

Renders `children` once per note, in the order they are read: by start, then from the highest pitch down. That order is also the tab order. Renders no element of its own.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `children` | `(note: Note) => React.ReactNode` |  |  |

### Note

One note, placed on its row from its start, as long as its length. A toggle button named after its pitch and span (`getNoteLabel` on `Root`). Its `--piano-roll-velocity` and `--piano-roll-chance` CSS variables are its velocity and how likely it is to play. Renders a `<div>` element.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `note` | `Note` |  | The note to draw: the roll's copy of it is used once it is edited. |

**Data attributes**

| Attribute | Description |
| --- | --- |
| `data-selected` | Whether the note is in the selection: also `aria-pressed`. |
| `data-muted` | Whether the note is deactivated: it stays on the roll and does not play. |
| `data-disabled` | Present when disabled. |

### NoteHandle

One edge of the note it sits in, dragged to change the note's length: the end makes it longer or shorter, the start trims it from the front while its end stays put. On the grid unless Cmd/Ctrl is held, and for the whole selection when the note is part of it. Shift + Left and Right on the note do the same from the keyboard, so the handle is hidden from assistive technology. Renders a `<div>` element.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `edge` | `"start" \| "end"` |  | The edge of the note this handle drags. |

### Marquee

The selection rectangle being dragged across the grid; renders nothing the rest of the time. Hidden from assistive technology. Renders a `<div>` element.

### SelectionSpan

The time the selected notes cover, from the earliest start to the latest end, as a band from top to bottom of what it sits in: the grid, the velocity lane or the ruler. Renders nothing with no note selected. It lets presses through, all but its `PianoRoll.StretchHandle`s, and is hidden from assistive technology. Renders a `<div>` element.

### StretchHandle

One edge of the selection's span, for its `SelectionSpan`: a drag stretches the selected notes in time from the other edge, on the grid unless Cmd/Ctrl is held. The Notes edits that double and halve lengths do the same from the keyboard, so it is hidden from assistive technology. Renders a `<div>` element.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `edge` | `"start" \| "end"` |  | The edge of the span this handle drags; the other stays put. |

### VelocityLane

A strip under the grid where each note's velocity, or with `level="chance"` its chance to play, stands as a bar placed at the note's start: a timeline lane, like the grid. In draw mode it is a brush: a drag across it sets every bar under the pointer, in the grid cell it is over, to the pointer's height. Put a `PianoRoll.Velocity` per note inside, with `PianoRoll.Notes`. Renders a `<div>` element.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `level` | `"velocity" \| "chance"` | `"velocity"` | What the bars stand for: each note's velocity, or its chance to play. |

**Data attributes**

| Attribute | Description |
| --- | --- |
| `data-level` | `"velocity"` or `"chance"`. |
| `data-mode` | `"draw"` or `"select"`: in draw mode the lane is a brush. |

### Velocity

One note's velocity, or its chance in a `level="chance"` lane, as a bar from the bottom of the lane: its height is the value, through the `--piano-roll-level` CSS variable. Dragging it up or down changes the value of the selection by the same amount when the note is part of it, so a phrase keeps its dynamics; Shift + click toggles the note in the selection. On a velocity bar, Cmd/Ctrl + drag sets how far the velocity may stray each time the note plays, its `--piano-roll-deviation`. A native `<input type="range">` inside takes the arrow keys (Page Up/Down for larger steps, Home/End for none and full) and carries the accessible name, which defaults to the note's. The next drawn note gets the velocity set last. Renders a `<div>` element.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `note` | `Note` |  | The note whose velocity this is. |
| `getAriaValueText` | `(velocity: number) => string` |  | Returns how a velocity, 0…1, is announced. Defaults to a percentage. |

**Data attributes**

| Attribute | Description |
| --- | --- |
| `data-selected` | Whether the note is in the selection. |
| `data-muted` | Whether the note is deactivated. |

## Styled

The registry's styled Piano roll, its usage and interaction: [Piano roll](/docs/components/base/piano-roll).
