You can use requestAnimationFrame for animations as an alternative to setTimeout or setInterval. Instead of manually calling a function to draw a frame every x milliseconds (setTimeout or setInterval), requestAnimationFrame gets called before its next repaint which could potentially vary in time between calls. Since the function doesn't call itself on a loop like setInterval, you will need to manually call it again to continue drawing the next frame.

const animate = (timestamp) => {
// draw something
requestAnimationFrame(animate)
}
requestAnimationFrame(animate)

The callback function which is given a timestamp, which should be used to calculate the next frame.

According to MDN,

The number of callbacks is usually 60 times per second, but will generally match the display refresh rate in most web browsers as per W3C recommendation.

and

Be sure to always use the first argument (or some other method for getting the current time) to calculate how much the animation will progress in a frame, otherwise the animation will run faster on high refresh rate screens.

Example

Let's say I wanted to animate a spinning box. A naïve way of implementing it with requestAnimationFrame would be something like this:

The problem with this approach is that the animation isn't guaranteed to run at the same speed on everyone's screens. Someone with a 60Hz refresh rate would see one full rotation after 360 / 60 seconds. Another way of thinking about it is: 1 degree rotation every function call, where the function gets called 60 timies per second. Someone with a 120Hz refresh rate would see one full rotation after 360 / 120 seconds, which would be twice as fast as someone with the 60Hz refresh rate.

A better approach to achieve consistant animations would be to figure out at what rate the animation should run and use the timestamp argument to figure out exactly what to draw on the screen.

d = r * t

In order to achieve one rotation in 360 / 60 seconds:

d = (360 / 60) * t

Now, on every function call, we'll use timestamp to calculate how much time has passed since the last invocation to figure out the correct degrees to rotate.

With this approach, if the function gets called twice as fast on one machine vs another, it will still finish one rotation in 360 / 60 seconds.

Note that this example uses milliseconds for the rate because timestamp is a number in milliseconds. If you are viewing this on a 60Hz screen, both animations may look the same but the underlying logic for setting the values are different.

References