| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Try it in the browser: Key and BPM finder. Runs on this package, nothing is uploaded.
Onset detection, tempo estimation, and beat tracking. · live demo ↗
|
Onset Detection |
npm install @audio/beat
import { detect, onsets, tempo, beatTrack } from '@audio/beat'
// full pipeline: onsets → tempo → beat grid
let result = detect(samples, { fs: 44100 })
result.bpm // 120.2
result.beats // Float64Array [0.01, 0.51, 1.01, ...]
result.onsets // Float64Array [0.01, 0.26, 0.51, ...]
// individual algorithms
let ons = onsets(samples, { fs: 44100 }) // onset times in seconds
let tmp = tempo(samples, { fs: 44100 }) // { bpm, confidence }
let bt = beatTrack(samples, { fs: 44100 }) // { beats, bpm, confidence }Works in Node.js and browser. No Web Audio API needed — operates on raw Float32Array / Float64Array samples.
Mono only. Pass a single channel. For stereo, use one channel (buffer.getChannelData(0)) or mix to mono first.
┌──────────────────────────────────────────┐
│ raw samples │
└──────────┬───────────────────┬────────────┘
│ │
┌──────────▼──────────┐ ┌─────▼──────────┐
│ Onset Detection │ │ Tempo Estimation│
│ │ │ │
│ STFT → magnitude → │ │ autocorrelation│
│ positive flux → │ │ or comb-filter │
│ adaptive threshold │ │ of ODF │
└──────────┬──────────┘ └─────┬──────────┘
│ │
│ ┌──────────────┘
│ │
┌──────────▼────▼─────────────────────────┐
│ Beat Tracking │
│ │
│ phase-aligned grid (detect) │
│ or DP optimal sequence (beatTrack) │
└──────────────────────────────────────────┘
Onset detection finds when events happen — note attacks, drum hits, transients. Four algorithms with different trade-offs.
Tempo estimation finds how fast — the dominant periodicity in the onset function. Two methods: autocorrelation and comb-filter resonance.
Beat tracking combines both: given onsets and tempo, find the beat positions. detect snaps a grid to onsets; beatTrack uses dynamic programming to find the globally optimal beat sequence.
All onset functions take raw samples and return onset times in seconds as Float64Array.
Spectral flux. STFT → magnitude → sum positive frame-to-frame differences → adaptive threshold peak-pick. The general-purpose default.
import { onsets } from '@audio/beat'
let ons = onsets(samples, { fs: 44100 })| Param | Default | |
|---|---|---|
| fs | 44100 | Sample rate |
| frameSize | 2048 | STFT window size |
| hopSize | 512 | Hop between frames |
| delta | 1.4 | Adaptive threshold multiplier |
| windowSize | 8 | Peak-pick local mean window (frames) |
Use when: General-purpose onset detection — music, speech, mixed material.
Ref: Dixon, "Onset Detection Revisited" (DAFx 2006).
Complexity: $O(N \log F)$ where $N$ = samples, $F$ = frame size (FFT).
Energy flux. Per-frame RMS energy → positive first differences → adaptive threshold. No FFT — fastest algorithm. Best for strong transients.
import { energyOnsets } from '@audio/beat'
let ons = energyOnsets(samples, { fs: 44100 })Same params as onsets.
Use when: Percussive material, real-time, CPU-constrained — 10× faster than spectral flux.
Not for: Soft onsets, pitched instruments with gradual attacks.
Ref: Klapuri, "Auditory Model Based Beat Tracking" (ICMC 1999).
Complexity: $O(N)$ — no FFT, just RMS per frame.
Phase deviation. Measures divergence between predicted and actual STFT phase, weighted by magnitude. More robust to steady-state signals (sustained notes, drones) than spectral flux.
import { phaseOnsets } from '@audio/beat'
let ons = phaseOnsets(samples, { fs: 44100 })Same params as onsets.
Use when: Material with sustained tones where spectral flux produces false onsets.
Not for: Speed-critical paths — requires complex FFT (2× cost of spectral flux).
Ref: Bello et al., "A Tutorial on Onset Detection in Music Signals" (IEEE TASLP 2005).
Complexity: $O(N \log F)$ with complex FFT.
Multi-band spectral flux. Splits spectrum into frequency bands, computes spectral flux per band, sums. Detects onsets across different instrument ranges simultaneously.
import { bandOnsets } from '@audio/beat'
let ons = bandOnsets(samples, { fs: 44100, bands: 6 })| Param | Default | |
|---|---|---|
| bands | 4 | Number of frequency bands |
| + all onsets params |
Use when: Full-band music where low-frequency kicks and high-frequency hats produce onsets that a single-band detector misses.
Ref: Klapuri, "Sound Onset Detection by Applying Psychoacoustic Knowledge" (ICASSP 1999).
Complexity: $O(N \log F)$ — same FFT cost, slightly more post-processing.
| Speed | Transients | Soft onsets | Steady-state | Best for | |
|---|---|---|---|---|---|
| onsets | ★★★ | ★★★★ | ★★★ | ★★ | General purpose |
| energyOnsets | ★★★★★ | ★★★★★ | ★ | ★ | Percussive, real-time |
| phaseOnsets | ★★ | ★★★ | ★★★★ | ★★★★ | Sustained tones |
| bandOnsets | ★★★ | ★★★★ | ★★★ | ★★★ | Full-band music |
Both return { bpm, confidence }. Pass candidates: N to get ranked alternatives.
Autocorrelation of the onset detection function. Finds the dominant periodicity by correlating the spectral flux ODF with itself at different lags. Perceptual weighting (log-Gaussian centered at 120 BPM) resolves octave ambiguity.
import { tempo } from '@audio/beat'
let { bpm, confidence } = tempo(samples, { fs: 44100 })
let { bpm, candidates } = tempo(samples, { fs: 44100, candidates: 3 })| Param | Default | |
|---|---|---|
| fs | 44100 | Sample rate |
| minBpm | 60 | Minimum BPM to consider |
| maxBpm | 200 | Maximum BPM to consider |
| candidates | 1 | Number of tempo candidates to return |
Use when: General tempo estimation — robust for most material.
Ref: Ellis, "Beat Tracking by Dynamic Programming" (JNMR 2007).
Complexity: $O(N \log F + L^2)$ where $L$ = lag range in ODF frames.
Comb-filter resonance. Tests BPM hypotheses by correlating the ODF with raised-cosine pulse trains at each candidate tempo (+ harmonics). Returns the BPM with highest resonance.
import { combTempo } from '@audio/beat'
let { bpm, confidence } = combTempo(samples, { fs: 44100 })Same params as tempo.
Use when: Cross-validation with autocorrelation, or when the signal has strong harmonic tempo structure.
Ref: Scheirer, "Tempo and Beat Analysis of Acoustic Musical Signals" (JASA 1998).
Complexity: $O(N \log F + B \cdot L)$ where $B$ = BPM range tested.
Full pipeline: spectral flux onsets → comb-filter tempo → phase-aligned beat grid. Shares a single STFT pass across onset and tempo stages, so it costs only marginally more than either alone.
import { detect } from '@audio/beat'
let { bpm, confidence, beats, onsets } = detect(samples, { fs: 44100 })Returns { bpm, confidence, beats: Float64Array, onsets: Float64Array }.
Use when: Quick one-call solution — good enough for most applications.
Not for: Tempo changes or rubato — the grid is uniform. Use beatTrack for adaptive tracking.
Dynamic programming beat tracker. Estimates tempo (via autocorrelation), then finds the globally optimal beat sequence by maximizing onset strength while penalizing tempo deviation. Each beat position is placed where the onset function is strongest, subject to staying near the expected tempo period.
import { beatTrack } from '@audio/beat'
let { beats, bpm, confidence } = beatTrack(samples, { fs: 44100 })
let result = beatTrack(samples, { fs: 44100, bpm: 120 }) // hint tempo| Param | Default | |
|---|---|---|
| bpm | auto-estimated | Target BPM (auto-estimated if omitted) |
| tightness | 680 | Tempo constraint weight (higher = stricter) |
| + all tempo params |
Returns { beats: Float64Array, bpm, confidence }.
Use when: Irregular timing, live performance, rubato — adapts to where beats actually fall.
Not for: Perfectly metronomic material — detect is faster and sufficient.
Ref: Ellis, "Beat Tracking by Dynamic Programming" (JNMR 2007).
Low-level building blocks. Used internally, exported for custom pipelines.
STFT → magnitude → sum positive differences. Returns the onset detection function (ODF) as { odf, nFrames, hopSize, frameSize, fs }.
import { spectralFlux } from '@audio/beat'
let { odf, nFrames, hopSize } = spectralFlux(samples, { fs: 44100 })Per-frame RMS energy → positive first differences. Returns { odf, nFrames, hopSize, frameSize, fs }.
Adaptive threshold peak-picker. Local mean × delta → pick peaks above threshold. Returns onset times in seconds.
import { spectralFlux, peakPick } from '@audio/beat'
let { odf, hopSize, fs } = spectralFlux(samples, { fs: 44100 })
let onsets = peakPick(odf, { hopSize, fs, delta: 1.5 })All algorithms run 250–8000× faster than real-time on a single core (16.5s signal, 44.1 kHz, Node.js):
| Algorithm | Time | Throughput | Real-time multiple |
|---|---|---|---|
| energyOnsets | 2 ms | 362 Msamp/s | 8200× RT |
| onsets | 19 ms | 38 Msamp/s | 850× RT |
| tempo | 19 ms | 39 Msamp/s | 890× RT |
| bandOnsets | 21 ms | 35 Msamp/s | 790× RT |
| combTempo | 25 ms | 29 Msamp/s | 660× RT |
| detect | 37 ms | 20 Msamp/s | 450× RT |
| beatTrack | 37 ms | 20 Msamp/s | 450× RT |
| phaseOnsets | 63 ms | 12 Msamp/s | 260× RT |
Full 8-algorithm pass on 8.5s audio completes in ~150ms in-browser.
Systematic benchmark across 10 musical styles × 10 tempos (70–180 BPM) = 100 cases per method.
| Method | Acc1 (%) | Acc2 (%) | MAE (BPM) | Octave errors |
|---|---|---|---|---|
| tempo | 70 | 87 | 19.5 | 16 |
| combTempo | 87 | 93 | 8.7 | 6 |
| detect | 87 | 93 | 8.7 | 6 |
| beatTrack | 87 | 93 | 8.7 | 6 |
detect, beatTrack, and combTempo all share a single STFT pass — they're as accurate as each other, and all better than the autocorrelation baseline. Use detect for uniform grids, beatTrack for adaptive (rubato-aware) placement.
tempo (autocorrelation) varies ±2% between runs due to random noise in the FM synth. Comb-filter methods are stable.
Fully self-contained: FM-synthesis drum patterns (synth.js) generate deterministic test signals across 10 styles × 10 BPMs. No external audio files. Also tests against a deterministic floatbeat track (125 BPM). Run node test.js to reproduce.
| Back | FazBrowse Home | New Git URL |