A robust metronome using the Web Audio API

A metronome is, by essence, something that needs to have very precise scheduling.

The web is an environment where precise scheduling is often challenging, and increasingly so with the security mitigations put in place since the publication of Spectre/Meltdown.

Writing a good metronome using the Web Audio API seems at first to be a rather complicated task, and this post by Monica Dinculescu does a very good job at explaining all the things that can (and does) go wrong when dealing with timers in JavaScript. By the end of the post, the technique proposed is fairly robust, at the expense of having to do more than one complicated tricks and trying a few different techniques. Since it uses threads that are not real-time for the scheduling, it’s bound to have some jitter at some point, which may or may not be a problem, depending on the context.

But what if we could use the Web Audio API differently to do most of the work for us, so we can forget about clock domains, setTimeout throttle in the background, Workers, and all that?

This tutorial will serve as an example of a more general Web Audio API advice: don’t use the main thread if you don’t have to: you can’t predict how much it is doing. It’s hard enough to keep it under control in your own app, and it’s borderline impossible is you happen to write a library that is going to be used by a number of people to do various things that you didn’t anticipate.

I’ve written a web page that is a metronome in literate JavaScript using docco, and the output is below. The sources are on GitHub.


The metronome shown here is extremely robust, it can be tested here. It will never glitch or drift or have any jitter or anything except in case of bugs in the Web Audio API implementation. Which is certainly something that can happen (I’ve certainly written my fair share of those in Firefox).

A similar technique can be applied to other audio programs: connecting audio signal to AudioParams in the Web Audio API is a good way to have very robust parameter changes even under load. Under normal circumstances, the real-time thread on which the Web Audio API does its processing will not be preempted by other thread, and its scheduling will be really precise.

Of course, for apps that need a lot of interactivity, the main thread load will still be a factor, but when it’s possible to not use it, it’s always better to do so.