---
title: Phaser-Flanger
description: "A phaser and flanger as a device you can plug in: a mode, a rate, a depth, a centre, a delay, a feedback and a mix as a state, a panel that draws the sweep, a Web Audio engine and the tools an agent drives it with."
component: true
base: base
---

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

Sweeps notches through the sound with a slow oscillator: in phaser mode a chain of four all-pass filters around the centre frequency, in flanger mode a very short delay. The panel draws what the oscillator does over two seconds; the knobs the mode does not use are drawn off. 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-phaser
```

</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/phaser-device @audio-ui/phaser-panel @audio-ui/phaser-engine
```

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

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

</Steps>

</TabsContent>

</CodeTabs>

## Usage

```tsx
import { PhaserPanel } from "@/components/audio-ui/devices/phaser/panel";
import { phaserDevice } from "@/components/audio-ui/devices/phaser/device";

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

<PhaserPanel 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. `phaserSettings(state)` gives the numbers that go on the nodes and `phaserSweep(state)` what the panel draws.

## The engine

```ts
import { createPhaserEngine } from "@/components/audio-ui/devices/phaser/engine";

const engine = createPhaserEngine(ctx, state);
source.connect(engine.input);
engine.output.connect(ctx.destination);
engine.update(nextState); // parameters ramp: no click
engine.dispose();
```

Both paths are always built and the mode crossfades them, so switching does not click. One oscillator moves the filters, in cents, and the delay, in seconds, and the flanger's sweep is kept inside its delay so the time never goes negative.

## Synced to the tempo

Switch **Sync** on and the rate is a note value instead of hertz: one cycle of the oscillator for each `1/4`, `1/8t` (a triplet), `1/2d` (dotted) and so on, from 1/32 to a whole note, read against the host's tempo. `1/4` at 120 BPM is 2 Hz, and the rate is held between a cycle in 50 seconds and 40 Hz. The tempo is the host's: give it to the engine with the first state or with any update.

```ts
const engine = createPhaserEngine(ctx, state, { tempo }); // { bpm: 100, beatsPerBar: 4, beatUnit: 4 }
engine.update(nextState, { tempo: { ...tempo, bpm: 120 } }); // the rate follows
engine.update(otherState); // no context: the last tempo holds
```

The panel takes the same tempo as a prop, `tempo`, and draws the oscillator at the rate it really has. With Sync on the rate knob is drawn off, with it off the note list is. The tempo does not come from an agent's tool call: `audioui_phaser_set` can switch Sync on and pick a note, and the host decides the tempo it is read against.

## The tools

```ts
import { deviceTools } from "@audio-ui/core/device";
import { phaserDevice } from "@/components/audio-ui/devices/phaser/device";

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

`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. A choice such as the curve or the mode takes only the values of its options.

## 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 choices are toggle buttons, and everything takes the keyboard.
