# Primitives

> How the headless primitives of @audio-ui/react are put together, styled, controlled and read, the same way for every one of them.

All primitives in `@audio-ui/react` share one structure. The Knob shows it, and the others follow the same pattern.

## Parts

A primitive is a set of parts you assemble. Each part renders one element and does one job: the root owns the value, the control takes the pointer, the thumb takes the focus.

```tsx
import { Knob } from "@audio-ui/react";

<Knob.Root defaultValue={50}>
  <Knob.Label>Cutoff</Knob.Label>
  <Knob.Control>
    <Knob.Track />
    <Knob.Indicator />
    <Knob.Thumb aria-label="Cutoff" />
  </Knob.Control>
  <Knob.Value />
</Knob.Root>
```

Nothing is painted: no colour, size or theme comes from a primitive. Each part sets only the inline styles its behaviour needs (`touch-action` on a drag surface, `position: relative` on a container that holds absolutely placed parts, `pointer-events` on decorations), and the look is yours. Parts you leave out are not rendered, so a knob with no value readout is a knob without `Knob.Value`.

## Style from state

State is on the element, never behind a prop you have to read. Style it with the attribute selectors of your CSS or the variants of your framework:

```tsx
<Knob.Thumb className="cursor-grab data-dragging:cursor-grabbing" />
```

- **Attributes and variables.** A part has `data-disabled`, `data-dragging` and the like when they are true, and no attribute when they are not. What changes too often to be an attribute is a CSS variable, such as a knob mark's angle or a timeline's unit. [Headless styling](/docs/react/headless-styling) lists both for every part.
- **State functions.** `className` and `style` can be functions of the part's state:

```tsx
<Knob.Thumb className={(state) => (state.dragging ? "ring-2" : "ring-0")} />
```

## Render another element

Every part takes a `render` prop to change the element it renders or to compose it with your own component. Its props, ref and handlers are merged with yours, not replaced:

```tsx
<Knob.Control render={<MyDial />} />
<Timeline.Clip render={(props, state) => <article {...props} />} />
```

A handler you pass runs alongside the part's own. A consumer's `onPointerDown` does not switch the drag off.

## Control the value

A primitive is uncontrolled with `defaultValue`, or controlled with `value`. Either way it tells you what changed and why:

```tsx
<Knob.Root
  onValueChange={(value, eventDetails) => {
    if (eventDetails.reason === "wheel") {
      eventDetails.cancel(); // refuse this one
    }
  }}
  onValueCommitted={(value, eventDetails) => save(value)}
  value={cutoff}
/>
```

- `onValueChange` fires on every change, with `eventDetails.reason` (`"drag"`, `"wheel"`, `"keyboard"`, `"reset"`, `"input-change"`...) and `eventDetails.event`, the event behind it. `eventDetails.cancel()` refuses the change: a controlled value stays put, an uncontrolled one does not move.
- `onValueCommitted` fires once, when a gesture that changed the value ends. That is the time to start a note, push to an undo stack or save, not on every move of a drag.
- While a pointer drags, the part follows it even if your controlled value is late, and takes the value back when the pointer is released.

## Live values

A playhead, a meter or a waveform that follows a signal changes sixty times a second, too often to go through React state. Give it a `source` instead, an object with `getSnapshot` and `subscribe`, and the part writes each value straight to the DOM:

```tsx
import { createSource } from "@audio-ui/react";

const position = createSource(0);

// in your audio loop
position.set(audioContext.currentTime);

<Timeline.Root source={position}>
  <Timeline.Lane>
    <Timeline.Playhead />
  </Timeline.Lane>
</Timeline.Root>
```

Your page does not re-render while it plays.

## Accessibility

Each control holds a native `<input type="range">`, visually hidden. It takes the focus, answers the arrow, Page, Home and End keys, announces its value and carries the accessible name, so a screen reader and the keyboard treat it as the slider it is. Give the thumb an `aria-label`, or a `Label` part, and format what is read out with `getAriaValueText` or `format`. Pointer presses do not draw the keyboard focus ring; keyboard focus does.

## Import only what you use

Import from the root, or from a subpath to keep the rest out of your bundle:

```tsx
import { Knob } from "@audio-ui/react/knob";
```

A single primitive weighs a few kilobytes gzipped (a knob about 5 KB, a fader about 3 KB).

## Forms

The parameter controls are form fields. Give one a `name` and its native input submits with the form, so a preset can be a plain `<form>`:

```tsx
<form action={savePreset}>
  <Knob.Root defaultValue={1000} name="cutoff">
    ...
  </Knob.Root>
  <Fader.Root defaultValue={40} max={100} name="gain">
    ...
  </Fader.Root>
  <XYPad.Root name="pad">...</XYPad.Root>
</form>
```

