
Product
Socket Firewall Now Blocks Malicious VS Code and Open VSX Extensions
Socket Firewall blocks malicious VS Code and Open VSX extensions before install, protecting developers from compromised editor marketplaces.
audio-filter
Advanced tools
Canonical audio filters implementations.
Covering
weighting, auditory, analog, speech, eq, effect domains.
npm install audio-filter
// import everything
import * as filter from 'audio-filter'
// import individually
import aWeighting from 'audio-filter/weighting/a-weighting.js'
import gammatone from 'audio-filter/auditory/gammatone.js'
import korg35 from 'audio-filter/analog/korg35.js'
import vocoder from 'audio-filter/speech/vocoder.js'
import graphicEq from 'audio-filter/eq/graphic-eq.js'
import comb from 'audio-filter/effect/comb.js'
All filters share one shape:
filter(buffer, params) // → buffer (modified in-place)
Takes an Array/Float32Array/Float64Array, modifies it in-place, returns it. Pass the same params object on every call to persist state across blocks automatically:
let params = { fc: 1000, resonance: 0.5, fs: 44100 }
for (let buf of stream) moogLadder(buf, params)
For frequency analysis, weighting filters expose a .coefs(fs) method returning a second-order sections (SOS) array — [{b0, b1, b2, a1, a2}, ...], one biquad per section — for use with digital-filter:
import { freqz, mag2db } from 'digital-filter/core'
let sos = aWeighting.coefs(44100)
let resp = freqz(sos, 2048, 44100)
let db = mag2db(resp.magnitude)
Standard measurement curves. Each is defined by a standards body to a specific curve shape and normalization.
| filter | standard | normalized |
|---|---|---|
aWeighting | IEC 61672-1:2013 | 0 dB at 1 kHz |
cWeighting | IEC 61672-1:2013 | 0 dB at 1 kHz |
kWeighting | ITU-R BS.1770-4:2015 | — |
itu468 | ITU-R BS.468-4:1986 | +12.2 dB at 6.3 kHz |
riaa | RIAA 1954 / IEC 60098 | 0 dB at 1 kHz |
Models how the ear perceives loudness — attenuates low and very high frequencies.
Transfer function: $H(s) = \frac{Ks^4}{(s+\omega_1)^2(s+\omega_2)(s+\omega_3)(s+\omega_4)^2}$
Poles: $\omega_1 = 2\pi \cdot 20.6,\text{Hz}$, $\omega_2 = 2\pi \cdot 107.7,\text{Hz}$, $\omega_3 = 2\pi \cdot 737.9,\text{Hz}$, $\omega_4 = 2\pi \cdot 12194,\text{Hz}$
Implementation: matched z-transform ($z_k = e^{s_k/f_s}$), 3 SOS sections — no frequency warping near Nyquist
Normalization: 0 dB at 1 kHz (IEC requirement)
import { aWeighting } from 'audio-filter/weighting'
let p = { fs: 44100 }
for (let buf of stream) aWeighting(buf, p) // A-weighted stream
Standard: IEC 61672-1:20131
Use when: measuring SPL, noise, OSHA compliance, audio quality
Not for: loudness in broadcast (use K-weighting), noise annoyance (use ITU-468)
Like A-weighting but flatter — less rolloff at low and high frequencies.
Transfer function: $H(s) = \frac{Ks^2}{(s+\omega_1)^2(s+\omega_4)^2}$
Poles: $\omega_1 = 2\pi \cdot 20.6,\text{Hz}$, $\omega_4 = 2\pi \cdot 12194,\text{Hz}$ (same as A-weighting outer poles)
Implementation: matched z-transform, 2 SOS sections
cWeighting(buffer, { fs: 44100 })
Standard: IEC 61672-1:20131
Use when: peak sound level measurement, where A-weighting over-penalizes bass
Compared to A: rolls off below 31.5 Hz and above 8 kHz; flat 31.5 Hz–8 kHz
The loudness measurement curve — a high shelf plus a highpass. Used to compute LUFS.
Stage 1: pre-filter — high shelf +4 dB above ~1.5 kHz (head diffraction simulation)
Stage 2: RLB highpass — 2nd-order Butterworth at ~38 Hz (removes sub-bass)
Exact coefficients at 48 kHz: specified in BS.1770 Annex 1; this implementation uses them verbatim
import { kWeighting } from 'audio-filter/weighting'
kWeighting(buffer, { fs: 48000 }) // exact ITU-R BS.1770 coefficients
kWeighting(buffer, { fs: 44100 }) // approximated via biquad design
Standard: ITU-R BS.1770-4:20152, EBU R128
Use when: computing integrated loudness (LUFS/LKFS), broadcast loudness normalization
Not for: A-weighted SPL measurement (different shape, different standard)
Peaked noise weighting — peaks at +12.2 dB near 6.3 kHz — models how humans actually perceive noise annoyance.
Shape: rises steeply from 31.5 Hz, peaks at +12.2 dB at 6.3 kHz, rolls off above 10 kHz
Implementation: practical IIR approximation via cascaded biquads, within ~1 dB of spec
itu468(buffer, { fs: 48000 })
Standard: ITU-R BS.468-4:19863 (original CCIR 468, 1968)
Rationale: human hearing is more sensitive to short noise bursts than sine tones; 468 weights accordingly
Use when: measuring noise in broadcast equipment, tape noise, hum and hiss
Compared to A-weighting: 6.3 kHz peak makes it harsher on hiss; preferred in European broadcast
Playback equalization for vinyl records — a shelving curve with three time constants.
Transfer function: $H(s) = \frac{1 + sT_2}{(1 + sT_1)(1 + sT_3)}$
Time constants: $T_1 = 3180,\mu\text{s}$ (50.05 Hz pole), $T_2 = 318,\mu\text{s}$ (500.5 Hz zero), $T_3 = 75,\mu\text{s}$ (2122 Hz pole)
Implementation: 1 SOS section via bilinear transform, normalized 0 dB at 1 kHz
import { riaa } from 'audio-filter/weighting'
riaa(phonoSignal, { fs: 44100 }) // correct vinyl playback
Standard: RIAA 1954, IEC 60098:19874
Purpose: playback de-emphasis undoes the mastering pre-emphasis applied during vinyl cutting
Shape: boosts bass ~+20 dB at 20 Hz, rolls off treble; at playback restores flat response
Models of the human auditory system — how the cochlea and brain decompose sound into frequency channels. Used in psychoacoustics, music information retrieval, and hearing aid design.
The cochlear filter — bandpass tuned to one frequency, decaying oscillation, mimics an inner hair cell.
Model: cascade of complex one-pole filters; 4th-order is the standard cochlear approximation
Bandwidth: $\text{ERB} = 24.7\left(\frac{4.37 f_c}{1000} + 1\right),\text{Hz}$
Implementation: complex resonator with gain normalization to 0 dB at $f_c$
import { gammatone } from 'audio-filter/auditory'
let params = { fc: 1000, fs: 44100 }
gammatone(buffer, params) // bandpass at 1 kHz with cochlear envelope
Origin: Patterson et al. (1992)5
Use when: cochlear modeling, auditory scene analysis, psychoacoustic feature extraction
Compared to Butterworth bandpass: gammatone has asymmetric temporal envelope matching biological data
Reuse params across blocks — state in params._s, gain cached in params._gain.
ISO/IEC fractional-octave filter bank — the standard for acoustic measurement and spectrum analysis.
Center frequencies: ISO 266 series — $f_c = 1000 \cdot G^{k/n}$, $G = 10^{3/10}$
Bandwidth: each band spans $f_c \cdot G^{-1/(2n)}$ to $f_c \cdot G^{+1/(2n)}$
1/1 octave: 10 bands (31.5–16 kHz) — coarse; 1/3 octave: 30 bands — standard; 1/6+: psychoacoustics
Returns: array of { fc, coefs } — each band is a biquad bandpass section
import { octaveBank } from 'audio-filter/auditory'
import { filter } from 'digital-filter'
let bands = octaveBank(3, 44100) // 1/3-octave, 30+ bands
for (let band of bands) {
let buf = Float64Array.from(signal)
filter(buf, { coefs: band.coefs })
spectrum.push({ fc: band.fc, energy: rms(buf) })
}
Standard: IEC 61260-1:20146, ANSI S1.11:2004
Use when: acoustic measurement, noise assessment, spectrum visualization
Equivalent Rectangular Bandwidth scale — how the auditory system actually spaces its channels.
ERB formula: $\text{ERB}(f_c) = 24.7\left(\frac{4.37 f_c}{1000} + 1\right)$
Spacing: ~1 ERB between adjacent channels — logarithmic above 1 kHz, more linear below
Returns: array of { fc, erb, bw } descriptors; apply gammatone at each fc for the filter bank
import { erbBank, gammatone } from 'audio-filter/auditory'
let bands = erbBank(44100)
let states = bands.map(b => ({ fc: b.fc, fs: 44100 }))
for (let buf of stream) {
let channels = bands.map((_, i) => {
let b = Float64Array.from(buf)
gammatone(b, states[i])
return b
})
}
Origin: Moore & Glasberg (1983, 1990)7
Use when: speech processing, hearing models, auditory feature extraction
Compared to Bark: ERB is more accurate above 500 Hz; Bark is the psychoacoustic masking model
Zwicker's 24 critical bands — the psychoacoustic foundation of perceptual audio coding.
Scale: 24 bands spanning 20 Hz–20 kHz; named after Heinrich Barkhausen
Band widths: ~100 Hz wide below 500 Hz; ~20% of center frequency above
Returns: array of { bark, fLow, fHigh, fc, coefs } — each band is a biquad bandpass section
import { barkBank } from 'audio-filter/auditory'
import { filter } from 'digital-filter'
let bands = barkBank(44100) // 24 critical bands
for (let band of bands) {
let buf = Float64Array.from(signal)
filter(buf, { coefs: band.coefs })
excitation[band.bark] = rms(buf)
}
Origin: Zwicker (1961)8
Use when: perceptual audio coding (MP3/AAC use Bark-like groupings), loudness models, masking
Compared to ERB: Bark bands are wider and fewer; ERB is more accurate for hearing science
Discrete-time models of analog circuits — each named after the hardware it replicates. Nonlinear, stateful, process in-place. The filters in synthesizers.
Robert Moog's 4-pole transistor ladder, 1965 — the most imitated filter in electronic music.
Circuit: 4 cascaded one-pole transistor ladder sections, global feedback from output to input
Implementation: Zero-delay feedback (ZDF) via trapezoidal integration — Zavalishin (2012)9, Ch. 6
Response: $-24,\text{dB/oct}$ lowpass; resonance peak at $f_c$; self-oscillation (sine wave) at resonance=1
Nonlinearity: $\tanh$ saturation at input (transistor ladder characteristic)
import { moogLadder } from 'audio-filter/analog'
let params = { fc: 800, resonance: 0.7, fs: 44100 }
moogLadder(buffer, params)
// Self-oscillation — runs indefinitely from a single impulse
let silent = new Float64Array(4096); silent[0] = 0.01
moogLadder(silent, { fc: 1000, resonance: 1, fs: 44100 })
Patent: Moog (1965) US347562310
vs Diode ladder: Moog saturates only at input; diode saturates at each stage — different character at high resonance
Roland TB-303 / EMS VCS3 style — per-stage saturation gives the characteristic acid "squelch".
Circuit: Roland TB-303, EMS VCS3, EDP Wasp
Key difference from Moog: $\tanh$ nonlinearity at each of 4 stages, not just input; feedback is a weighted sum of all stage outputs
Character: preserves bass at high resonance; more "squelchy" and aggressive than Moog
Implementation: ZDF — Zavalishin (2012)9; Pirkle (2019)11, Ch. 10
Stability: stable up to resonance=0.95; bounded output
import { diodeLadder } from 'audio-filter/analog'
let params = { fc: 500, resonance: 0.8, fs: 44100 }
diodeLadder(buffer, params)
Korg MS-10/MS-20, 1978 — 2-pole filter with lowpass and complementary highpass outputs.
Topology: 2 cascaded one-pole sections with nonlinear feedback; HP = input − LP
Response: $-12,\text{dB/oct}$; aggressive resonance due to nonlinear feedback; both LP and HP from one circuit
import { korg35 } from 'audio-filter/analog'
korg35(buffer, { fc: 1000, resonance: 0.5, type: 'lowpass', fs: 44100 })
korg35(buffer, { fc: 1000, resonance: 0.5, type: 'highpass', fs: 44100 })
Circuit: Korg MS-10/MS-20 (1978)
Analysis: Stilson & Smith (1996)12; Zavalishin (2012)9, Ch. 5
vs Moog ladder: 2-pole ($-12,\text{dB/oct}$) vs 4-pole ($-24,\text{dB/oct}$); Korg35 has complementary HP mode
Filters that model or process the human vocal tract — from vowel synthesis to spectral voice coding.
Parallel resonator bank — each peak models one vocal tract resonance (formant).
Model: parallel combination of second-order resonators, each modeling one vocal tract mode
Formant frequencies: determined by vocal tract shape; F1 controls vowel openness, F2 controls front/back
Typical ranges: F1: 250–850 Hz, F2: 850–2500 Hz, F3: 1700–3500 Hz
Implementation: uses resonator internally — constant peak-gain bandpass per formant
Defaults: F1=730 Hz, F2=1090 Hz, F3=2440 Hz (open vowel /a/)
import { formant } from 'audio-filter/speech'
formant(excitation, { fs: 44100 }) // vowel /a/ (default)
formant(excitation, {
formants: [{ fc: 270, bw: 60, gain: 1 }, { fc: 2290, bw: 90, gain: 0.5 }],
fs: 44100
}) // vowel /i/
Use when: speech synthesis, singing synthesis, vocal effects, acoustic phonetics
Not a substitute for: LPC synthesis, which estimates formants automatically from a speech signal
Channel vocoder — transfers the spectral envelope of one sound onto the pitched content of another.
Note: takes two separate buffers, returns a new buffer (does not modify in-place).
Principle: analyze modulator into N bands → extract envelope per band → multiply with filtered carrier → sum
Implementation: N parallel bandpass filters on both signals; envelope follower per modulator band
Band count: 8 = robotic effect; 16 = classic vocoder sound; 32+ = more speech intelligibility
import { vocoder } from 'audio-filter/speech'
// carrier: pitched source (sawtooth, buzz, noise...)
// modulator: signal whose spectral shape to impose (voice, instrument...)
let output = vocoder(carrier, modulator, { bands: 16, fs: 44100 })
Inventor: Dudley (1939)13, Bell Labs
Use when: voice effects, talkbox simulation, cross-synthesis, spectral morphing
Equalization and frequency routing — from parametric studio EQ to speaker crossover networks.
10-band ISO octave equalizer — fixed center frequencies, gain per band.
Implementation: parallel biquad peaking filters, one per band; gains combined additively
Band spacing: 1-octave intervals — $f_k = 1000 \cdot 2^k,\text{Hz}$
Bands: 31.25, 62.5, 125, 250, 500, 1000, 2000, 4000, 8000, 16000 Hz
import { graphicEq } from 'audio-filter/eq'
graphicEq(buffer, {
gains: { 125: -3, 1000: +6, 8000: +2 },
fs: 44100
})
Standard: ISO 266:1997 center frequencies
Use when: quick tonal shaping, DJ mixers, consumer audio, live sound
vs Parametric EQ: fixed centers but simpler — no per-band frequency or Q control
N-band EQ with fully adjustable frequency, Q, and gain per band.
Implementation: cascaded biquad sections — one per band; peak uses peaking EQ biquad, shelves use Zölzer shelf design14
Band types: peak (bell curve at $f_c$), lowshelf (boost/cut below $f_c$), highshelf (boost/cut above $f_c$)
import { parametricEq } from 'audio-filter/eq'
parametricEq(buffer, {
bands: [
{ fc: 80, Q: 0.7, gain: +4, type: 'lowshelf' },
{ fc: 1000, Q: 2.0, gain: -3, type: 'peak' },
{ fc: 8000, Q: 0.7, gain: +2, type: 'highshelf' },
],
fs: 44100
})
Use when: studio mixing, mastering, precise tonal correction
vs Graphic EQ: fully adjustable $f_c$, Q, and gain per band; no fixed centers
Linkwitz-Riley crossover network — splits audio into N frequency bands with flat magnitude sum.
Filter type: cascade of two Butterworth filters of half the specified order
Property: LR4 (order=4) bands sum to flat magnitude response with correct phase alignment
Orders: LR2 ($-12,\text{dB/oct}$), LR4 ($-24,\text{dB/oct}$, most common), LR8 ($-48,\text{dB/oct}$)
Returns: SOS[][] — one SOS array per band
import { crossover } from 'audio-filter/eq'
import { filter } from 'digital-filter'
let bands = crossover([500, 5000], 4, 44100) // 3 bands: lo / mid / hi
let lo = Float64Array.from(buffer); filter(lo, { coefs: bands[0] })
let mid = Float64Array.from(buffer); filter(mid, { coefs: bands[1] })
let hi = Float64Array.from(buffer); filter(hi, { coefs: bands[2] })
Designers: Linkwitz & Riley (1976)15
Use when: speaker system design, multi-band dynamics, band splitting for separate processing
Headphone crossfeed — mixes a filtered copy of each channel into the other to reduce in-head localization.
Takes two separate channel buffers, modifies both in-place.
Problem: speaker playback has inter-channel crosstalk and head shadowing; headphones remove these, causing an unnatural "in-head" stereo image
Solution: add a lowpass-filtered, attenuated copy of each channel to the opposite channel, simulating crosstalk and head diffraction
fc: models the head-shadow lowpass (~700 Hz is typical); level: 0.3 = mild, 0.5 = strong
import { crossfeed } from 'audio-filter/eq'
crossfeed(left, right, { fc: 700, level: 0.3, fs: 44100 })
Origin: Bauer (1961)16; BS2B (Bauer Stereophonic-to-Binaural) algorithm
Signal processing utilities — conditioning, shaping, and analyzing audio signals.
Removes DC offset — the simplest useful filter.
$H(z) = \dfrac{1 - z^{-1}}{1 - Rz^{-1}}$
Topology: zero at $z = 1$ (DC), pole at $z = R$
Cutoff: $f_c \approx \frac{(1-R) f_s}{2\pi}$ — $R = 0.995$ gives ~22 Hz at 44.1 kHz
import { dcBlocker } from 'audio-filter/effect'
let params = { R: 0.995 }
dcBlocker(buffer, params)
Use when: removing DC bias before processing, preventing lowpass filter saturation
Adds a delayed copy of the signal to itself — notches and peaks at harmonics of $f_s / D$.
Feedforward: $H(z) = 1 + g \cdot z^{-D}$ — notches at $f = \frac{(2k+1) f_s}{2D}$
Feedback: $H(z) = \dfrac{1}{1 - g \cdot z^{-D}}$ — peaks at $f = \frac{k \cdot f_s}{D}$
import { comb } from 'audio-filter/effect'
comb(buffer, { delay: 100, gain: 0.6, type: 'feedback' })
Use when: flanging, chorus (with modulated delay), Karplus-Strong string synthesis, room mode modeling
Unity magnitude at all frequencies — shifts phase only. First and second order.
First order: $H(z) = \dfrac{a + z^{-1}}{1 + a z^{-1}}$ — pole at $z = -a$, 180° phase shift at Nyquist
Second order: $H(z) = \dfrac{d - 2R\cos(\omega_0)z^{-1} + R^2 z^{-2}}{1 - 2R\cos(\omega_0)z^{-1} + R^2 z^{-2}}$ — 360° phase shift around $\omega_0$
import { allpass } from 'audio-filter/effect'
allpass.first(buffer, { a: 0.5 }) // coefficient a
allpass.second(buffer, { fc: 1000, Q: 1, fs: 44100 }) // center fc, quality Q
Use when: phase equalization, reverb building blocks (Schroeder reverb), stereo widening
First-order highpass (emphasis) and its inverse (de-emphasis) — used before and after coding or transmission.
$H(z) = 1 - \alpha z^{-1}$ (emphasis) / $H(z) = \dfrac{1}{1 - \alpha z^{-1}}$ (de-emphasis)
Rolloff: emphasis boosts above $f_c = \frac{(1-\alpha) f_s}{2\pi}$ — $\alpha = 0.97$ gives ~420 Hz at 44.1 kHz
Inverse pair: deemphasis exactly cancels emphasis — $H_e(z) \cdot H_d(z) = 1$
import { emphasis, deemphasis } from 'audio-filter/effect'
emphasis(buffer, { alpha: 0.97 }) // before encoding
deemphasis(buffer, { alpha: 0.97 }) // after decoding — exact inverse
Use when: speech coding (GSM, AMR uses $\alpha = 0.97$), tape recording, FM broadcasting
Constant peak-gain bandpass — peak amplitude stays fixed regardless of bandwidth.
$H(z) = \dfrac{1 - R^2}{1 - 2R\cos(\omega_0)z^{-1} + R^2 z^{-2}}$
Pole radius: $R = e^{-\pi \cdot bw / f_s}$ — controls bandwidth; $bw \to 0$ gives infinite Q
Peak gain: always 0 dB by construction — $(1 - R^2)$ normalizes the peak
import { resonator } from 'audio-filter/effect'
resonator(buffer, { fc: 440, bw: 20, fs: 44100 })
Use when: additive synthesis (bells, gongs), modal synthesis, formant bank building
vs Peaking EQ: resonator has fixed 0 dB peak; peaking EQ has variable gain — use resonator for synthesis, EQ for mixing
Tracks the instantaneous amplitude of a signal with configurable attack and release.
Attack: $y[n] = \alpha_A \cdot y[n{-}1] + (1-\alpha_A)|x[n]|$ when $|x[n]| > y[n{-}1]$
Release: $y[n] = \alpha_R \cdot y[n{-}1]$ when $|x[n]| \leq y[n{-}1]$
Time constants: $\alpha = e^{-1/(\tau f_s)}$ — converts seconds to pole radius
import { envelope } from 'audio-filter/effect'
let params = { attack: 0.001, release: 0.05, fs: 44100 }
envelope(buffer, params) // buffer replaced with envelope signal (0–1)
Use when: compressor/limiter sidechain, auto-wah, ducking, VCA control, gain riding
Limits the rate of change — limits rise and fall rates separately.
Operation: clips the per-sample derivative — $\Delta y \leq \text{rise}/f_s$ and $\Delta y \geq -\text{fall}/f_s$
Nonlinear: not a linear filter — frequency response depends on signal amplitude
import { slewLimiter } from 'audio-filter/effect'
slewLimiter(buffer, { rise: 500, fall: 200, fs: 44100 })
Use when: smoothing control signals and automation, click prevention, portamento/glide, analog CV emulation
Error-feedback dithering — quantizes to N bits while shaping quantization noise into high frequencies.
Principle: $y[n] = Q(x[n] + e_\text{shaped}[n])$ — quantization error fed back through shaping filter
Default filter: first-order highpass $H(z) = 1 - z^{-1}$ — pushes noise toward Nyquist
Gain: noise shaping trades total noise power for spectral placement; audible band gets quieter
import { noiseShaping } from 'audio-filter/effect'
noiseShaping(buffer, { bits: 16 }) // dither to 16-bit, noise shaped above 10 kHz
Use when: dithering before bit-depth reduction, CD mastering, 16-bit export from 32-bit float
Reference: Lipshitz, Wannamaker & Vanderkooy (1992)17
Shapes white noise to $1/f$ spectrum — equal energy per octave.
Spectrum: power spectral density $S(f) \propto 1/f$ — $-3,\text{dB/oct}$ slope, equal energy per octave
Implementation: Voss-McCartney algorithm — sum of white noise sources at octave-spaced update rates; approximated by cascaded first-order IIR filters
import { pinkNoise } from 'audio-filter/effect'
let buf = new Float64Array(1024)
for (let i = 0; i < buf.length; i++) buf[i] = Math.random() * 2 - 1
pinkNoise(buf, {}) // white → pink (−3 dB/oct spectral slope)
Use when: noise testing, psychoacoustic masking reference, procedural audio, natural-sounding noise
vs White noise: white noise has equal energy per Hz ($-0,\text{dB/oct}$); pink is perceptually flat
Applies a constant dB/octave slope — tilts the entire spectrum.
Model: first-order IIR approximation of fractional power-law spectrum $S(f) \propto f^\alpha$
slope: $\alpha = -3,\text{dB/oct}$ gives pink noise character; $-6,\text{dB/oct}$ gives brownian/red noise
import { spectralTilt } from 'audio-filter/effect'
spectralTilt(buffer, { slope: -3, fs: 44100 }) // −3 dB/oct: brownian noise character
spectralTilt(buffer, { slope: +3, fs: 44100 }) // +3 dB/oct: pre-emphasis for coding
Use when: matching microphone/speaker frequency responses, spectral coloring, noise synthesis
Lowpass with continuously variable bandwidth — smooth parameter automation without discontinuities.
Implementation: biquad lowpass with per-sample coefficient update using smooth interpolation
Property: no discontinuity when $f_c$ or $Q$ change — avoids clicks from abrupt coefficient jumps
import { variableBandwidth } from 'audio-filter/effect'
variableBandwidth(buffer, { fc: 2000, Q: 1.0, fs: 44100 })
Use when: LFO-modulated filter cutoff, automated EQ sweeps, smooth filter animation
vs Direct biquad: recalculating biquad coefficients per sample causes zipper noise; variable bandwidth avoids this
| I need to... | Use |
|---|---|
| Measure SPL or noise level | aWeighting (general), cWeighting (peak), itu468 (broadcast noise) |
| Measure loudness (LUFS/LU) | kWeighting |
| Decode vinyl audio | riaa |
| Model the cochlea / auditory system | gammatone, erbBank |
| Analyze a spectrum in octave bands | octaveBank |
| Psychoacoustic analysis / masking model | barkBank |
| Synth filter — warmth and resonance | moogLadder |
| Synth filter — acid / squelch | diodeLadder |
| Synth filter — 2-pole LP + HP | korg35 |
| Synthesize vowel sounds | formant |
| Transfer one sound's spectral shape to another | vocoder |
| Studio EQ at fixed ISO frequencies | graphicEq |
| Studio EQ with full per-band control | parametricEq |
| Split audio for multi-way speakers | crossover |
| Improve headphone stereo imaging | crossfeed |
| Remove DC offset | dcBlocker |
| Create flanging / resonant combing | comb |
| Phase-shift without changing magnitude | allpass.first, allpass.second |
| Pre-process for audio coding | emphasis / deemphasis |
| Modal synthesis (bells, drums, rooms) | resonator |
| Track signal amplitude | envelope |
| Smooth a control signal | slewLimiter |
| Dither for bit-depth reduction | noiseShaping |
| Generate pink / brown noise | pinkNoise + spectralTilt |
| Tilt spectrum for tone shaping | spectralTilt |
Why does my filter click when I change fc or resonance?
Biquad coefficients change discontinuously between samples. Use variableBandwidth for smooth automated sweeps, or crossfade.
Why does my Moog/Diode filter blow up?
resonance=1 on Moog is intentional self-oscillation. Diode ladder is stable up to 0.95. Limit input gain before high resonance.
Does mutating params between calls reset state?
No — mutating the same object (params.fc = newFc) preserves state. Replacing the object (params = { fc: newFc }) loses it.
Why does .coefs(fs) return an SOS array instead of one biquad?
A-weighting needs 3 second-order sections; a single biquad can't represent a 6-pole response. Pass SOS arrays to digital-filter's filter() or freqz().
What sample rate should I use for accurate A-weighting? 96 kHz for IEC Class 1 across the full 20 Hz–20 kHz range. At 48 kHz error grows above 10 kHz (~1 dB at 10 kHz, ~4 dB at 20 kHz).
Chain filters
let p1 = { fc: 200, fs: 44100 }
let p2 = { R: 0.995 }
for (let buf of stream) {
dcBlocker(buf, p2) // DC removal first
moogLadder(buf, p1)
}
Stereo — independent state per channel
let pL = { fc: 1000, fs: 44100 }
let pR = { fc: 1000, fs: 44100 }
for (let [L, R] of stereoStream) {
moogLadder(L, pL)
moogLadder(R, pR)
}
Frequency analysis
import { freqz, mag2db } from 'digital-filter'
let sos = aWeighting.coefs(44100)
let { magnitude } = freqz(sos, 4096, 44100)
let db = mag2db(magnitude) // dB at 4096 frequencies, 20 Hz–Nyquist
Multi-band split
let bands = crossover([500, 5000], 4, 44100) // lo / mid / hi
let [lo, mid, hi] = bands.map(coefs => {
let buf = Float64Array.from(input) // copy — filter is in-place
filter(buf, { coefs })
return buf
})
// process independently, then sum
Automate cutoff without clicks
let p = { fc: 200, Q: 1.0, fs: 44100 }
for (let buf of stream) {
p.fc = 200 + lfo() * 1800 // mutate in-place — state preserved
variableBandwidth(buf, p)
}
New params object on every call — state resets each block
// Wrong
for (let buf of stream) moogLadder(buf, { fc: 1000, fs: 44100 })
// Right — create once, reuse
let p = { fc: 1000, fs: 44100 }
for (let buf of stream) moogLadder(buf, p)
Shared params for stereo — channels corrupt each other's state
// Wrong
let p = { fc: 1000, fs: 44100 }
for (let [L, R] of stream) { moogLadder(L, p); moogLadder(R, p) }
// Right — one object per channel
let pL = { fc: 1000, fs: 44100 }, pR = { fc: 1000, fs: 44100 }
for (let [L, R] of stream) { moogLadder(L, pL); moogLadder(R, pR) }
Filtering the same buffer twice for multi-band — second band sees pre-filtered input
// Wrong
filter(buffer, { coefs: bands[0] })
filter(buffer, { coefs: bands[1] }) // input already filtered!
// Right — copy per band
let bufs = bands.map(b => { let c = Float64Array.from(buffer); filter(c, { coefs: b.coefs }); return c })
Omitting fs — silently uses 44100 Hz math on 48000 Hz audio
// Wrong — wrong cutoffs at 48 kHz
moogLadder(buffer, { fc: 1000 })
// Right
moogLadder(buffer, { fc: 1000, fs: 48000 })
AudioContextIEC 61672-1:2013, Electroacoustics — Sound level meters — Part 1: Specifications. Supersedes IEC 651:1979. ↩ ↩2
ITU-R BS.1770-4:2015, Algorithms to measure audio programme loudness and true-peak audio level. Adopted by EBU R128. ↩
ITU-R BS.468-4:1986, Measurement of audio-frequency noise voltage level in sound broadcasting. Originally CCIR 468, 1968. ↩
RIAA standard (1954); IEC 60098:1987, Analogue audio disk records and reproducing equipment. ↩
Patterson, R.D., Robinson, K., Holdsworth, J., McKeown, D., Zhang, C. & Allerhand, M. (1992). "Complex sounds and auditory images." Auditory Physiology and Perception, Pergamon, pp. 429–446. ↩
IEC 61260-1:2014, Electroacoustics — Octave-band and fractional-octave-band filters — Part 1: Specifications. ANSI S1.11:2004. ↩
Moore, B.C.J. & Glasberg, B.R. (1983). "Suggested formulae for calculating auditory-filter bandwidths and excitation patterns." JASA 74(3), pp. 750–753. Updated 1990. ↩
Zwicker, E. (1961). "Subdivision of the audible frequency range into critical bands." JASA 33(2), p. 248. ↩
Zavalishin, V. (2012). The Art of VA Filter Design. Native Instruments. ↩ ↩2 ↩3
Moog, R.A. (1965). Voltage controlled electronic music modules. Patent US3475623. ↩
Pirkle, W.C. (2019). Designing Audio Effect Plugins in C++, 2nd ed. Routledge. ↩
Stilson, T. & Smith, J.O. (1996). "Analyzing the Moog VCF with considerations for digital implementation." Proc. ICMC. ↩
Dudley, H. (1939). "The vocoder." Bell Laboratories Record 17, pp. 122–126. Patent US2151091. ↩
Zölzer, U. (2011). DAFX: Digital Audio Effects, 2nd ed. Wiley. ↩
Linkwitz, S. & Riley, R. (1976). "Active Crossover Networks for Non-Coincident Drivers." JAES 24(1), pp. 2–8. ↩
Bauer, B.B. (1961). "Stereophonic Earphones and Binaural Loudspeakers." JAES 9(2), pp. 148–151. ↩
Lipshitz, S.P., Wannamaker, R.A. & Vanderkooy, J. (1992). "Quantization and Dither: A Theoretical Survey." JAES 40(5), pp. 355–375. ↩
FAQs
Audio filters – weighting, auditory, analog, speech, eq, effects
The npm package audio-filter receives a total of 63 weekly downloads. As such, audio-filter popularity was classified as not popular.
We found that audio-filter demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Product
Socket Firewall blocks malicious VS Code and Open VSX extensions before install, protecting developers from compromised editor marketplaces.

Research
More than 140 Mastra npm packages were compromised in a supply chain attack that used a typosquatted dependency to deliver a cross-platform infostealer during installation.

Research
/Security News
A new npm package tests AI malware scanners with prompt injection, safety-triggering comments, context flooding, and obfuscated JavaScript.