To answer the question in footnote 1, off the top of my head: setTimeout and setInterval have no timing guarantees because in the browser window/tab context there's no guarantee that they're active/visible. It doesn't make sense for a UI event to fire (their intended use) if the UI is hidden/inactive, and timing guarantees are therefore deferred to specific implementations.
requestAnimationFrame does make those timing guarantees.
RAF is still imprecise for audio use. For audio in the browser, you usually want to defer all timing events that must be precise to a scheduler that uses either the webAudio context time or even more ideally, use a lookahead scheduler to schedule events by audio frame using an internal counter and the sample rate.
A great programming exercise that forces you to wrap your head around JavaScript timing events is writing a small step sequencer that can be used while it is running with minimal latency.
Interesting point, thank you. I was reading the docs today and encountered the following (presented fyi):
It turns out that this is currently true [0] because the base audio context (BAC) time is accurate to within 20us whereas RAF is accurate to within 1ms [1]. However in future browser versions the default sensitivity for BAC is going to drop to 2ms. This is done to help improve user privacy by hampering browser fingerprinting.
This is all configurable and its materiality depends on use case, but interesting read: thank you.
> As specified in the HTML standard, browsers will enforce a minimum timeout of 4 milliseconds once a nested call to setTimeout has been scheduled 5 times.
- Timeouts in inactive tabs
> To reduce the load (and associated battery usage) from background tabs, browsers will enforce a minimum timeout delay in inactive tabs. It may also be waived if a page is playing sound using a Web Audio API AudioContext.
> The specifics of this are browser-dependent.
- Throttling of tracking scripts
> Firefox enforces additional throttling for scripts that it recognises as tracking scripts. When running in the foreground, the throttling minimum delay is still 4ms. In background tabs, however, the throttling minimum delay is 10,000 ms, or 10 seconds..
- Late timeouts
> The timeout can also fire later than expected if the page (or the OS/browser) is busy with other tasks.
- Deferral of timeouts during pageload
> Firefox will defer firing setTimeout() timers while the current tab is loading.
I thought they weren't accurate because the callback is only added to the stack once the timer expires, this means you may need to wait for the stack to clear before execution happens.
requestAnimationFrame does make those timing guarantees.