# Frequency Shifter

> A frequency shifter device: a shift or ring mode, the shift in hertz, the ring's frequency, a precision and a mix, a panel that draws what it does to a note, a Web Audio engine and tools for agents.

```tsx
"use client";

import { useState } from "react";
import {
  type FrequencyShifterState,
  frequencyShifterDevice,
} from "@/components/audio-ui/devices/frequency-shifter/device";
import { createFrequencyShifterEngine } from "@/components/audio-ui/devices/frequency-shifter/engine";
import { FrequencyShifterPanel } from "@/components/audio-ui/devices/frequency-shifter/panel";
import { useDeviceLoop } from "@/hooks/use-device-loop";
import { useMasterBus } from "@/hooks/use-master-bus";

const MASTER_VOLUME = 0.7;

/**
 * A frequency shifter you can plug in: its state, a panel that draws what it
 * does to a note and its harmonics and an engine that plays it, each its own
 * file and none knowing the others. Play the loop and turn the shift.
 */
export function BlockFrequencyShifter() {
  const [state, setState] = useState<FrequencyShifterState>(() =>
    frequencyShifterDevice.defaults()
  );
  const master = useMasterBus(MASTER_VOLUME);
  useDeviceLoop({
    create: createFrequencyShifterEngine,
    destination: master,
    state,
  });
  return (
    <div className="rounded-xl h-full w-full min-w-0">
      <FrequencyShifterPanel onValueChange={setState} value={state} />
    </div>
  );
}
```

Moves every frequency of the sound by the same number of hertz. A pitch shift multiplies frequencies and keeps a note's harmonics in line; a frequency shift adds to them, so they stop lining up: a few hertz make a slow phasing, more make the sound metallic and strange. In Ring mode it multiplies the sound by a sine instead, leaving the sum and the difference of each frequency and the sine's, the bell of a ring modulator. 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/frequency-shifter
```

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

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

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

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

import { useState } from "react";
import {
  type FrequencyShifterState,
  frequencyShifterDevice,
} from "@/components/audio-ui/devices/frequency-shifter/device";
import { createFrequencyShifterEngine } from "@/components/audio-ui/devices/frequency-shifter/engine";
import { FrequencyShifterPanel } from "@/components/audio-ui/devices/frequency-shifter/panel";
import { useDeviceLoop } from "@/hooks/use-device-loop";
import { useMasterBus } from "@/hooks/use-master-bus";

const MASTER_VOLUME = 0.7;

/**
 * A frequency shifter you can plug in: its state, a panel that draws what it
 * does to a note and its harmonics and an engine that plays it, each its own
 * file and none knowing the others. Play the loop and turn the shift.
 */
export function BlockFrequencyShifter() {
  const [state, setState] = useState<FrequencyShifterState>(() =>
    frequencyShifterDevice.defaults()
  );
  const master = useMasterBus(MASTER_VOLUME);
  useDeviceLoop({
    create: createFrequencyShifterEngine,
    destination: master,
    state,
  });
  return (
    <div className="rounded-xl h-full w-full min-w-0">
      <FrequencyShifterPanel 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 frequency shifter 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 | Shift, Ring | Shift | Shift moves every frequency, Ring multiplies the sound by a sine. It is picked in the title bar, and the controls the mode does not use are drawn off. |
| Shift | -2000 Hz to 2000 Hz | 100 Hz | How far every frequency moves, up or down. One pushed below 0 comes back up as its mirror. |
| Frequency | 1 Hz to 5 kHz | 440 Hz | The frequency of the ring's sine. Under about 20 Hz it is a tremolo; above, a ring. |
| Precision | Low, Mid, High | Mid | How long the shifter's filter is: High keeps low notes clean, and delays the sound by 2.6 ms; Low delays it by 0.2 ms, and blurs the lows. A new precision fades in. |
| Mix | 0 % to 100 % | 100 % | The balance of the shifted sound and the clean one. |

## Panel

The panel draws what the device does to a 220 Hz note and its harmonics: the frequencies that go in, faint, and the ones that come out. `frequencyShifterPartials(state)` is what it draws.

```tsx
import { FrequencyShifterPanel } from "@/components/audio-ui/devices/frequency-shifter/panel";
import { frequencyShifterDevice } from "@/components/audio-ui/devices/frequency-shifter/device";

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

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

## Engine

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

The shifter runs on the audio thread: the frequency shifter of `@audio/effect-freqshift`, a Hilbert filter that turns the sound into its analytic signal and turns it like a wheel, and the ring modulator of `@audio/effect-ringmod`, in an `AudioWorklet` loaded from a blob (`FREQUENCY_SHIFTER_WORKLET` is its source), 128 frames at a time. Each mixes the clean sound in itself, the shifter's delayed as long as its filter, so the mix never combs. The worklet plays both modes, one output each, and the mode fades one in and the other out. A worklet cannot import, so the device carries both kernels as published (`FREQUENCY_SHIFTER_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.

The shift delays the sound by half its filter's length, which is the [latency](/docs/devices#latency) of the device: 32 samples at Mid. The ring has none.
