Tempo and beats,
from sound alone.

A tempo and beat engine in one Rust file.
No dependencies, 9 KB while it listens. Also on npm.

cargo add taktus

Drop a song. See its beat.

It runs in this page, on your device. Nothing is uploaded.

Drop an audio file here

0.0

beats a minute

Beats
0
Read in
0 ms
Confidence
High
0:00

A tempo and its double are the same beats counted at another level. Which one people call the tempo is partly convention.

That file could not be read as audio.

Try an MP3, a WAV or an M4A.

Built to go unnoticed.

Measured on one thread of a laptop processor, an Intel i7-1260P.

0.4ms
to read a second of music, from the file to its tempo and every beat.
0.007%
of one core to follow a song as it plays. It listens to a quarter of it and rests.
9KB
held while it listens. Nothing of the sound is kept.
0
dependencies. One Rust file, no unsafe code.
28KB
gzipped as one WebAssembly module, for browsers and Node.

Measured against the libraries people use.

Ahead of every signal-processing library tested, on average. Two neural beat trackers are more accurate, at several hundred times the cost.

Accuracy and cost of taktus and five well-known libraries, averaged over standard benchmark sets
LibraryTempoBeatsCost
taktus85.4%0.700.4ms
Essentia82.1%0.6624.8ms
librosa69.5%0.622.3ms
aubio68.6%0.541.3ms
Neural networks
beat_this93.2%0.89196ms
madmom90.3%0.81138ms

Tempo: recordings read within 4% of the annotated tempo, half and double counted as right, averaged over five standard sets. Beats: F-measure within 70 ms, averaged over four. Cost: milliseconds of processor time per second of music. Three of the five sets were training data for the two neural trackers, and some of their figures rest on part of a set. All ten libraries, set by set

A few lines, in Rust or JavaScript.

use taktus::{beats_of, tempo_of, Ear};

// Mono samples in, onsets out: a hundred values a second.
let mut ear = Ear::new(44_100);
let onsets: Vec<f32> = samples.iter().filter_map(|s| ear.hear(*s)).collect();

if let Some(beat) = tempo_of(&onsets) {
    let beats = beats_of(&onsets, beat.period); // seconds from the start
    println!("{:.1} beats a minute, {} beats", beat.bpm(), beats.len());
}