- **Knob and Fader** submit one number under their `name`. The Knob also takes `form` to belong to a form elsewhere in the page.
- **XY Pad** submits two, as `pad.x` and `pad.y`.
- The other primitives (Envelope, Plot, Sequencer, Pad, Piano, PianoRoll, Timeline, Waveform, Meter) hold a value that is not a single number, a curve or a pattern for instance, so they do not submit by themselves: read it from `onValueChange` or `onValueCommitted` and send it as you like.
- A disabled control is left out of the submission, as with any input.

## The package

`@audio-ui/react` holds every primitive, each also its own entry point, `@audio-ui/react/<primitive>`, which ships that primitive alone. Each has its page in this section.

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

```ts
import { createSource, adjacentPoint, Envelope, Fader, Knob, /* … */ } from "@audio-ui/react";
```

- Used by: `arrangement`, `arrangement-engine`, `use-arrangement`, `use-audio-levels`, `use-loudness`, `use-piano-roll-playback`, `use-pitch`, `use-spectrum`, `use-time-selection`, `use-timeline-cursor`

| Export | Kind | What it is |
| --- | --- | --- |
| `createSource` | function | Creates a source you push to yourself, from an audio callback, a WebSocket, a worker or a test: call `set` with each new reading, and whatever reads the source follows. |
| `Source` | interface | A value that changes outside React, in the shape `useSyncExternalStore` expects: `subscribe` registers a listener called on each change, and `getSnapshot` returns the current value. |
| `WritableSource` | interface | A source that also takes values: what `createSource` returns. |
| `adjacentPoint` | function | The nearest of `points` strictly after `from` (`direction` 1) or strictly before it (`direction` -1), or null when there is none that way: what jumping to the next or the previous locator, or cue, needs. |
| `Envelope` | namespace | The Envelope primitive's parts, `Envelope.Root` and the rest. |
| `Fader` | namespace | The Fader primitive's parts, `Fader.Root` and the rest. |
| `Knob` | namespace | The Knob primitive's parts, `Knob.Root` and the rest. |
| `Meter` | namespace | The Meter primitive's parts, `Meter.Root` and the rest. |
| `Pad` | namespace | The Pad primitive's parts, `Pad.Root` and the rest. |
| `Piano` | namespace | The Piano primitive's parts, `Piano.Root` and the rest. |
| `PianoRoll` | namespace | The PianoRoll primitive's parts, `PianoRoll.Root` and the rest. |
| `Plot` | namespace | The Plot primitive's parts, `Plot.Root` and the rest. |
| `Sequencer` | namespace | The Sequencer primitive's parts, `Sequencer.Root` and the rest. |
| `ClipLimits` | interface | What a clip edit keeps a clip within: the timeline's length, the shortest it may be, and its source's length. |
| `ClipValue` | interface | A clip: a stretch of a source (a recording, a pattern) placed on the timeline. |
| `clipEnd` | function | Where the clip ends on the timeline. |
| `copyRange` | function | What the stretch from `from` to `to` holds of `items`: the part of each clip inside it, moved so the stretch starts at 0. |
| `coverClips` | function | What a clip laid on a lane leaves of the clips already there: they are covered, not stacked. |
| `duplicateClip` | function | A copy of the clip placed right after it, or as late as the timeline allows when there is not room for it. |
| `FADE_SHAPES` | const | The fade shapes in the order a control steps through them. |
| `FadeShape` | type | How a fade rises or falls: evenly, quick then slow, slow then quick, or eased at both ends. |
| `fadeGain` | function | The gain a fade of `shape` has `progress` of the way through it, from 0 (silent) to 1 (full): the curve of a fade-in; a fade-out plays it backwards. |
| `insertTime` | function | Opens a stretch `length` long at `at`: everything from there moves later, and a clip across `at` is cut in two around the gap. |
| `moveClip` | function | Moves the clip to begin at `start`, keeping its length and its place in the source. |
| `nextFadeShape` | function | The shape after `shape`, going round, for a control that steps through them. |
| `removeTime` | function | Takes the stretch from `from` to `to` out of the timeline: what is in it goes, and everything after it moves back to close the gap. |
| `setClipFadeIn` | function | Sets the fade-in's length, which cannot run into the fade-out. |
| `setClipFadeOut` | function | Sets the fade-out's length, which cannot run into the fade-in. |
| `setClipFadeShape` | function | Gives the fade at `edge` the shape `shape`. |
| `splitClip` | function | Cuts the clip in two at `at`, a position on the timeline. |
| `Timeline` | namespace | The Timeline primitive's parts, `Timeline.Root` and the rest. |
| `trimClipEnd` | function | Trims the clip's end to `end`, as far as the timeline and the source allow. |
| `trimClipStart` | function | Trims the clip's start to `start`: what is cut off stays in the source, so the clip's content does not move on the timeline. |
| `Waveform` | namespace | The Waveform primitive's parts, `Waveform.Root` and the rest. |
| `XYPad` | namespace | The XYPad primitive's parts, `XYPad.Root` and the rest. |
