RNNoise filter
Suppress background noise in the user's audio with RNNoise, compiled to WebAssembly so it runs on Bun, Node, Deno and Cloudflare Workers.
Overview
RNNoiseFilter removes background noise from the user's audio before it reaches VAD and
speech-to-text, using RNNoise, a small recurrent neural
network. It is a port of Pipecat's RNNoiseFilter. Upstream calls the native pyrnnoise library;
pipedog runs RNNoise 0.2 as WebAssembly, shipped inside the package, so there is nothing to
install and it works on Cloudflare Workers too.
Upstream page
Pipecat's documentation for the same filter.
Source
rnnoise_filter.ts, rnnoise.ts and engine.ts.
Installation
bun add @pipedog/audio-rnnoise-filterimport { RNNoiseFilter } from "@pipedog/audio-rnnoise-filter";Runtimes
| Runtime | Works | What it needs |
|---|---|---|
| Bun | ✅ yes | Nothing to install: the WebAssembly module ships in the package. |
| Node.js | ✅ yes | Nothing to install: the WebAssembly module ships in the package. |
| Deno | ✅ yes | Nothing to install: the WebAssembly module ships in the package. |
| Cloudflare Workers | ✅ yes | Nothing to install: the WebAssembly module ships in the package. The bundler must import .wasm files as modules (wrangler's default rule). |
Generated from pipecat.runtimes and pipecat.compat in packages/audio-rnnoise-filter/package.json: ✅ means the compat smoke test passes on that runtime.
Usage
Set the filter as the transport's audioInFilter. It starts and stops with the transport.
import { RNNoiseFilter } from "@pipedog/audio-rnnoise-filter";
import { TransportParams } from "@pipedog/core/transports/base_transport";
const params = new TransportParams({
audioInEnabled: true,
audioInFilter: new RNNoiseFilter(),
});RNNoise works on 480-sample frames (10 ms) at 48 kHz. The filter buffers incoming audio into frames
and, when the transport is not at 48 kHz, resamples in and out with the
soxr-style stream resampler (@pipedog/resampler-wasm). A call
returns the frames that completed, so the first calls can be empty, and the output lags the input by
less than one frame.
Send a FilterEnableFrame to switch the filter on and off while the bot runs:
import { FilterEnableFrame } from "@pipedog/core/frames/frames";
await worker.queueFrame(new FilterEnableFrame({ enable: false }));Configuration
new RNNoiseFilter(options?):
| Option | Type | Default | Description |
|---|---|---|---|
resamplerQuality | "VHQ" | "HQ" | "MQ" | "LQ" | "QQ" | "QQ" | Resampler preset when the transport is not at 48 kHz. "QQ" has the lowest latency. |
rnnoise | ({ sampleRate }) => RNNoiseLike | the bundled WASM build | Create your own denoiser, for example a fake in tests. |
Lower-level pieces are exported too: initRnnoise(source?) loads the module from bytes, a
WebAssembly.Module, a URL or a Response (on Workers only a module), RNNoise mirrors pyrnnoise's
denoise_chunk, and RnnoiseEngine.createState() gives the raw 480-sample processFrame.
Cloudflare Workers
workerd refuses to compile WebAssembly from bytes, so on Workers the package's "workerd" export
condition imports rnnoise.wasm as a pre-compiled module. Wrangler's default rule for .wasm files
does this; nothing else is needed. The module is 1.4 MB.
Differences from upstream
- WebAssembly instead of
pyrnnoise. The model is RNNoise 0.2, so the denoising is the same kind of output, but it is not bit-identical. The module is the one built by@jitsi/rnnoise-wasm(Apache-2.0). - The
rnnoiseoption and a protected_createResampler()hook replace the module-level patching upstream's tests do.
See the package README for the licenses of what it bundles.
pipedog is an unofficial community TypeScript port of Pipecat. It is not affiliated with or endorsed by Daily or the Pipecat project.
Portions adapted from the Pipecat documentation, Copyright (c) 2024–2025 Daily, BSD-2-Clause (see apps/docs/LICENSE-pipecat-docs).