BPM Detector
Drop in a track. Get its tempo and the exact time of every beat — computed on your own machine, with nothing uploaded.
What it measures
Tempo detection starts by finding onsets — the moments when energy rises sharply across the spectrum, which is what a drum hit, a plucked string or a syllable looks like to a machine. Those moments form a jagged curve over time. A steady pulse shows up as a repeating pattern in that curve, and the strongest period is the tempo.
Knowing the tempo is not the same as knowing where the beats fall, so a second pass places a grid: it searches for the sequence of beat positions that both lands on strong onsets and stays evenly spaced, trading one against the other. That is why the beats returned here sit on the music rather than on a metronome started at zero.
Why it runs in your browser
Every comparable tool asks you to upload the track first. That is a consequence of how they are built, not a requirement of the problem — the analysis is a few million floating-point operations, which a phone does without noticing.
This one is the montage engine from Life2Film, compiled to WebAssembly. The same code decides where to cut when the app assembles a video, and it runs on your device there for the same reason it does here: sending someone’s footage to a server to answer a question the device can answer is a cost, not a feature.
Your file is read and re-encoded by your own browser. No request carries it anywhere — this page has no upload endpoint to send it to.
Questions
Does my audio get uploaded anywhere?
No. The analysis runs inside your browser as WebAssembly. The file is read from disk into memory and never sent over the network — you can check by opening the network tab, or by pulling the plug once the page has loaded.
How accurate is it?
It uses onset-strength analysis with dynamic-programming beat tracking — the same approach behind established music-information-retrieval tooling, and the same code that drives Life2Film’s montage engine. On music with a steady pulse it lands within a beat of the truth. On rubato, live recordings without a click, or heavily syncopated material, treat the number as a starting point.
What does “half or double tempo” mean?
Tempo is ambiguous by nature: a track at 140 BPM is also, correctly, 70 BPM counted in half time. Detectors pick whichever is stronger in the signal. If the number feels off by exactly 2×, that is what happened, and both answers are shown so you can take the one you need.
Which formats work?
Whatever your browser can decode: MP3, WAV, M4A, AAC, FLAC and OGG in current Chrome, Edge, Safari and Firefox. Video files work too — the audio track is what gets read.
Can I use the beat times in my editor?
That is what the export is for. EDL is the one to pick for DaVinci Resolve or Premiere Pro — each beat arrives as a named marker on the timeline. Final Cut takes the FCPXML. OTIO works in Resolve, Avid and Premiere too, and is the right choice if the beats are heading into a pipeline rather than straight onto a timeline. Audacity reads the label export, and CSV or JSON cover spreadsheets and scripts.
Why does the frame rate matter?
Beats fall on fractions of a second; timelines count whole frames. Every marker has to be rounded to the nearest frame of the project it is going into, so a grid exported at 25 fps will sit up to 20 ms off in a 30 fps sequence. Set the rate to match your project and the markers land exactly where the beat is. It does not affect CSV or JSON, which carry raw seconds as well.
The other tools
Build this into your own project
Finding the tempo and beat grid of a track — the same way this page does it, on the user's machine, with no server. Take the code, or hand the prompt to a coding agent.
const engine = await import('https://life2film.com/wasm/va_wasm.js');
await engine.default({ module_or_path: 'https://life2film.com/wasm/va_wasm_bg.wasm' });
const ctx = new AudioContext();
const buffer = await ctx.decodeAudioData(await file.arrayBuffer());
// Down-mix to mono at 22050 Hz first (OfflineAudioContext), then:
const { bpm, beats } = JSON.parse(engine.detect_beats(mono, 22050, null)); Add beat detection to my project. It must run in the browser with no upload and no API key.
Requirements:
- Use the "life2film-engine" npm package (WebAssembly). detect_beats takes mono f32 PCM, which is
exactly what AudioContext.decodeAudioData gives you.
- Down-mix to mono at 22050 Hz with an OfflineAudioContext before analysing. The onset envelope lives
far below that, so the source rate costs several times the memory and time for an identical answer.
- Tempo is genuinely ambiguous: 140 BPM is also 70 in half time. Show both readings rather than
treating a 2x difference as an error.
- Draw the beats over a waveform so the user can check them, but show a WINDOW of a few seconds, not
the whole track — 400 beats across 1000 pixels is a solid block that proves nothing.
- Let the user play the track with a click on every beat. It is the fastest way to verify a grid.
- Export the grid for editors: EDL for Resolve and Premiere, FCPXML for Final Cut, OTIO for
pipelines, Audacity labels, CSV, JSON. Markers land on whole frames, so ask for the project frame
rate — a grid at 25 fps sits up to 20 ms off in a 30 fps sequence.
Reference implementation: https://life2film.com/tools/bpm-detector/
The engine is documented for agents, and every tool page is available as
markdown by adding .md to its address.