# Wah

> A wah device: an envelope, sweep or pedal mode, the band's frequency and resonance, an envelope's range and timing, an oscillator's rate and depth, a mix and an output, a panel that draws the band, a Web Audio engine and tools for agents.

```tsx
"use client";

import { useState } from "react";
import {
  type WahState,
  wahDevice,
} from "@/components/audio-ui/devices/wah/device";
import { createWahEngine } from "@/components/audio-ui/devices/wah/engine";
import { WahPanel } from "@/components/audio-ui/devices/wah/panel";
import { useDeviceLoop } from "@/hooks/use-device-loop";
import { useMasterBus } from "@/hooks/use-master-bus";

const MASTER_VOLUME = 0.7;

/**
 * A wah you can plug in: its state, a panel that draws the band and its
 * travel and an engine that plays it, each its own file and none knowing the
 * others. Play the loop: the louder the hit, the higher the band.
 */
export function BlockWah() {
  const [state, setState] = useState<WahState>(() => wahDevice.defaults());
  const master = useMasterBus(MASTER_VOLUME);
  useDeviceLoop({
    create: createWahEngine,
    destination: master,
    state,
  });
  return (
    <div className="rounded-xl h-full w-full min-w-0">
      <WahPanel onValueChange={setState} value={state} />
    </div>
  );
}
```

Sweeps a resonant band through the sound. In Envelope mode the sound's own loudness moves it, the classic auto-wah: the harder a note is played, the higher the band. In Sweep mode a slow oscillator moves it. In Pedal mode it stays where it is set, a wah pedal held at one place, for a host to move by automation. It is a [device](/docs/devices): a state, a panel, an engine and tools, each its own file.

## Installation

**CLI**

```bash
npx shadcn@latest add @audio-ui/wah
```

It installs the device's state, its engine and its panel to `components/audio-ui/devices/wah/`. The demo above, which wires them to a loop, is `@audio-ui/block-wah`.

**Manual**

**1. Install the packages:**

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

**2. Add the pieces you want. All of them, or only the state and the panel if your engine is elsewhere:**

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

**3. Copy and paste the demo that wires them, if you want it.**

```tsx title="components/examples/block-wah.tsx"
"use client";

import { useState } from "react";
import {
  type WahState,
  wahDevice,
} from "@/components/audio-ui/devices/wah/device";
import { createWahEngine } from "@/components/audio-ui/devices/wah/engine";
import { WahPanel } from "@/components/audio-ui/devices/wah/panel";
import { useDeviceLoop } from "@/hooks/use-device-loop";
import { useMasterBus } from "@/hooks/use-master-bus";

const MASTER_VOLUME = 0.7;

/**
 * A wah you can plug in: its state, a panel that draws the band and its
 * travel and an engine that plays it, each its own file and none knowing the
 * others. Play the loop: the louder the hit, the higher the band.
 */
export function BlockWah() {
  const [state, setState] = useState<WahState>(() => wahDevice.defaults());
  const master = useMasterBus(MASTER_VOLUME);
  useDeviceLoop({
    create: createWahEngine,
    destination: master,
    state,
  });
  return (
    <div className="rounded-xl h-full w-full min-w-0">
      <WahPanel onValueChange={setState} value={state} />
    </div>
  );
}
```

**Agent**

Give your agent the skill, and it picks the component, installs it and wires it to your audio:

```bash
npx skills add ouestlabs/audio-ui
```

Then ask it:

```text
Add the wah from audio/ui to my project and wire it to my audio code.
```

To let the agent read the docs live, connect the [MCP server](/docs/mcp). More in [Agent Skill](/docs/agents).

## Controls

| Control | Range | Default | What it does |
| --- | --- | --- | --- |
| On | On or off | On | Turns the device on. Off, the signal passes through unchanged. |
| Mode | Envelope, Sweep, Pedal | Envelope | What moves the band, picked in the title bar: the envelope of the sound, an oscillator, or nothing. The controls the mode does not use are drawn off. |
| Frequency | 50 Hz to 5 kHz | 400 Hz | Where the band is: the bottom of its travel in Envelope mode, the middle of the sweep in Sweep mode, the pedal's place in Pedal mode. |
| Range | 100 Hz to 6 kHz | 3 kHz | How far above the frequency the loudest sound lifts the band, in Envelope mode. |
| Sensitivity | 0.5× to 10× | 2× | How much of the sound's level drives the envelope: higher, quieter playing reaches the top too. |
| Attack | 0.5 ms to 50 ms | 2 ms | How fast the band rises when the sound gets louder. |
| Release | 10 ms to 1000 ms | 100 ms | How fast it falls back when the sound fades. |
| Rate | 0.05 Hz to 10.00 Hz | 1.50 Hz | How fast the oscillator sweeps, in Sweep mode. |
| Sync | On or off | Off | Takes the rate from a note value and the tempo of the host. See [Tempo sync](/docs/devices#tempo-sync). |
| Note | 1/32 triplet, 1/32, 1/32 dotted, 1/16 triplet, 1/16, 1/16 dotted, 1/8 triplet, 1/8, 1/8 dotted, 1/4 triplet, 1/4, 1/4 dotted, 1/2 triplet, 1/2, 1/2 dotted, 1/1 triplet, 1/1, 1/1 dotted | 1/4 | The note value of one cycle of the oscillator. |
| Depth | 0.00 oct to 3.00 oct | 0.80 oct | How far the oscillator sweeps either side of the frequency, in octaves. |
| Resonance | 0.5 to 20 | 5 | How narrow and how loud the band is, its Q: at 5 it stands 14 dB above the rest. |
| Mix | 0 % to 100 % | 100 % | The balance of the band and the clean sound: a wah is the band alone, at 100 %. |
| Output | -24 dB to 12 dB | 0 dB | The level of the output, to bring a narrow band back to the level of the sound. |

## Panel

The panel draws the band where it rests, and dashed where its travel ends: the top of the envelope's lift, or both ends of the sweep. The mode is picked in its title bar. `wahCurve(state, bandHz)` is a curve it draws, the band at a frequency mixed with the clean sound, and `wahReach(state)` where the band rests and how far it goes.

```tsx
import { WahPanel } from "@/components/audio-ui/devices/wah/panel";
import { wahDevice } from "@/components/audio-ui/devices/wah/device";

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

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

## Engine

Create it, connect it and update it as for any device: see [The engine](/docs/devices#the-engine).

The wah runs on the audio thread: the auto-wah of `@audio/effect-autowah` and the wah of `@audio/effect-wah` in an `AudioWorklet` loaded from a blob (`WAH_WORKLET` is its source), 128 frames at a time. Both are the same band-pass, a trapezoidal state-variable filter stable at any resonance, so every mode plays at the same level. The worklet plays all three modes, one output each, and the mode fades one in and the others out, so changing it does not click. A worklet cannot import, so the device carries both kernels as published (`WAH_KERNELS`, in its `kernels.ts`). Until it is ready, and in a context with no worklets, the sound passes clean: `ready` resolves once it is in the graph.
