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.
| Elapsed | Cumulative drift |
|---|---|
| 10 s | 0.2–0.4 ms |
| 20 s | 0.2–0.4 ms |
| 30 s | 0.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 for | It 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 ×5 | 3.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:
- The timestamp is the clock. Start stores a real time; elapsed time is always recomputed as
now − start. Ticks never accumulate, so browser throttling cannot bend the count. - The display is just a view. A
requestAnimationFrameloop repaints the number. When the tab is occluded and rAF freezes, the display pauses, but the underlying time does not. Switch back and the correct value is instantly on screen. - Slow, safe fallbacks. Where rAF is unavailable (some embedded browsers), a modest interval keeps the display fresh; accuracy still comes from the timestamp math, not the loop.
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
- For short, focused timing (cooking at the screen, a one-minute drill), almost any browser timer is fine.
- For anything over a few minutes, or anything where the tab will lose focus, use a timer built on timestamp math, like every tool on this site.
- If a web timer has ever "lost time" on you in another tab, you now know the likely culprit: it was counting ticks the browser stopped delivering.
Limitations, stated plainly
- One platform. Every number above is Chrome 153 on one Linux desktop. Other browsers, operating systems and phones will differ; we have not measured them yet.
- Clocks measuring clocks. We used
performance.now()as the reference inside the same browser, so this measures scheduling behavior, not oscillator accuracy against an external clock. Hardware clock error (typically seconds per day) is a separate question. - Human error dominates. At 60 Hz the display itself quantizes to about 16.7 ms per frame, and reaction time adds more. Every scheduling effect we measured is far smaller than what the user adds by looking and reacting.
- Alarms are not fixed by timestamps. A throttled tab can deliver a wake-up callback late no matter how the elapsed time is computed. See the FAQ below.
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.