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
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.
| Library | Tempo | Beats | Cost |
|---|---|---|---|
| taktus | 85.4% | 0.70 | 0.4ms |
| Essentia | 82.1% | 0.66 | 24.8ms |
| librosa | 69.5% | 0.62 | 2.3ms |
| aubio | 68.6% | 0.54 | 1.3ms |
| Neural networks | |||
| beat_this | 93.2% | 0.89 | 196ms |
| madmom | 90.3% | 0.81 | 138ms |
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());
}
import { Ear, beatsOf, tempoOf } from "taktus";
// Mono samples in, onsets out: a hundred values a second.
const ear = new Ear(audio.sampleRate);
const onsets = ear.hear(audio.getChannelData(0));
const beat = tempoOf(onsets);
if (beat) {
const beats = beatsOf(onsets, beat.period); // seconds from the start
console.log(`${beat.bpm.toFixed(1)} beats a minute, ${beats.length} beats`);
}