How to detect bpm in your browser — local web audio analysis
- Step 1Drop a file into the browser tab — Drag any audio file onto the drop zone. The bytes stay on your device — nothing is sent anywhere. There are no settings to choose.
- Step 2Local decode via Web Audio —
AudioContext.decodeAudioDatadecodes the file to PCM using the browser's own codecs. No native binary, no server. - Step 3Onset extraction runs on the main thread — The PCM is downmixed to mono, downsampled to 11.025 kHz, and reduced to a per-10 ms onset signal — a compact numeric representation of where the beats hit.
- Step 4Autocorrelation on GPU or CPU — The onset signal is autocorrelated across the 60-180 BPM lag range. WebGPU runs it on the graphics card if present; otherwise the same loop runs on the CPU.
- Step 5Inspect the gpuAccelerated flag — The report's
gpuAcceleratedfield (and the GPU badge in the UI) confirms which path ran. The BPM is the same regardless. - Step 6Copy or download the JSON locally — Save the report as
<name>-bpm.jsonor copy it. It never touched a network.
What actually runs in the browser
BPM Finder does NOT use FFmpeg. The whole pipeline is Web Audio decode plus a numeric onset-autocorrelation pass — that is why the result is a JSON report, not a re-encoded audio file. WebGPU is tried first for the autocorrelation loop; if WebGPU is unavailable the identical math runs on the CPU.
| Stage | What happens | Why |
|---|---|---|
| Decode | AudioContext.decodeAudioData decodes the file to PCM in the browser | Reuses the browser's own codec — MP3/WAV/M4A/OGG everywhere; FLAC/Opus depend on the browser |
| Downmix | All channels are averaged to mono | Tempo lives in the rhythm, not the stereo image — mono halves the work |
| Downsample | Resampled to 11.025 kHz via an OfflineAudioContext | A kick drum sits below 200 Hz, so high frequencies are irrelevant and dropping them bounds CPU cost regardless of source quality |
| Energy + onset | Energy is summed per 10 ms window; the onset signal is the positive frame-to-frame energy increase (an HFC-lite novelty function) | Beats show up as sudden energy jumps — this isolates them |
| Autocorrelation | The onset signal is autocorrelated across lags equivalent to 60-180 BPM; the top 5 scoring lags are converted to BPM | A repeating beat produces a periodic onset signal; the strongest correlation lag is the tempo period |
| Confidence | confidence = 1 - (second-best score / best score), rounded to 2 decimals | A clear winner over the runner-up means high confidence; two near-tied candidates means low confidence |
Fields in the JSON report
BPM Finder writes a small JSON object. It is shown in a scrollable panel with Copy and Download buttons; Download saves it as <name>-bpm.json. There are no options to set — the tool needs no configuration.
| Field | Type | Meaning |
|---|---|---|
bpm | number | The top-scoring tempo, rounded to one decimal (e.g. 128, 92.5) |
confidence | number 0-1 | How far the top candidate beat the runner-up; 1 - second/best, 2 decimals |
candidates | array of 5 | The five highest-scoring tempos, each { bpm, score }. score is the raw autocorrelation sum (unitless — only useful for comparing candidates within one run) |
gpuAccelerated | boolean | true when the autocorrelation ran on the GPU via WebGPU; false when it fell back to the CPU. The result is identical either way |
Tier limits for BPM detection
BPM Finder is a free-tier tool and the analysis never re-encodes audio. The limits below are the audio-family per-file caps from the JAD tier table — note durationMin is a per-file length cap, separate from file size.
| Tier | Max file size | Max duration / file | Files per batch |
|---|---|---|---|
| Free | 50 MB | 30 min | 1 file |
| Pro | 200 MB | 120 min | 10 files |
| Pro-media | 100 GB | unlimited | 100 files |
| Developer | 100 GB | unlimited | unlimited |
Cookbook
Worked examples focused on the client-side, no-server behaviour. JSON is the real report shape (numbers illustrative).
Same file, GPU machine vs CPU-only machine
WebGPU only changes where the autocorrelation runs, never the answer. The gpuAccelerated flag flips; the BPM does not.
Desktop with WebGPU:
{ "bpm": 128, "confidence": 0.64, "gpuAccelerated": true }
Same file on a CPU-only laptop:
{ "bpm": 128, "confidence": 0.64, "gpuAccelerated": false }
Identical bpm and confidence — GPU is a speed path only.Confirming nothing is uploaded
Open the browser DevTools Network tab, drop a file, and watch: there is no request carrying the audio. Decoding and analysis are pure JS/WebGPU.
DevTools > Network, then drop track.wav: (no POST of audio bytes appears) (no XHR/fetch to an analysis endpoint) The only network traffic is the static page + WASM-free JS. Takeaway: works even with the tab offline after load.
Reproducibility check
Because every parameter is fixed, re-running the same file is deterministic — useful when scripting a manual QA pass.
Run 1: { "bpm": 95, "confidence": 0.41, ... }
Run 2: { "bpm": 95, "confidence": 0.41, ... }
Run 3: { "bpm": 95, "confidence": 0.41, ... }
No randomness, no model weights — same input, same output.Why it's a JSON report, not an audio file
BPM Finder is an analysis tool — it reads the file but never re-encodes it. That is the structural difference from the FFmpeg-backed converters.
Input: song.mp3 (unchanged on disk) Output: song-bpm.json (a few hundred bytes of analysis) Contrast with a converter (mp3-to-wav), which DOES re-encode the audio via FFmpeg WASM and outputs a new audio file.
Handing the number to a re-time tool
Detection and editing are separate tools. Read the BPM here, then re-time elsewhere — all still local in the browser.
BPM Finder: detects 90 BPM
Goal: 96 BPM for a workout-mix segment
96 / 90 = 1.067 -> tempo-changer at ~107%,
pitch preserved automatically.Edge cases and what actually happens
Tempo sits outside the 60-180 BPM search window
Octave-foldedThe autocorrelation only searches lags equivalent to 60-180 BPM. A 50 BPM ballad or a 190 BPM drum-and-bass track can't land outside that window, so the reported value is the half- or double-time fold that falls inside it (a 190 BPM track reads ~95). Inspect the candidates list — the true tempo is usually present as a halved or doubled relative of the top result.
Half-time / double-time ambiguity on a steady track
By designAutocorrelation cannot distinguish a backbeat from the main pulse: 140 and 70 are mathematically both valid periods of the same beat grid. The detector picks the strongest correlation, which is sometimes the half or double. Both values appear in candidates; pick the one that matches the genre you expect.
Speech, podcast, or ambient audio with no beat
UnreliableThe algorithm needs a periodic onset pattern. Dialogue, field recordings, and pad-only ambient have no repeating energy spikes, so the autocorrelation finds no clear peak — confidence collapses toward 0 and the bpm is effectively noise. For voice work you want the silence-stripper or speech-leveler, not a tempo reading.
Rubato, live, or tempo-drifting performance
Dominant tempo onlyOrchestral rallentando, a live band speeding up, or free-time jazz have no single tempo. The detector returns the dominant period across the whole file and silently misses the drift. Trim to one steady section with the audio-trimmer and re-run if you need a per-section reading.
Browser can't decode the format (FLAC/Opus on older Safari)
Decode errorDecode uses the browser's own decodeAudioData. Safari historically refused FLAC and pre-17 Safari refused Opus. If decode throws, the analysis can't start. Convert to WAV or MP3 first with wav-to-mp3 or flac-to-mp3, then run BPM Finder on the result.
Free-tier file is over 50 MB or 30 minutes
LimitFree tier caps each file at 50 MB and 30 minutes of duration — the duration cap is separate from size, so a long low-bitrate MP3 can hit it under the byte limit. Pro raises this to 200 MB / 120 min, Pro-media to 100 GB / unlimited. A single 3-4 minute song is never near the free cap.
Very short clip (a loop of a few seconds)
Low confidenceAutocorrelation needs enough repetitions of the beat to build a strong peak. A 2-3 second loop may produce a reasonable bpm but a thin confidence. Feed at least one full musical phrase (8-16 bars) for a stable reading.
Click track or metronome
SupportedA bare metronome is the ideal input — each click is a clean onset, the autocorrelation peak is sharp, and confidence runs high. This is a good way to sanity-check the tool against a known tempo.
Polyrhythmic or layered-tempo material
Dominant layer winsIf two rhythmic layers run at related tempos, the autocorrelation reports whichever produces the stronger periodic onset energy. Competing candidates show up in the candidates array with close scores and a low confidence — that closeness is your signal that the track is ambiguous.
Frequently asked questions
Does my audio get uploaded?
No. Decoding and the autocorrelation pass both run in your browser tab — the file's bytes never leave your device. Unlike many online BPM sites, there is no server round-trip, so there is nothing to upload and nothing to delete afterward.
Does this use FFmpeg like the other JAD audio tools?
No — and this is a common misconception. The converters and editors use FFmpeg compiled to WebAssembly, but BPM Finder is pure Web Audio: decodeAudioData plus a numeric onset-autocorrelation pass. That is why it outputs a JSON report instead of re-encoded audio, and why the metrics badge shows only GPU (when WebGPU ran), never the MT/SIMD flags the FFmpeg tools show.
What does the confidence number mean?
Confidence is 1 - (second-best candidate score / best candidate score). A value near 1 means the top tempo dominated every rival; a value near 0 means two or more tempos scored almost the same (usually a half/double-time tie). It is a relative separation measure, not a probability — treat anything under ~0.2 as 'verify by ear'.
Why are there five candidates instead of one answer?
The detector keeps the top five autocorrelation peaks so you can resolve the half-time / double-time ambiguity yourself. The true tempo is almost always either the top result or a 2x / 0.5x relative of it sitting lower in the list. The score field lets you see how close the runners-up were.
What's the supported BPM range?
The autocorrelation searches lags equivalent to 60-180 BPM. Tempos outside that window get folded into it: a 50 BPM track reads near 100, a 190 BPM track reads near 95. That covers the vast majority of pop, rock, electronic, and hip-hop. For extreme tempos, read the value as a half/double of the candidate that matches your genre.
What file formats can I analyse?
Whatever your browser can decode: MP3, WAV, FLAC, M4A, OGG, Opus, and the audio track inside common video files. Decoding relies on the browser's built-in codecs, so FLAC and Opus support depends on the browser (Chrome and recent Safari handle both; very old Safari does not). If a format won't decode, convert it to WAV or MP3 first.
How fast is it?
A 3-4 minute song typically resolves in under two seconds on a modern laptop. The pipeline downmixes to mono and downsamples to 11.025 kHz before any heavy math, so analysis time barely changes whether the source is a 128 kbps MP3 or a 24-bit/96 kHz WAV.
Does WebGPU change the answer?
No. WebGPU only moves the autocorrelation loop onto the graphics card for speed; the math is bit-for-bit the same as the CPU path. The gpuAccelerated flag in the report (and the GPU badge in the UI) just tells you which path ran. On machines without WebGPU it silently uses the CPU and you get the identical BPM.
Can I change the analysis settings?
No — BPM Finder has no options. The window size, downsample rate, and search range are fixed for reliable, repeatable results, so there are no sliders or presets to get wrong. You drop a file and read the report.
It reported half (or double) the tempo I expected — is it broken?
No, that's the half-time / double-time ambiguity inherent to autocorrelation: a beat at 140 BPM is also perfectly periodic at 70 BPM. Look at the candidates array — the value you expected is almost certainly listed there as a 2x or 0.5x of the reported bpm. Pick the one that fits the genre.
Can I re-time a track once I know its BPM?
Yes. Take the detected BPM, then use the tempo-changer to nudge the track to a target tempo (expressed as a percentage), or the time-stretcher to stretch by a factor without shifting pitch. To see the rhythm you measured, render it with the waveform-generator.
Do I need an account?
No. BPM Finder is a free-tier tool with no sign-in required. Signing in only raises the file-size and duration caps (Pro and above) and records a private processed-file counter for dashboard stats — never the audio itself.
Does it work offline?
Once the page has loaded, yes — the analysis is pure in-browser JavaScript and WebGPU with no server dependency, so you can disconnect and still detect BPM. (Decoding still relies on the codecs already built into your browser.)
Which browsers support the WebGPU acceleration?
Recent Chrome, Edge, and Safari expose WebGPU; where it's missing the tool silently uses the CPU path and you get the same BPM, just a little slower on long files. The gpuAccelerated flag in the report tells you which one ran on your machine.
Privacy first
Every JAD Audio tool runs entirely in your browser via FFmpeg (WebAssembly) and RNNoise. Your audio files never leave your device — verified by zero outbound network requests during processing.