---
title: Pocket Synth
description: "A synth as a device you can plug in: a waveform, a filter and a volume as a state, a panel with a pad that plays it, a Web Audio engine and the tools an agent plays it with."
component: true
base: base
---

<ComponentPreview styleName="base-nova" name="block-pocket-synth" />

One oscillator through a resonant filter. Press the pad: pitch goes across, the filter cutoff up. It is a device like the [compressor](/docs/components/compressor): a state, a panel that draws it, an engine that plays it and tools an agent calls, each its own file.

## Installation

<CodeTabs>

<TabsList>
  <TabsTrigger value="cli">CLI</TabsTrigger>
  <TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>

<TabsContent value="cli">

```bash component
npx shadcn@latest add @audio-ui/block-pocket-synth
```

</TabsContent>

<TabsContent value="manual">

<Steps>

<Step>Install the packages:</Step>

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

<Step>Add the pieces you want. All of them, or only the state and the panel if your engine is elsewhere:</Step>

```bash
npx shadcn@latest add @audio-ui/pocket-synth-device @audio-ui/pocket-synth-panel @audio-ui/pocket-synth-engine
```

<Step>Copy and paste the demo that wires them, if you want it.</Step>

<ComponentSource
  styleName="base-nova"
  name="block-pocket-synth"
  title="components/examples/block-pocket-synth.tsx"
/>

</Steps>

</TabsContent>

</CodeTabs>

## Usage

```tsx
import { PocketSynthPanel } from "@/components/audio-ui/devices/pocket-synth/panel";
import { pocketSynthDevice } from "@/components/audio-ui/devices/pocket-synth/device";

const [state, setState] = useState(() => pocketSynthDevice.defaults());

<PocketSynthPanel onValueChange={setState} value={state} />;
```

The state is a plain object keyed by parameter id, so a host stores it, saves it with its project and sends it where it likes. `pocketSynthPitch(position)` and `pocketSynthCutoff(position)` turn a pad position from 0 to 1 into hertz.

## The engine

```ts
import { createPocketSynthEngine } from "@/components/audio-ui/devices/pocket-synth/engine";

const engine = createPocketSynthEngine(ctx, state);
engine.output.connect(ctx.destination);
engine.noteOn(note);  // starts a note, or glides the one being held
engine.noteOff();     // lets it go
engine.update(nextState);
engine.dispose();
```

One voice at a time: `noteOn` starts a note, or glides the one being held to the new one, and `noteOff` releases it. The wave and the filter type of a state change under a held note.

## The tools

```ts
import { deviceTools } from "@audio-ui/core/device";
import { pocketSynthDevice } from "@/components/audio-ui/devices/pocket-synth/device";

const [set, reset, play] = deviceTools(pocketSynthDevice);
```

`set` and `reset` change the state, with the ranges the device enforces: a value out of range is brought in and reported, a wrong one is refused by name, and a call never throws.

`play` plays a note: its call gives the parts of the note (a pitch, and a cutoff) and how long it lasts, and returns the note and the duration for the host to give to the engine.

```ts
import { playNote } from "@audio-ui/core/device";

const result = play.run(state, input);
if (result.note) playNote(engine, result.note, result.duration ?? 0.5);
```

## Accessibility

Every control is a native input: the knobs are sliders named after their parameter, with the value and unit as the text value, the switch is a toggle button, and everything takes the keyboard.
