Library / SDK
chrisguttandin/web-audio-beat-detector avatar
chrisguttandin/web-audio-beat-detector

web-audio-beat-detector: tempo detection in the browser

A beat detection utility which is using the Web Audio API.

680 stars27 forksJavaScriptMIT

At a glance

What is it?
web-audio-beat-detector estimates the tempo of a piece of audio from an AudioBuffer, using the Web Audio API and a technique published by Joe Sullivan. It exposes two functions, one that returns the tempo and one that also returns the offset of the first beat, and the heavy lifting runs in a web worker rather than on the main thread.
Who is it for?
web-audio-beat-detector fits a browser application that needs the tempo of a track it already has in memory, such as a player that wants to sync a visual to the beat, and that does not want to upload the audio anywhere. It does not fit a server side pipeline, because the whole implementation is built on the browser's audio context, and it does not fit a case where a wrong tempo estimate is expensive, because the algorithm is deliberately the simple one.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 54 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

One function, one promise

web-audio-beat-detector is a beat detection utility that uses the Web Audio API, and the whole public surface is small enough to describe in a paragraph.

It is published on npm and installed like any other package.

shell
npm install web-audio-beat-detector

Then you import the public function, and the import is a named one from the package root.

js
import { analyze } from 'web-audio-beat-detector';

The function takes an AudioBuffer as its first parameter and returns a promise that eventually resolves with the tempo of that buffer as a number. The usage example is the shape every caller will write.

js
analyze(audioBuffer)
    .then((tempo) => {
        // the tempo could be analyzed
    })
    .catch((err) => {
        // something went wrong
    });

Two details in that are worth naming. The input is an AudioBuffer, which means the caller has already decoded the audio, and the output is a promise, which means the analysis does not happen synchronously on the calling thread. Both are consequences of the same design, which is that the work runs somewhere else.

That is visible in the dependencies, because the package pulls in a broker and a worker package of its own. The analysis is executed in a web worker and coordinated through the broker, so a page analysing a track does not block its own user interface while it does it. That is a deliberate choice about where the arithmetic runs rather than about what it does.

The project is MIT licensed, and the release history shows a steady patch line, with 8.2.39 in August 2026, 8.2.38 in July, and 8.2.37 in June, and the last push was on 2026-08-30.

Two functions, and the difference between them

There is a second exported function, and the difference between the two is the useful part of the API.

js
import { guess } from 'web-audio-beat-detector';

The guess function also takes an AudioBuffer and also returns a promise, but the promise resolves with an object rather than a number. The object contains the estimated BPM, which is the rounded tempo, and the offset of the first beat in seconds.

js
guess(audioBuffer)
    .then(({ bpm, offset, tempo }) => {
        // the bpm and offset could be guessed
        // the tempo is the same as the one returned by analyze()
    })
    .catch((err) => {
        // something went wrong
    });

So the two functions are the same computation with different packaging. The tempo in the result of guess is the same number that analyze returns on its own, and the two extra fields are the rounded value and the position of the first beat.

That offset is what makes a beat detector useful for anything visual. A tempo tells you how often the beats arrive, and an offset tells you when the first one arrives, and together they let you place a flash, a step, or a transition on the beat rather than approximately near it. A tempo on its own is a number to display; a tempo and an offset is a timeline.

The rounding is also deliberate. The raw tempo is a real number with a lot of decimal places, and the rounded BPM is the one a user interface or a beat grid wants, so the library gives you both rather than making you choose.

Offset and duration, and the convention they follow

Both functions accept an offset and a duration as optional arguments, and when they are given they select only that part of the buffer.

The documentation is explicit about the convention, and it is the right one to follow: the usage is the same as in the documentation of the start method of the audio buffer source node, the browser method that plays a region of a buffer.

That is a good decision, because the browser already has a vocabulary for this. The offset is where the region starts, measured in seconds from the beginning of the buffer, and the duration is how long the region is. A caller who has already written code that starts a buffer at a given offset knows exactly how to pass these two numbers here.

The arguments are positional, and they come before the options object, so the two calling shapes are shown side by side in the documentation. One passes the offset and the duration and then the tempo settings, and the other passes only the settings.

js
// with an offset and duration
analyze(audioBuffer, 1, 10, { maxTempo: 120, minTempo: 60 });
// with no other arguments
analyze(audioBuffer, { maxTempo: 120, minTempo: 60 });

The useful case for this is an intro. A track that spends thirty seconds on an ambient passage and then drops into a tempo is a track whose overall BPM is the BPM of the drop, and analysing only the section you care about gives a better answer than analysing everything.

The tempo range, and why it matters

By default the detector expects the tempo to be between ninety and one hundred and eighty beats per minute, and that expectation is configurable through the tempo settings, which are passed as the last argument.

The settings object has a maximum tempo and a minimum tempo, and the example sets them to one hundred and twenty and sixty. The documentation is clear that these are the bounds of the search rather than a filter on the result, because the algorithm has to choose a candidate within them.

This is the single most important thing to know about the library, and it is the kind of default that produces a confidently wrong number rather than an error. A track at seventy beats per minute analysed with the default settings will not report seventy, and it will not report that it failed. It will report the best candidate it found in the range it was given.

