FX

Filter

Biquad filter as a bus FX insert. Lowpass for muffles, highpass for radios, bandpass for telephone, peaking for EQ.

Everything muffles when the player goes underwater, and the cheapest convincing version of that is one filter on one bus.

A Filter is a single BiquadFilterNode wrapped in the FX-insert contract. Pick a type, a frequency and a Q. Drive any of them from a Parameter when you want the transition rather than the state.

API surface

Filter + FilterConfig ts
class Filter implements FxInsert {
  readonly input: GainNode;
  readonly output: GainNode;
  bypassed: boolean;

  setType(t: FilterKind): void;
  setFrequency(hz: number): void;
  setQ(q: number): void;
  setGain(db: number): void;   // peaking gain, live
  dispose(): void;
}

type FilterKind = 'lowpass' | 'highpass' | 'bandpass' | 'notch' | 'peaking' | 'allpass';

interface FilterConfig {
  type?: FilterKind;
  frequency?: number;                 // Hz
  q?: number;
  gain?: number;                      // dB — peaking only
}

Live demo

Type, frequency, Q. Try lowpass at 400 Hz with high Q for a wah.

Recipes

Insert + live tweak

ts ts
import { Filter } from '@schmooky/zvuk';

const fx = new Filter(engine.context, {
  type: 'lowpass',
  frequency: 800,
  q: 1.2,
});
engine.bus('music').addFx(fx);

// Live sweep
fx.setFrequency(2000);
fx.setQ(0.7);

"Underwater" sweep from a Parameter

ts ts
// Sweep a filter from a Parameter for "underwater" / "muffled" effects.
const muffle = engine.parameter('muffle', 0);
muffle.bindTo((v) => fx.setFrequency(v), { from: 20000, to: 400, curve: 'easeIn' });

muffle.set(1);   // fully underwater

Common starting points

EffectTypeFrequencyQ
Muffled / underwaterlowpass400–800 Hz1
Tinny radio / phonebandpass1500 Hz2
Subwoofer cuthighpass120 Hz1
De-hisslowpass10 kHz0.7
Wahbandpassswept4–8

Pitfalls

Don't push Q to extremes.
Q above 10 starts ringing. On music, that's a feedback howl. Stay under 5 unless you're sweeping for a deliberate effect.

Related