Skip to content

Chorus

The Chorus component adds depth and thickness to audio by creating multiple delayed copies with slight pitch variations.

Props

PropTypeDefaultDescription
inputModStreamRefRequiredAudio signal to process
outputModStreamRefRequiredProcessed audio output
labelstring'chorus'Label for the component in metadata
ratenumber1.5LFO rate in Hz (controlled or initial value)
onRateChange(rate: number) => void-Callback when rate changes
depthnumber0.002Modulation depth (controlled or initial value)
onDepthChange(depth: number) => void-Callback when depth changes
delaynumber0.02Base delay time in seconds (controlled or initial value)
onDelayChange(delay: number) => void-Callback when delay changes
wetnumber0.5Wet/dry mix 0-1 (controlled or initial value)
onWetChange(wet: number) => void-Callback when wet/dry mix changes
childrenfunction-Render prop function receiving control props

Render Props

When using the children render prop, the following controls are provided:

PropertyTypeDescription
ratenumberCurrent LFO rate in Hz
setRate(value: number) => voidUpdate the rate
depthnumberCurrent modulation depth
setDepth(value: number) => voidUpdate the depth
delaynumberCurrent base delay time
setDelay(value: number) => voidUpdate the delay
wetnumberCurrent wet/dry mix
setWet(value: number) => voidUpdate the wet/dry mix
isActivebooleanWhether the effect is active

Usage

Basic Usage

tsx
import { ToneGenerator, Chorus, Monitor } from '@mode-7/mod';
import { useRef } from 'react';

function App() {
  const toneOut = useRef(null);
  const chorusOut = useRef(null);

  return (
    <>
      <ToneGenerator output={toneOut} />
      <Chorus input={toneOut} output={chorusOut} />
      <Monitor input={chorusOut} />
    </>
  );
}

With Render Props (UI Controls)

tsx
import { Chorus } from '@mode-7/mod';
import { useRef } from 'react';

function App() {
  const inputRef = useRef(null);
  const outputRef = useRef(null);

  return (
    <Chorus input={inputRef} output={outputRef}>
      {({ rate, setRate, depth, setDepth, delay, setDelay, wet, setWet }) => (
        <div>
          <div>
            <label>Rate: {rate.toFixed(2)} Hz</label>
            <input
              type="range"
              min="0.1"
              max="5"
              step="0.1"
              value={rate}
              onChange={(e) => setRate(Number(e.target.value))}
            />
          </div>
          <div>
            <label>Depth: {(depth * 1000).toFixed(1)} ms</label>
            <input
              type="range"
              min="0.001"
              max="0.01"
              step="0.0001"
              value={depth}
              onChange={(e) => setDepth(Number(e.target.value))}
            />
          </div>
          <div>
            <label>Delay: {(delay * 1000).toFixed(0)} ms</label>
            <input
              type="range"
              min="0.01"
              max="0.05"
              step="0.001"
              value={delay}
              onChange={(e) => setDelay(Number(e.target.value))}
            />
          </div>
          <div>
            <label>Mix: {(wet * 100).toFixed(0)}%</label>
            <input
              type="range"
              min="0"
              max="1"
              step="0.01"
              value={wet}
              onChange={(e) => setWet(Number(e.target.value))}
            />
          </div>
        </div>
      )}
    </Chorus>
  );
}

Controlled Props

tsx
import { ToneGenerator, Chorus, Monitor } from '@mode-7/mod';
import { useState, useRef } from 'react';

function App() {
  const toneOut = useRef(null);
  const outputRef = useRef(null);
  const [rate, setRate] = useState(1.5);
  const [depth, setDepth] = useState(0.002);
  const [delay, setDelay] = useState(0.02);
  const [wet, setWet] = useState(0.5);

  return (
    <>
      <ToneGenerator output={toneOut} />
      <Chorus
        input={toneOut}
        output={outputRef}
        rate={rate}
        onRateChange={setRate}
        depth={depth}
        onDepthChange={setDepth}
        delay={delay}
        onDelayChange={setDelay}
        wet={wet}
        onWetChange={setWet}
      />
      <Monitor input={outputRef} />
    </>
  );
}

Imperative Refs

tsx
import { ToneGenerator, Chorus, ChorusHandle, Monitor } from '@mode-7/mod';
import { useRef, useEffect } from 'react';

function App() {
  const chorusRef = useRef<ChorusHandle>(null);
  const toneOut = useRef(null);
  const outputRef = useRef(null);

  useEffect(() => {
    // Access current state
    if (chorusRef.current) {
      const state = chorusRef.current.getState();
      console.log('Rate:', state.rate);
      console.log('Depth:', state.depth);
      console.log('Delay:', state.delay);
      console.log('Wet:', state.wet);
    }
  }, []);

  return (
    <>
      <ToneGenerator output={toneOut} />
      <Chorus ref={chorusRef} input={toneOut} output={outputRef} />
      <Monitor input={outputRef} />
    </>
  );
}

Note: The imperative handle provides read-only access via getState(). To control the component programmatically, use the controlled props pattern shown above.

Important Notes

Rate

  • Controls the speed of the chorus modulation
  • Typical range: 0.1 to 5 Hz
  • Lower rates create subtle movement
  • Higher rates create more dramatic effects

Depth

  • Controls the amount of pitch variation
  • Very small values (0.001-0.005) work best
  • Too much depth can sound unnatural

Delay

  • Base delay time before modulation
  • Typical range: 0.01 to 0.05 seconds
  • Shorter delays create tighter chorus
  • Longer delays create more separated effect

Released under the MIT License.