# Tilt EQ

> A tilt EQ device: one knob that tilts the tone around a pivot and an output level, a panel that draws the curve, a Web Audio engine and tools for agents.

```tsx
"use client";

import { useState } from "react";
import {
  type TiltEqState,
  tiltEqDevice,
} from "@/components/audio-ui/devices/tilt-eq/device";
import { createTiltEqEngine } from "@/components/audio-ui/devices/tilt-eq/engine";
import { TiltEqPanel } from "@/components/audio-ui/devices/tilt-eq/panel";
import { useDeviceLoop } from "@/hooks/use-device-loop";
import { useMasterBus } from "@/hooks/use-master-bus";

const MASTER_VOLUME = 0.7;

/**
 * A tilt EQ you can plug in: its state, a panel that draws the curve it tilts
 * and an engine that plays it, each its own file and none knowing the others.
 * Play the loop and turn the tilt: brighter one way, darker the other.
 */
export function BlockTiltEq() {
  const [state, setState] = useState<TiltEqState>(() =>
    tiltEqDevice.defaults()
  );
  const master = useMasterBus(MASTER_VOLUME);
  useDeviceLoop({
    create: createTiltEqEngine,
    destination: master,
    state,
  });
  return (
    <div className="rounded-xl h-full w-full min-w-0">
      <TiltEqPanel onValueChange={setState} value={state} />
    </div>
  );
}
```

Makes the whole sound brighter or darker with one knob. It tilts the tone around a pivot: turned up, the highs rise and the lows fall by as much, turned down the other way round. 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/tilt-eq
```

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

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

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

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

import { useState } from "react";
import {
  type TiltEqState,
  tiltEqDevice,
} from "@/components/audio-ui/devices/tilt-eq/device";
import { createTiltEqEngine } from "@/components/audio-ui/devices/tilt-eq/engine";
import { TiltEqPanel } from "@/components/audio-ui/devices/tilt-eq/panel";
import { useDeviceLoop } from "@/hooks/use-device-loop";
import { useMasterBus } from "@/hooks/use-master-bus";

const MASTER_VOLUME = 0.7;

/**
 * A tilt EQ you can plug in: its state, a panel that draws the curve it tilts
 * and an engine that plays it, each its own file and none knowing the others.
 * Play the loop and turn the tilt: brighter one way, darker the other.
 */
export function BlockTiltEq() {
  const [state, setState] = useState<TiltEqState>(() =>
    tiltEqDevice.defaults()
  );
  const master = useMasterBus(MASTER_VOLUME);
  useDeviceLoop({
    create: createTiltEqEngine,
    destination: master,
    state,
  });
  return (
    <div className="rounded-xl h-full w-full min-w-0">
      <TiltEqPanel 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 tilt eq 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. |
| Tilt | -12 dB to 12 dB | 0 dB | How far the tone tilts, 12 dB either way: up raises the highs and lowers the lows, down does the opposite. At 0 the sound is as it was. |
| Pivot | 100 Hz to 10 kHz | 1 kHz | The frequency the tone tilts around, which stays where it was. |
| Output | -12 dB to 12 dB | 0 dB | The level of the output. |

## Panel

The panel draws the curve the tilt plays, with a line at the pivot. `tiltEqCurve(state)` is the curve it draws, from the shelves of `@audio/biquad` as the kernel makes them.

```tsx
import { TiltEqPanel } from "@/components/audio-ui/devices/tilt-eq/panel";
import { tiltEqDevice } from "@/components/audio-ui/devices/tilt-eq/device";

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

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

## Engine

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

The tilt is the kernel of `@audio/eq-tilt` in an `AudioWorklet` loaded from a blob (`TILT_EQ_WORKLET` is its source), 128 frames at a time: a low shelf and a high shelf of `@audio/biquad` at the pivot, with opposite gains and a Q of 0.5, gentler than the fixed slope of a `BiquadFilterNode` shelf, which is why it runs in a worklet. A worklet cannot import, so the device carries both modules as published (`TILT_EQ_KERNELS`, in its `kernels.ts`). A turn of a knob makes new coefficients and keeps the filters' state, so it does not click. Until the worklet is ready the sound passes clean, and `engine.ready` resolves to `false` in a context that cannot run worklets. `tiltEqSettings(state)` is what goes on it: the kernel's own gain raises the lows, so it takes the tilt the other way round.
