StopwatchHQ

How Reliable Is a Browser Timer? We Measured It

We benchmarked how browsers schedule timers in practice: drift, jitter, the 4 ms clamp, background-tab throttling, and what that means for a web stopwatch. Reproducible numbers, single platform, limitations stated.

Browsers document how timers are scheduled; we wanted to see how they actually behave. We ran a seven-part benchmark on Chrome 153 (Linux, 32-thread desktop CPU, high-refresh display) using performance.now() as the reference clock, and recorded when every callback really fired. This is independent verification with reproducible numbers, not a discovery: the behaviors below match what Chrome and MDN document. The results explain why tick-counting timers drift while timestamp-based ones hold.

The one-line answer

In a focused, visible tab, scheduling is tight: our 30-second setInterval test finished between 0.2 and 0.5 ms late. The dangers are elsewhere: fast timers cannot fire faster than 4 ms apart, hidden tabs throttle (eventually to about one callback per minute), and the animation clock stops entirely in occluded windows. A stopwatch that counts ticks will drift in exactly those cases; one that recomputes from a timestamp will not.

How we measured

Each test ran in a real page (not an empty lab bench) and compared scheduled time against performance.now(), the monotonic millisecond clock browsers expose to JavaScript. We recorded when every callback actually fired, then subtracted the ideal schedule. The full run takes about 90 seconds when the tab is focused.

Finding 1: setInterval drifts, but less than you'd think

A naive one-second interval, ticked 30 times, accumulated between 0.2 and 0.5 ms of lag over the half-minute. That sounds perfect, and for a 30-second sprint it is. The problem is the trend: drift of this kind is roughly proportional to elapsed time and to system load, so a timer built on "count the ticks" can lose whole seconds across an hour, exactly when a cooking or exam timer is being trusted most.

ElapsedCumulative drift
10 s0.2–0.4 ms
20 s0.2–0.4 ms
30 s0.2–0.5 ms

At a 50 ms interval, individual ticks deviated 0–0.5 ms from schedule. Small, but it never cancels out; it only accumulates.

Finding 2: fast timers hit the 4 ms wall

Browsers clamp short timers: nested setTimeout and rapid setInterval calls cannot legally fire faster than every 4 ms once nesting passes five levels. We measured how hard that rule bites:

You asked forIt actually fired every
setInterval(1 ms)4.0 ms
setInterval(4 ms)4.0 ms
setInterval(16 ms)16.0 ms
setInterval(100 ms)100.0 ms
setTimeout(0), nested ×53.6–4.2 ms per hop

Anything below 4 ms is a fiction. If your timer's display updates by counting intervals, a "1 ms" loop is really a 4 ms loop, and the count drifts from the wall clock four times as fast as intended.

Finding 3: background tabs are a different planet

This is the finding that matters most for anyone who runs a timer in one tab and works in another. Chrome's rules are tiered: a hidden tab's timers drop to roughly once per second immediately, and after a tab has been hidden for about five minutes, its chained timers fall under “intensive throttling”, about one firing per minute, unless the tab is playing audio or holding a connection. In our runs the throttled behavior dominated once the tab sat in the background. A tick-counting stopwatch left back there loses minutes of display time; the timestamp math keeps counting correctly either way.

The animation clock behaves even more dramatically: requestAnimationFrame stopped being called entirely in the occluded window. It did not slow down. It froze. (Occlusion detection is platform-dependent; this was Chrome on Linux hiding the tab behind another window.)

Finding 4: the clocks themselves are excellent

The raw clock sources are the good news. performance.now() advanced in clean 0.1 ms steps on our machine (a common granularity), and it is monotonic: it never jumps when the system syncs time. Date.now() is coarser; 5,000 rapid reads often returned the same millisecond, which is why display loops that poll it see "flat" frames. Both are far more precise than any visual display needs.

How StopwatchHQ is built on these facts

Everything above points to one architecture, and it is the one our online stopwatch uses:

We verified the payoff directly. In a focused tab, both approaches agreed after ten seconds (10,000 ms vs 9,999 ms): foreground, a tick counter is fine, and the 1 ms gap between the two was noise. The designs diverge only when the tab loses focus, which is where throttling eats ticks and the timestamp approach keeps the truth.

What this means for you

Limitations, stated plainly

Frequently asked questions

Is setInterval drift big enough to matter?

On a quiet, focused tab, no: half a millisecond over 30 seconds. It matters when load rises or when you add up hours of accumulation, and it matters instantly under background throttling. Timestamp-based timing sidesteps the whole class of bugs.

Why did my timer pause when I switched tabs?

Its display was probably driven by requestAnimationFrame, which browsers suspend in occluded tabs, or its logic counted interval ticks, which get throttled to as little as one per minute. The elapsed-time math keeps running either way if the timer is built correctly.

Which is more accurate, setInterval or requestAnimationFrame?

Neither, on its own: they are delivery mechanisms, not clocks. Accuracy comes from recomputing elapsed time from a timestamp at display time. We use rAF for smooth visuals and timestamps for truth.

Does timestamp-based timing fix alarms too?

No, and it is worth being honest about that. Timestamps keep the display honest, but a hidden tab may deliver the wake-up callback up to a minute late once intensive throttling applies. For alarms that must fire on time, the tab needs to stay visible, play audio, or hand the job to the operating system. That is a separate problem from display drift, and we are explicit that it is not solved by the architecture alone.

Does this apply on phones?

The same principles, with sharper edges: mobile browsers throttle and suspend background tabs even more aggressively, and screen lock adds its own pauses. Timestamp-based timing is the only approach we would trust there.

Try it yourself

Open the stopwatch, start it, switch tabs for ten minutes, and come back: the reading will match your phone. That is the difference between counting beats and reading the clock. We are also building a small page that runs the same seven tests in your own browser, so you can verify this on your hardware rather than trusting ours; it will be linked here when ready.

Related tools

For timing itself, the online stopwatch, countdown timer and pomodoro timer all use the timestamp architecture described above. For how we put that accuracy to work, see our guides on study timers, cooking timers and workout timers.