# useTransportClock

> A look-ahead transport clock on the audio clock: it calls back with the audio time of each step so notes are scheduled sample-accurately, with a loop region, seek and tempo changes.

```bash
npx shadcn@latest add @audio-ui/use-transport-clock
```

```ts
import { useTransportClock } from "@/hooks/use-transport-clock";
```

- Files: `hooks/use-transport-clock.ts`
- Depends on: `web-audio`; npm: `@audio-ui/engine`
- Used by: `sequencer`, `use-piano-roll-playback`, `use-playback-clock`, `use-simulated-clock`

| Export | Kind | What it is |
| --- | --- | --- |
| `TransportRegion` | interface |  |
| `UseTransportClockOptions` | interface |  |
| `UseTransportClockReturn` | interface |  |
| `useTransportClock` | function | The region-aware {@link https://web.dev/audio-scheduling | look-ahead} transport clock, the seam between musical time (tempo, steps, loop region, seek) and Web Audio scheduling. |

## Usage

A timer in JavaScript drifts and stalls when the page is busy or in the background; the audio clock does not. `useTransportClock` looks a little ahead on the audio clock and calls you back with the exact audio time of each step, so you schedule the sound at that time and it plays on the beat whatever the page is doing.

```tsx
import { useTransportClock } from "@/hooks/use-transport-clock";

const clock = useTransportClock({
  bpm: 120,
  stepsPerBeat: 4, // a step is a sixteenth note
  totalSteps: 64, // four bars
  region: loop, // { start, end } in steps, or null for all of it
  onStep: (step, time) => {
    for (const note of notesAt(step)) {
      voices.triggerNote(note.pitch, envelope, note.velocity, time);
    }
  },
  onReset: () => voices.releaseAll(),
});

<Button onClick={clock.isPlaying ? clock.stop : clock.start}>Play</Button>;
```

The clock knows when, never what: `onStep` decides what plays. `onReset` runs on every start, stop, seek and loop change, to drop what was already scheduled ahead. For a playhead, read `getPosition()` in an animation frame and push it to a [source](/docs/guides#your-own-engine): it gives fractional steps, so the playhead glides between them.

The [piano roll](/docs/components/base/piano-roll#playing-the-notes) plays its notes with this clock through `usePianoRollPlayback`, which is a good example to copy.

**Options**

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `totalSteps` | `number` |  | How many steps the grid has. |
| `bpm` | `number` | `120` | The tempo. A change keeps the step that plays and carries on at the new speed. |
| `stepsPerBeat` | `number` | `4` | Steps in a beat. |
| `region` | `{ start, end } \| null` |  | The steps the clock loops over, read on every poll. Unset, the whole grid. |
| `onStep` | `(step, time) => void` |  | Called ahead of each step with its audio time: start your sounds at `time`. |
| `onReset` | `() => void` |  | Called when playback jumps or stops: release what is scheduled. |

**Returns**

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `start, stop` | `() => void` |  | Play from the start of the region, or stop. |
| `isPlaying` | `boolean` |  | Whether it plays. |
| `reanchor` | `(step?) => void` |  | Plays on from `step` now (the start of the region when left out): seeking, or hearing a moved loop at once. No-op while stopped. |
| `getPosition` | `() => number` |  | Where it is, in fractional steps. |
| `secondsPerStep` | `number` |  | A step's length at the current tempo, to place a note's end. |

The scheduling itself is plain functions in `@audio-ui/engine/time` (`advance`, `anchoredCursor`, `positionAt`, `SCHEDULE_AHEAD_SECONDS`): the hook only connects them to React and the audio context, so an engine outside React uses them directly.
