Free tools Windows power users keep installed
One-click scans. No signup required.
setInterval(..., 1000) does not guarantee a callback every second. A countdown that subtracts one second each time the callback runs measures callback count, not elapsed time. If JavaScript is busy or the browser throttles timers in a background tab, callbacks arrive late and the displayed countdown falls behind. Keep a clock-based start time or deadline as the source of truth, then calculate the remaining time afresh on each update.
Why does setInterval drift?
A timer delay is a scheduling request, not a promise of exact callback spacing. MDN Web Docs puts it plainly: “Note also that the actual amount of time that elapses between calls to the callback may be longer than the given delay.” (MDN: Window.setInterval())
As an Amazon Associate I earn from qualifying purchases.
Timer callbacks run through the event loop. They cannot interrupt JavaScript already running on the main thread, so a busy page can delay a callback. Browsers can also throttle timers in inactive tabs, according to browser-specific rules. (MDN: Window.setTimeout())
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →If each delayed callback still subtracts exactly one second, every delay becomes permanent error in the counter. Requesting a shorter interval does not solve the underlying problem: the browser can still delay scheduling or execution. There is no general measured drift figure in the cited API documentation, and actual delays depend on runtime conditions.
#1 Best Overall
Make the clock or deadline the source of truth
Instead of decrementing a variable, record a start time or target deadline. On each render, find the difference between that time and the current time. A late callback may make the display update late, but the next calculation reflects elapsed time rather than pretending only one interval passed.
Countdown for a duration in the current page
Use performance.now() to measure elapsed duration within the page’s time origin. It is monotonic, so system clock adjustments do not change the elapsed-time calculation. It is relative to performance.timeOrigin, not Unix epoch time. (MDN: Performance.now(); MDN: High precision timing)
Rank #2
const durationMs = 60_000;
const startedAt = performance.now();
function render() {
const elapsed = performance.now() - startedAt;
const remainingMs = Math.max(0, durationMs - elapsed);
showRemaining(remainingMs);
if (remainingMs > 0) {
setTimeout(render, 100); // Display cadence only; elapsed time is recomputed.
}
}
render();
The 100 ms timeout above controls how often the display is refreshed; it does not define the countdown’s accuracy. The code examples here illustrate the timing pattern and are not reported test results. MDN notes that whether performance.now() advances during system sleep can vary by operating system, so decide whether sleep should count for your application and reconcile on resume if needed.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errorsCountdown to a persistent deadline
If a target must survive a reload or be compared with an epoch-based timestamp, store an epoch deadline using Date.now() and recompute the remainder from it:
Rank #3
const durationMs = 60_000;
const deadline = Date.now() + durationMs;
function render() {
const remainingMs = Math.max(0, deadline - Date.now());
showRemaining(remainingMs);
if (remainingMs > 0) {
setTimeout(render, 100);
}
}
render();
Date.now() uses wall-clock time, so a user or system clock change can affect the result. Choose it when an epoch-based deadline is needed, and account for clock changes when they matter. Do not subtract a performance.now() value from a Date.now() value directly: they belong to different clock domains.
What to do when the page returns to the foreground
When a hidden page becomes visible again, immediately recalculate from the stored start time or deadline and repaint. Do not replay one callback for every missed second: those callbacks did not measure the time that passed. The Page Visibility API can notify the application when visibility changes; the exact background throttling policy varies by browser. (MDN: Page Visibility API)
Rank #4
Choose a scheduler for the display or work
| Need | Suitable scheduler | What it does not guarantee |
|---|---|---|
| Simple countdown display | setInterval or recursive setTimeout |
Exact callback timing; derive remaining time from a clock or deadline. |
| Work that must not overlap itself | Recursive setTimeout |
Fixed-rate execution; the next call is scheduled after the previous work completes. |
| Smooth visual animation | requestAnimationFrame |
Background execution; most browsers pause it for hidden pages. |
| Work in a worker context | Worker timers | A universal exact-time guarantee; timer constraints still apply. |
Recursive setTimeout is useful when a cycle’s work may take longer than the requested delay: scheduling the next cycle after the current work finishes avoids overlapping cycles. It still does not create a precise clock. (MDN: Window.setInterval())
requestAnimationFrame is a one-shot callback generally aligned with the browser’s painting schedule, making it suitable for visual animation. Most browsers pause it in background tabs. It schedules presentation; it does not make a deadline-based countdown accurate by itself. (MDN: Window.requestAnimationFrame())
Best Value
A worker can move work away from window main-thread tasks, but it does not guarantee exact timing or remove browser lifecycle constraints. A visible countdown still needs its interface updated. (MDN: WorkerGlobalScope.setInterval())
Quick Recap
Common countdown-timer mistakes
- Subtracting a fixed amount per callback: A callback can arrive late, so callback count is not elapsed time.
- Using a smaller interval to chase accuracy: Shorter requested delays do not prevent main-thread contention or background throttling.
- Treating recursive
setTimeoutas a precision clock: It prevents the next cycle from being scheduled before the previous one finishes, but its delay is still a scheduling request. - Relying on
requestAnimationFramefor background ticking: Most browsers pause it in hidden pages. - Mixing clock domains:
performance.now()is time-origin-relative, whileDate.now()is epoch-based and can reflect system clock changes.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




