# Unlimiter

> An unlimiter device: amount, drive, crest, adaptive and a guarded ceiling, a panel that draws the lift for an attack's size, a Web Audio engine and tools for agents.

```tsx
"use client";

import { useState } from "react";
import {
  type UnlimiterState,
  unlimiterDevice,
} from "@/components/audio-ui/devices/unlimiter/device";
import { createUnlimiterEngine } from "@/components/audio-ui/devices/unlimiter/engine";
import { UnlimiterPanel } from "@/components/audio-ui/devices/unlimiter/panel";
import { useDeviceLoop } from "@/hooks/use-device-loop";
import { useMasterBus } from "@/hooks/use-master-bus";

const MASTER_VOLUME = 0.7;

/**
 * An unlimiter you can plug in: its state, a panel that draws the lift it
 * gives an attack for its size and an engine that plays it, each its own file
 * and none knowing the others. Play the loop: its attacks are lifted back by
 * what they are missing.
 */
export function BlockUnlimiter() {
  const [state, setState] = useState<UnlimiterState>(() =>
    unlimiterDevice.defaults()
  );
  const master = useMasterBus(MASTER_VOLUME);
  useDeviceLoop({
    create: createUnlimiterEngine,
    destination: master,
    state,
  });
  return (
    <div className="rounded-xl h-full w-full min-w-0">
      <UnlimiterPanel onValueChange={setState} value={state} />
    </div>
  );
}
```

Gives back the attacks a limiter flattened. It tells an attack by how far a fast follower of the level rises over a slow one, and lifts it: adaptive, by what it is missing to reach the crest, so a healthy attack is left alone. It plays the de-limiter of [audio.js](https://audio.js.org). 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/unlimiter
```

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

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

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

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

import { useState } from "react";
import {
  type UnlimiterState,
  unlimiterDevice,
} from "@/components/audio-ui/devices/unlimiter/device";
import { createUnlimiterEngine } from "@/components/audio-ui/devices/unlimiter/engine";
import { UnlimiterPanel } from "@/components/audio-ui/devices/unlimiter/panel";
import { useDeviceLoop } from "@/hooks/use-device-loop";
import { useMasterBus } from "@/hooks/use-master-bus";

const MASTER_VOLUME = 0.7;

/**
 * An unlimiter you can plug in: its state, a panel that draws the lift it
 * gives an attack for its size and an engine that plays it, each its own file
 * and none knowing the others. Play the loop: its attacks are lifted back by
 * what they are missing.
 */
export function BlockUnlimiter() {
  const [state, setState] = useState<UnlimiterState>(() =>
    unlimiterDevice.defaults()
  );
  const master = useMasterBus(MASTER_VOLUME);
  useDeviceLoop({
    create: createUnlimiterEngine,
    destination: master,
    state,
  });
  return (
    <div className="rounded-xl h-full w-full min-w-0">
      <UnlimiterPanel 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 unlimiter 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. |
| Amount | 0 dB to 18 dB | 6 dB | The most an attack is lifted. |
| Drive | 0 to 10 | 1 | How much an attack is lifted. Adaptive, 1 lifts it back to the crest; otherwise, the dB of lift for each dB of the attack's size. |
| Crest | 3 dB to 25 dB | 10 dB | How far a healthy attack rises over the sound around it. Adaptive, an attack is lifted by what it is missing to reach it. |
| Ceiling | -12 dB to 0 dB | -1 dB | The level the lifted peaks are kept under, with Guard on. |
| Adaptive | On or off | On | Lifts each attack by what it is missing, so a healthy one is left alone. Off, every attack is lifted in proportion to its size: a transient exaggerator. |
| Guard | On or off | Off | Keeps the lifted peaks under the ceiling. Off, they can go over 0 dB. |

## Panel

The panel draws the lift an attack gets for its size, how far the fast follower is over the slow one, through the package's own curves, with the crest when it is adaptive.

```tsx
import { UnlimiterPanel } from "@/components/audio-ui/devices/unlimiter/panel";
import { unlimiterDevice } from "@/components/audio-ui/devices/unlimiter/device";

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

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

## Engine

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

The browser has no such node. The engine plays the processor of `@audio/dynamics-unlimit`'s manifest as published, in an `AudioWorklet` fed only while it plays: off, or until the worklet is ready, the sound goes round it. Every value on the panel changes as it plays; its followers' times are the package's. It looks at no sound ahead, so it adds no latency.

Measured on a loop of hits pushed 12 dB into a limiter, which took their crest from 11.6 to 9.9 dB, the adaptive unlimiter gave it back to 11.7 dB. On the loop as it was, it added 0.9 dB.
