Skip to main content
Version: Next

WorkletNode

warning

Requires react-native-audio-worklets, react-native-worklets >= 0.10.0, and react-native-audio-api >= 1.0.0. See the Worklets introduction for installation.

WorkletNode is a pass-through analysis node. It accumulates incoming audio frames until bufferLength is reached, then invokes your worklet on the UI runtime with a read-only snapshot. Audio output is unchanged — modifications inside the callback do not affect the signal.

For background on runtimes and performance tips, see How to use audio worklets mindfully.

Constructor

import { AudioContext } from 'react-native-audio-api';
import { WorkletNode } from 'react-native-audio-worklets';

const node = new WorkletNode(context, callback, 1024);

// or with explicit options:
const timeDomainNode = new WorkletNode(context, callback, {
domain: 'time-domain',
bufferLength: 1024,
});

const frequencyDomainNode = new WorkletNode(context, callback, {
domain: 'frequency-domain',
bufferLength: 1024,
});

After construction, tune frequency-domain analysis at runtime:

frequencyDomainNode.smoothingTimeConstant = 0.5;

Parameters

ParameterTypeDescription
contextBaseAudioContextThe audio context that owns this node.
callbackWorkletNodeCallbackWorklet invoked on the UI runtime when a snapshot is ready. Must include the 'worklet' directive.
optionsnumber | WorkletNodeOptionsOptional. Pass bufferLength directly, { domain?, bufferLength? }, or omit for defaults (time-domain, bufferLength: 1024).

WorkletNodeOptions

type WorkletNodeDomain = 'time-domain' | 'frequency-domain';

interface WorkletNodeOptions {
domain?: WorkletNodeDomain; // default 'time-domain'
bufferLength?: number; // power of 2, 32..32768, default 1024
smoothingTimeConstant?: number; // default 0.8, frequency-domain only
}
OptionDefaultDescription
domain'time-domain'Snapshot type passed to the callback.
bufferLength1024Snapshot size: PCM samples (time-domain) or magnitude bins (frequency-domain). In frequency-domain mode the internal FFT size is bufferLength * 2.
smoothingTimeConstant0.8Exponential smoothing of linear magnitude bins (0..1).

WorkletNodeCallback

type WorkletNodeCallback = (audioData: Float32Array) => void;
ArgumentDescription
audioDataStable Float32Array reused every callback. Time-domain: down-mixed mono PCM (bufferLength samples). Frequency-domain: linear magnitude spectrum (bufferLength bins; FFT size bufferLength * 2).
caution

Do not assign audioData to a Reanimated shared value (or any state read later on the UI thread). The native buffer pool is reused for the next snapshot and may be written from the audio thread after your callback returns.

  • Safe: compute a scalar inside the callback and assign that (e.g. amplitude.value = rms).
  • Safe: copy samples if you need waveform data later — Float32Array.from(audioData).
  • Unsafe: waveform.value = audioData — you keep a live alias, not a frozen snapshot.

Errors

Error typeCondition
NotSupportedErrorreact-native-audio-worklets native module not installed, worklet extensions are not linked, or New Architecture is disabled.
NotSupportedErrorreact-native-worklets is missing or below the supported version (>= 0.10.0).
IndexSizeErrorbufferLength or smoothingTimeConstant is out of range.

How buffering works

  1. Each render quantum appends down-mixed mono frames into an internal buffer.
  2. Time-domain: when bufferLength frames are collected, the callback is scheduled.
  3. Frequency-domain: when bufferLength * 2 PCM samples are collected, native code runs window + FFT + magnitude smoothing, then schedules the callback with bufferLength bins.
  4. While the callback is running, new frames are skipped (audio still passes through).
  5. After the callback completes, accumulation starts again from the beginning.

If a single quantum contains more frames than needed to fill the buffer, only the frames required to reach bufferLength are copied — the rest are discarded.

Example

Oscillator → WorkletNode (RMS on UI) → destination, driving Reanimated bars:

import { AudioContext } from 'react-native-audio-api';
import { WorkletNode } from 'react-native-audio-worklets';
import { useSharedValue, withSpring } from 'react-native-reanimated';

function Visualizer() {
const amplitude = useSharedValue(0);

const start = () => {
const ctx = new AudioContext();

const workletNode = new WorkletNode(
ctx,
(audioData) => {
'worklet';

let sum = 0;
for (let i = 0; i < audioData.length; i++) {
sum += audioData[i] * audioData[i];
}
const rms = Math.sqrt(sum / audioData.length);
amplitude.value = withSpring(Math.min(rms * 4, 1));
},
1024
);

const oscillator = ctx.createOscillator();
oscillator.frequency.value = 440;
oscillator.connect(workletNode);
workletNode.connect(ctx.destination);
oscillator.start();
ctx.resume();
};

// ...
}

Properties

Inherits all properties from AudioNode.

AudioNodeproperties
PropertyTypeDescription
bufferLength
Read only
numberSnapshot size passed to the callback (set at construction). In frequency-domain mode, internal FFT size is bufferLength * 2.
smoothingTimeConstantnumberExponential smoothing of linear magnitude bins (0..1, default 0.8). Meaningful in frequency-domain mode.

Methods

Inherits all methods from AudioNode.

AudioNodemethods

WorkletNode does not define any additional methods.

Known issue

When using Reanimated shared values, UI updates from the worklet may not appear immediately because the microtask queue is not always flushed on the UI runtime.

Add this at the end of your callback if you see missing updates:

requestAnimationFrame(() => {});

Use only after confirming the issue — it adds a small scheduling cost.