I built a little browser timer as a side project, and the first bug report I gave myself was "the countdown is wrong when I come back to the tab." Classic.
Here's the thing: browsers ration timers on hidden pages to save battery. Chrome documents three stages (source):
- Minimal throttling: page is visible, or made sound in the last 30s. Timers run roughly on time.
- Throttling: page is hidden. Timers get checked about once per second.
- Intensive throttling (Chrome 88+): hidden for 5+ minutes, silent for 30+ seconds, and the timer chain count is 5 or more. Now timers get checked once per minute.
Firefox has its own rules too. MDN notes a 1s minimum in inactive desktop tabs and up to 15 minutes on Firefox for Android (MDN).
So if your countdown does this:
let remaining = 25 * 60;
setInterval(() => {
remaining -= 1; // assumes exactly 1s passed. it didn't.
render(remaining);
}, 1000);
...it drifts the moment the tab goes into the background.
Store the deadline, not the count
const endsAt = Date.now() + 25 * 60 * 1000;
function tick() {
const remainingMs = Math.max(0, endsAt - Date.now());
render(Math.ceil(remainingMs / 1000));
if (remainingMs === 0) finish();
}
setInterval(tick, 250);
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') tick(); // snap to the truth on return
});
Now the number is always right when the user looks at it. Persist endsAt to localStorage and it even survives a reload. (Trade-off: Date.now() follows the device clock, so if someone changes their clock, the countdown moves with it.)
What this does NOT fix
A correct number on return isn't the same as a sound that plays on time while the tab is hidden or the phone is asleep. Things that help, honestly listed:
- Keep the page visible.
- The Screen Wake Lock API keeps the screen on, but only while the page is visible, and the system can release it (low battery, power saver). HTTPS only.
- Tell users plainly: don't use a web timer as a safety-critical alarm.
That last one felt weird to put in my own UI, but I'd rather be upfront than have someone miss something important.
If you want to poke at the result, it's moment.: Pomodoro, intervals, a lap stopwatch and D-Day counters, no sign-up, and everything stays in your browser. Happy to hear how you handle this in your own apps.
Top comments (0)