We all know that setInterval is not super accurate, but I wanted to see how this would actually affect in practice audio scheduling (like a metronome). I made three kinds of metronomes, in order of goodness. I'm using emoji everywhere so that if you don't want to read the text, you can still know how you're supposed to feel.
I implemented 3 kinds of metronomes:
😱 | using setInterval() | |
😕 | using setInterval() in a worker | |
🥳 | using 20 prescheduled audio events |
For the below experiments, run all metronomes in a row for each and...
🤔 | do nothing in the callback. what to look for: all metronomes are pretty much the same. this is the unrealistic best case scenario: if you have a metronome in JS and are doing no other work: bless you. | |
🤢 | fake a blocking computation for 0.5s in the callback on the main thread. what to look for: audio and display message are both at a horribly incorrect tempo for the first 2 metronomes (because they're being blocked by the fake work). For the 3rd metronome the audio is correct (because it's not scheduled by the main browser thread), but the UI is janky for the same reason as the first 2 metronomes. | |
😰 | fake a blocking computation for 0.5s but chunked (split into 5ms chunks, each in its own rAF). what to look for: this is in between the two experiments: we're still doing a bit of blocking work (but significantly less than in the second example). The important thing to note here is that even though the blocking work seems small (what's 5ms here and there), it actually throws off the first metronome audio quite a bit. | |
🤩 | fake a blocking computation for 0.5s in a worker. what to look for: this is the realistic best case scenario. we're doing all the blocking work in a worker, which is off the main thread, so even the terrible setInterval() metronome is doing fine. |
Graph of the AudioContext currentTime property for each metronome (which is when you actually hear the tick). In the setInterval() metronomes, the time of the audio and the time of the callback (in which you'd do UI work) is the same. In the WebAudio scheduler metronome, the time of the audio is always correct, but the callback is in a rAF so it will be imprecise, which is why we log it separately: