Online Metronome (Tap Tempo)
A free online metronome with sample-accurate timing, adjustable beats per measure, an accented visual pulse, and a tap-tempo button to find any BPM by ear.
223 views
Tap in rhythm — needs at least 2 taps
Uses the Web Audio API's look-ahead scheduler (AudioContext.currentTime) for sample-accurate timing — not a simple setInterval, so there is no drift even under heavy page load.
Why Most Browser Metronomes Drift
The obvious way to build a metronome in JavaScript is setInterval(playClick, 60000 / bpm). It works for a few seconds — then it doesn't. setInterval only guarantees a callback fires no earlier than the requested delay, never how much later it might actually run. The JS main thread is shared with everything the page does — DOM updates, layout, garbage collection — and any of it can delay a pending timer by tens of milliseconds. For most UI code that's invisible; for a metronome it's fatal, since a click 40ms late this beat and 10ms late the next audibly wanders instead of sounding merely off, actively misleading a musician practicing against it.
Musicians rely on a metronome precisely because human perception of steady time is unreliable without an external reference — the whole point of the device since Johann Maelzel patented the wind-up version in 1815. A software metronome that itself drifts defeats that purpose, and the fix isn't a cleverer timer: every JS timer, including requestAnimationFrame, shares the same main-thread bottleneck. The real fix is to stop asking JavaScript to produce the sound at the right moment at all.
Look-Ahead Scheduling: How This Metronome Stays Accurate
This tool uses the pattern popularized by Chris Wilson, formerly of Google Chrome's Web Audio team, in his widely cited article "A Tale of Two Clocks": separate scheduling logic from sound timing entirely. The Web Audio API's AudioContext runs on its own audio thread with a sample-accurate clock, exposed to JavaScript as AudioContext.currentTime. Calling osc.start(t) with a future timestamp from that clock makes the audio engine play it at exactly t, regardless of what the main thread is doing that instant — it never had to "wake up" precisely on the beat to make it happen.
The main thread's only job is bookkeeping: roughly every 25ms, a scheduler checks for beats within the next 100ms and calls osc.start() for each with its exact future currentTime. Since that loop only needs to be approximately on time, ordinary jitter causes no audible problem — the sound is still scheduled with sample-accurate precision. Each click is a ~30ms oscillator burst, higher-pitched on beat one of every measure for an audible accent, synced with a matching visual pulse.
Tap Tempo solves a different problem: finding the BPM of music with no chart to read it from. Tap in time with the beat; the tool measures the interval between taps and converts it with 60000 / averageIntervalMs. At least two taps are required, and averaging recent taps smooths out small timing errors. A gap over two seconds resets the sequence, since that usually means the beat was lost rather than slowed dramatically.
Frequently Asked Questions
Why does a simple setInterval metronome sound uneven over time?
setInterval only promises a callback fires no sooner than the requested delay — it can fire later whenever the main thread is busy with layout, garbage collection, or other tab activity. Those small, irregular delays accumulate beat after beat, so the click drifts audibly instead of staying locked to tempo. This tool avoids that by handing the actual sound timing to the Web Audio API's own audio-thread clock instead of relying on the JS timer to fire at the right instant.
What is "look-ahead scheduling" and why does it fix the drift?
It's a pattern where JavaScript never tries to play a sound the instant it's due. A loop checks every ~25ms whether any beats fall in the next 100ms and pre-schedules an oscillator for each using AudioContext.currentTime plus the exact offset, via osc.start(preciseFutureTime). The audio engine fires that oscillator at sample-accurate precision on its own thread, independent of the main thread — so the scheduling loop can be sloppy while the actual beat stays exact.
How does Tap Tempo calculate the BPM?
Each click on the Tap Tempo button records a timestamp. From the second tap onward, the tool measures the interval since the previous tap, keeps a short rolling history of recent intervals, and converts their average to a tempo with 60000 divided by the average interval in milliseconds. At least two taps are needed, and a gap over two seconds resets the sequence, since that almost always means tapping stopped rather than continued at an extremely slow tempo.
Why is the first beat of each measure a different pitch?
It's an accent, standard on mechanical and digital metronomes alike: the downbeat (beat one) plays at a higher frequency than the rest, paired with a visual pulse in a different color. That makes it possible to tell where "one" falls in a 3/4 or 6/8 pattern at a glance or by ear, which matters for counting measures correctly, especially in time signatures where every beat would otherwise sound identical.
What BPM range does this metronome cover, and what does BPM mean?
BPM stands for beats per minute — how many pulses occur in sixty seconds. This tool supports 30 to 300 BPM, covering the full range used in written music: a Grave marking sits around 25-45 BPM, Andante walking tempo around 76-108, and Presto passages can exceed 200 BPM. 120 BPM is the default because it's a common, comfortable practice tempo and the classic Moderato marking.
Similar Tools
Report a Problem
Online Metronome (Tap Tempo)
Comments
No comments yet — be the first to write one!