So the rule for a caller is simple: if you know the kind of music you are analysing, set the range to match it. Half time and double time material is the usual trap, because a half time track at eighty beats a minute has a real tempo of one hundred and sixty, and a detector asked for ninety to one hundred and eighty has to choose which of the two it means.

And the other side of the same rule is that the defaults are a reasonable middle of the road range, which is why the library ships with them at all. Electronic music, which is where the algorithm is strongest, sits inside ninety to one hundred and eighty for most of what gets played.

The technique, and where it is strong

The readme credits the technique to Joe Sullivan and links the article in which it is explained, beat detection using JavaScript and the Web Audio API, and that attribution is more than a courtesy, because it tells you what kind of algorithm you are running.

The library describes the algorithm as not as complex, and not as expensive to compute, as many others. It says the result is surprisingly good, and specifies where: especially for electronic music.

So the trade is explicit. This is a simple method, chosen for the cost of running it, and the domain where it holds up best is material with a strong, steady pulse, which is exactly what electronic music provides. Material with a rubato tempo, a solo passage that drifts, or a recording with a lot of human noise on the transient is the case where a more expensive method would earn its cost.

The readme also acknowledges a more comprehensive implementation, by José M. Pérez, which comes with a user interface to search for tracks on a streaming service and then analyse them, and a blog post about detecting tempo with the browser's audio API. That is a useful pointer for a reader deciding between the two, because the comparison is not only about accuracy but about what each project is for: a library you call, and a tool with an interface around it.

For most browser use, calling a library is the right shape, and the acknowledged alternative is there for the cases where the extra machinery is what you actually wanted.

How the package is put together

The repository is small, and its layout says what the project is.

There is a source directory, a test directory, a configuration directory, and the usual set of files, with a husky directory for the git hooks, a licence, and a readme. The commit history is kept tidy by a commitizen configuration pointing at the conventional changelog package, which is why the release notes are readable.

The dependency list shows the shape of the build. The runtime dependencies are the Babel runtime helpers, the TypeScript helper library, and the two packages that carry the worker and the broker. There is no audio processing library, no machine learning runtime, and no numerical package, which confirms the claim that the algorithm is simple, and that the library is a small amount of arithmetic in the right place.

The development dependencies are the other half of the story. The Babel toolchain with the environment preset and the runtime plugin, a rollup build with the Babel and replace plugins, and the terser plugin for minification, which is the standard shape for a package that ships to browsers.

Two entries stand out. The test setup uses the browser mode of the test runner driven by WebdriverIO, because the code under test needs a real browser audio context rather than a stubbed one. And the standardised audio context package is present, which is the compatibility layer that gives the browser's audio implementation a consistent behaviour across engines, which is what you need if you want the tempo to be the same number in every browser.

The linting is done with a shared configuration, the formatting with Prettier, the commit hooks with husky and lint staged, and the type checking with a shared TypeScript configuration. It is the setup of a package that is developed seriously and published often.

Editorial conclusion

web-audio-beat-detector fits a browser application that needs the tempo of a track it already has in memory, such as a player that wants to sync a visual to the beat, and that does not want to upload the audio anywhere. It does not fit a server side pipeline, because the whole implementation is built on the browser's audio context, and it does not fit a case where a wrong tempo estimate is expensive, because the algorithm is deliberately the simple one. Verify first that the tempo of your material is inside the range the detector expects, which is ninety to one hundred and eighty beats per minute unless you change it, that the audio you feed it is decoded into an AudioBuffer rather than a stream, and that the worker based packaging works in the browsers you target, since the analysis does not run on the main thread.

Frequently asked questions

What does web-audio-beat-detector do?

It estimates the tempo of a piece of audio from an AudioBuffer using the Web Audio API, and it exposes two functions. The analyze function resolves with the tempo as a number, and the guess function resolves with an object with the rounded BPM, the offset of the first beat in seconds, and the same tempo value.

How do I install web-audio-beat-detector?

With npm install web-audio-beat-detector, or the equivalent for your package manager, and then import the function you want by name from the package root, for example the analyze or the guess function.

How do I analyse only part of a track with web-audio-beat-detector?

Both functions accept an offset and a duration as optional positional arguments, and they select that part of the AudioBuffer. The convention is the same as the start method of the audio buffer source node, so the offset is the start of the region in seconds and the duration is its length.

What tempo range does web-audio-beat-detector expect?

By default it expects the tempo to be between ninety and one hundred and eighty beats per minute. The range is configurable through the tempo settings passed as the last argument, with a maximum tempo and a minimum tempo, and the values bound the search rather than filtering the result.

On what kind of music does the web-audio-beat-detector algorithm work best?

The readme says the algorithm is chosen because it is not as complex or expensive to compute as many others, and that it yields surprisingly good results especially for electronic music. A more comprehensive implementation by another author is acknowledged in the readme, with a user interface for searching tracks and analysing them.

Official sources

  1. chrisguttandin/web-audio-beat-detector on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/chrisguttandin-web-audio-beat-detector.svg)](https://hysenlabs.com/projects/chrisguttandin-web-audio-beat-detector)

Was this useful?