@billdaddy/dspkit
v0.1.0
Published
Zero-dependency digital signal processing: FFT/IFFT, spectrum analysis, FIR filter design (lowpass/highpass/bandpass/bandstop), window functions, signal utilities.
Maintainers
Readme
dspkit
Zero-dependency digital signal processing: FFT/IFFT, spectrum analysis, FIR filter design, window functions, and signal utilities — TypeScript-first npm equivalent of Python's scipy.signal, Go's gonum/dsp, Java's JTransforms.
import { fft, magnitudeSpectrum, dominantFrequency, generateSine, lowpassFilter, applyFilter } from "@billdaddy/dspkit";
// Generate a 440 Hz sine wave at 44100 Hz sample rate
const signal = generateSine(440, 1.0, 44100, 4096);
// Compute FFT and find dominant frequency
const spectrum = fft(signal);
const { magnitude, frequencies } = magnitudeSpectrum(spectrum, 44100);
const freq = dominantFrequency(magnitude, frequencies); // ≈ 440
// Design a 1 kHz lowpass FIR filter and apply it
const cutoff = 1000 / 44100; // normalized (0–0.5)
const h = lowpassFilter(cutoff, 128);
const filtered = applyFilter(signal, h);Why dspkit?
Every major scientific computing ecosystem ships DSP primitives in its standard or near-standard library:
- Python —
scipy.signal(FFT, IIR/FIR design, spectrogram) - Go —
gonum.org/v1/gonum/dsp(FFT, windowing) - Java —
JTransforms,Apache Commons Math - C# —
Math.NET Numerics(FFT, filter design) - Julia —
DSP.jl(comprehensive)
The only npm package attempting this space — dsp.js — was self-declared unmaintained as of 2014, has no TypeScript support, and receives ~38k downloads per week only because no alternative exists. dspkit is its modern, zero-dep, TypeScript-native replacement.
Install
npm install @billdaddy/dspkitUsage
FFT / IFFT
import { fft, ifft, fftConvolve, nextPow2 } from "@billdaddy/dspkit";
// Real-valued signal → complex spectrum (auto zero-pads to next power of 2)
const spectrum = fft([1, 2, 3, 4, 5, 6, 7, 8]);
spectrum.re; // real parts
spectrum.im; // imaginary parts
spectrum.length; // 8 (already a power of 2)
// Non-power-of-2 inputs are zero-padded automatically
fft([1, 2, 3]).length; // 4
// Inverse FFT → recover original signal
const back = ifft(spectrum);
back.re; // ≈ [1, 2, 3, 4, 5, 6, 7, 8]
// FFT-based convolution: O(N log N) vs direct O(N*M)
const result = fftConvolve([1, 2, 3], [4, 5, 6]); // → Float64Array([4, 13, 28, 27, 18])Spectrum analysis
import { fft, magnitude, powerSpectrum, phase, magnitudeSpectrum, dominantFrequency, rms, peak, crestFactor } from "@billdaddy/dspkit";
const c = fft(signal);
magnitude(c); // |X[k]| — amplitude at each bin
powerSpectrum(c); // |X[k]|² — power at each bin
phase(c); // atan2(im, re) — phase at each bin
// One-sided magnitude spectrum with physical frequency labels
const { magnitude: mag, frequencies } = magnitudeSpectrum(c, 44100);
// mag[k] is amplitude at frequencies[k] Hz
dominantFrequency(mag, frequencies); // frequency with highest amplitude
// Time-domain statistics
rms(signal); // root-mean-square energy
peak(signal); // maximum absolute value
crestFactor(signal); // peak / rmsWindow functions
Reduce spectral leakage when your signal doesn't contain an integer number of cycles.
import { hannWindow, hammingWindow, blackmanWindow, bartlettWindow,
nuttallWindow, blackmanHarrisWindow, flatTopWindow, rectangularWindow,
applyWindow, coherentGain, getWindow } from "@billdaddy/dspkit";
const N = 1024;
const w = hannWindow(N); // → Float64Array of length N
applyWindow(signal, w); // element-wise multiply signal × window
coherentGain(w); // mean of window (≈ 0.5 for Hann)
// Get window by name
getWindow("blackman", N); // one of: rectangular|hann|hamming|blackman|bartlett|nuttall|blackman-harris|flat-top| Window | Sidelobe level | Main lobe width | Use case | |--------|---------------|-----------------|----------| | Rectangular | -13 dB | Narrowest | Never leak matters | | Hann | -31 dB | Moderate | General purpose | | Hamming | -41 dB | Moderate | General purpose | | Blackman | -57 dB | Wide | Low sidelobes | | Nuttall | -93 dB | Wide | Very low sidelobes | | Blackman-Harris | -92 dB | Wide | Very low sidelobes | | Flat-Top | -44 dB | Widest | Amplitude accuracy |
FIR filter design (windowed-sinc)
All cutoff frequencies are normalized: cutoff = f_Hz / f_sample. Valid range: (0, 0.5) where 0.5 = Nyquist.
import { lowpassFilter, highpassFilter, bandpassFilter, bandstopFilter, applyFilter } from "@billdaddy/dspkit";
const sampleRate = 44100;
// Lowpass: pass < 2 kHz, attenuate > 2 kHz
const lp = lowpassFilter(2000 / sampleRate, 128); // order 128 → 129 coefficients
// Highpass: pass > 5 kHz, attenuate < 5 kHz
const hp = highpassFilter(5000 / sampleRate, 128);
// Bandpass: pass 300–3400 Hz (telephone band)
const bp = bandpassFilter(300 / sampleRate, 3400 / sampleRate, 128);
// Bandstop (notch): suppress 50 Hz power-line hum
const bs = bandstopFilter(45 / sampleRate, 55 / sampleRate, 128);
// Apply any FIR filter to a signal (causal, same output length as input)
const filtered = applyFilter(signal, lp);
// Note: group delay = order/2 samples (filter is linear-phase)Direct convolution
import { convolve } from "@billdaddy/dspkit";
convolve([1, 2, 3], [4, 5, 6]);
// → Float64Array([4, 13, 28, 27, 18]) (length = 3 + 3 - 1 = 5)Signal generation
import { generateSine, generateCosine, generateSquare, generateNoise, linspace } from "@billdaddy/dspkit";
const sr = 44100, n = 4096;
generateSine(440, 1.0, sr, n); // A4 at full amplitude
generateCosine(440, 0.5, sr, n); // half-amplitude cosine
generateSquare(100, 1.0, sr, n, 10); // square wave (10 harmonics)
generateNoise(0.1, n); // white noise at 10% amplitude
linspace(0, 1, 11); // [0, 0.1, 0.2, ..., 1.0]Signal utilities
import { zeroPad, mean, removeDC, normalize, toDb, fromDb } from "@billdaddy/dspkit";
zeroPad(signal, 512); // extend with zeros to length 512
mean(signal); // arithmetic mean
removeDC(signal); // subtract mean (remove DC offset)
normalize(signal); // scale so peak absolute value = 1
toDb(0.5); // ≈ -6 dB
fromDb(-6); // ≈ 0.5API summary
FFT
| Function | Description |
|----------|-------------|
| fft(signal) | Forward FFT of real signal → ComplexArray |
| ifft(complex) | Inverse FFT → ComplexArray |
| fftConvolve(a, b) | FFT-based linear convolution |
| nextPow2(n) | Smallest power of 2 ≥ n |
Spectrum
| Function | Description |
|----------|-------------|
| magnitude(c) | |X[k]| for each bin |
| powerSpectrum(c) | |X[k]|² for each bin |
| phase(c) | atan2(im, re) for each bin |
| magnitudeSpectrum(c, sr?) | One-sided spectrum with frequency labels |
| dominantFrequency(mag, freqs) | Frequency at spectral peak |
| rms(signal) | Root-mean-square |
| peak(signal) | Maximum absolute value |
| crestFactor(signal) | peak / rms |
Windows
| Function | Description |
|----------|-------------|
| hannWindow(n) | Hann window |
| hammingWindow(n) | Hamming window |
| blackmanWindow(n) | Blackman window |
| bartlettWindow(n) | Bartlett (triangular) window |
| nuttallWindow(n) | Nuttall window |
| blackmanHarrisWindow(n) | Blackman-Harris window |
| flatTopWindow(n) | Flat-top window |
| rectangularWindow(n) | No windowing (boxcar) |
| applyWindow(signal, w) | Element-wise multiply |
| coherentGain(w) | Mean of window coefficients |
| getWindow(name, n) | Get window by name |
Filters (FIR)
| Function | Description |
|----------|-------------|
| lowpassFilter(cutoff, order, window?) | Lowpass FIR coefficients |
| highpassFilter(cutoff, order, window?) | Highpass FIR coefficients |
| bandpassFilter(lo, hi, order, window?) | Bandpass FIR coefficients |
| bandstopFilter(lo, hi, order, window?) | Bandstop (notch) FIR coefficients |
| applyFilter(signal, coeffs) | Apply FIR filter (causal) |
| convolve(a, b) | Direct linear convolution |
Signal utilities
| Function | Description |
|----------|-------------|
| generateSine(f, amp, sr, n, phase?) | Sine wave |
| generateCosine(f, amp, sr, n, phase?) | Cosine wave |
| generateSquare(f, amp, sr, n, harmonics?) | Square wave |
| generateNoise(amp, n) | White noise |
| zeroPad(signal, length) | Zero-pad to length |
| linspace(start, end, n) | Evenly-spaced values |
| mean(signal) | Arithmetic mean |
| removeDC(signal) | Subtract mean |
| normalize(signal) | Scale peak to 1 |
| toDb(linear) | Amplitude → dB |
| fromDb(db) | dB → amplitude |
Performance
| Operation | Complexity |
|-----------|-----------|
| fft(n) | O(n log n) |
| ifft(n) | O(n log n) |
| fftConvolve(a, b) | O(N log N), N = nextPow2(a+b-1) |
| convolve(a, b) | O(a.length × b.length) — fine for FIR taps |
| applyFilter(signal, h) | O(signal.length × h.length) |
| Window functions | O(n) |
Contributors ✨
License
MIT © trananhtung
