Online Timer

Set a countdown, switch tabs, forget about it. The remaining time is worked out from a fixed end instant on every frame instead of being counted up a second at a time, so the number is right the instant you look at it again — and the alarm is booked on the audio clock the moment you press start, before the tab has any chance to be throttled.

05:00

Timer ready, set to 5 minutes.

Ready · 5 minutes

Space start/pause · R reset · F full screen

Browsers only let a page make noise after you have interacted with it, so the alarm is armed the first time you press Start. Until then this tab is silent — that is the browser being polite, and nothing here is broken.

The problem with almost every online timer

There is an easy way to write a countdown and a correct way, and the easy way is everywhere. The easy way is to call setInterval once a second and subtract one from a counter each time it fires. It is four lines, it looks right on a developer's machine, and it is wrong in two distinct ways that only show up when a real person uses it.

The first is drift. A browser never fires an interval exactly on the millisecond; each tick arrives a little late, and because the next tick is scheduled relative to the last one, that lateness is kept. It does not average out. It adds up, tick after tick, for the whole life of the timer.

The second is worse and less known: a lost tick is lost forever. When the tab's single thread is blocked — a heavy script, a garbage collection pause, a laptop resuming from sleep, or simply the browser throttling a tab you are not looking at — the missed ticks are not replayed. The counter picks up where it left off, permanently behind by however long the interruption was. This is why a timer you set, minimised and came back to is so often showing a number that is plainly a lie.

Neither problem is hard to avoid. Store the instant the timer is due to finish, then, on every frame, subtract the current clock reading from it. Nothing accumulates, so nothing drifts; a sample that never arrives costs nothing, because the next one re-derives the answer from scratch. That is what this page does, and the rest of this page is the measurement that shows the difference is real.

Two counters, side by side

Claims about timer accuracy are usually made with adjectives. We would rather show the runs. The script that produced every number below lives in the repository as scripts/drift.mjs, and it writes its results straight into the module the site renders from, so no figure on this page has been transcribed by hand.

The method: two counters run side by side against the same monotonic clock. The naive counter is setInterval(cb, 1000) with elapsed time taken as ticks × 1000 — the four-line version described above. The timestamp counter is sampled every 16 ms, roughly one animation frame, and computes elapsed time as now − start — the method used here. For each whole second we record the moment each counter first displayed that second. The ideal moment is exactly k × 1000 ms, so the gap is that counter's lateness, in milliseconds. Lateness that grows with the seconds is drift; lateness that stays flat is just sampling latency.

Four conditions were measured: sixty seconds idle, sixty seconds with the thread deliberately kept busy (40 ms of blocking work every 200 ms, which is an ordinary amount for a page with an animation on it), five minutes idle to check the drift rate is linear, and fifteen seconds containing one deliberate three-second freeze.

Each condition was run 5 times, not once, and the conditions were interleaved — one idle, one busy, one long, one stalled, then round two — so a noisy stretch of machine time is spread across all four conditions instead of landing on one. Every figure below is the median of those runs with the observed range beside it. That is not statistical decoration. An earlier version of this page published one run per condition to four significant figures, and repetition showed that the single idle run it quoted was the fastest of its kind while the single busy run was among the slowest, which made the gap between them look bigger than the machine can actually support.

Conditions 1 and 2: what a minute costs

Idle, the naive counter delivered 59 ticks in 60.07 seconds of real time. That tick count matters for reading the next number: the lateness below is the lateness at tick 59, not at tick 60, because a counter that is running behind has not yet delivered the ticks it still owes. At that tick the display was 347.7 ms late — a drift rate of 5.89 ms per tick, with the 5 runs spanning 5.67 to 6.52 ms.

With the thread kept busy the same counter was 670 ms late at tick 59, a rate of 11.36 ms per tick spanning 9.42 to 12.97 ms — about 1.9× worse on the medians. Pair the extremes of the two sets against each other, though, and that multiplier runs from 1.4× to 2.3×. The width of that spread is why the median is what gets quoted, and why no single pairing is. The timestamp counter displayed all 60 seconds in every run, with a worst lateness of 30.2 ms idle and 48.5 ms busy — the length of a frame or two, and not growing.

Condition 3: is the drift linear?

This matters, because the long-duration figures in the table below are projections and a projection is only honest if the thing being projected is linear. Over five minutes the naive counter drifted at 6.07 ms per tick (range 5.64 to 6.13) against 5.89 ms per tick over one minute (range 5.67 to 6.52) — a ratio of medians of 1.03, with the extremes of the two sets spanning 0.86 to 1.08. The two ranges overlap, which is the most that a few thousand ticks on one machine can honestly establish: nothing here suggests the rate changes with the length of the run.

The like-for-like check: the five-minute runs reached tick 298, so multiplying the one-minute rate by 298 ticks predicts 1756 ms of lateness at that point. The runs themselves produced 1808.1 ms, so the prediction was 52 ms under. The tick count is not a pedantic detail: predicting at 300 ticks and comparing it against a 298-tick measurement would inflate that error by 2 ticks' worth of drift, and an earlier version of this page did exactly that.

Condition 4: one frozen thread

This is the run that makes the case. The thread was blocked for three seconds, five seconds into a fifteen-second run — the software equivalent of glancing away.

Measured directly on Node v22.20.0, win32 10.0.26200, AMD Ryzen 9 9950X3D 16-Core Processor, on 2026-08-14; medians of 5 runs. Both displays freeze while the thread is blocked — nothing can paint — so the interesting column is what happened afterwards.
After one 3-second freeze Naive tick counter Timestamp method
Whole seconds displayed after 15.1 s of real time 12 15
Whole seconds lost and never recovered 3 0
Worst lateness reached during the freeze 3052.7 ms 3009 ms
Lateness still on the clock when the freeze ended 3052.7 ms none — the next sample re-read the clock

Three seconds went in and three seconds came out — permanently. The naive counter showed 12 seconds where 15 had actually passed, and no amount of waiting gives them back, because there is no record anywhere that they were missed. The timestamp counter froze for exactly as long, then re-read the clock on its next sample and was immediately correct.

How far out a naive timer finishes, by length

Two of these rows were timed end to end; the rest apply the median per-tick rates to the tick count, which condition 3 showed is a fair thing to do. The point of the table is the shape rather than any single figure: the error is proportional to the length of the timer, so it is invisible on a one-minute egg and obvious on an exam paper. No run longer than five minutes was ever made, so every row below that is arithmetic on a rate, not a stopwatch reading.

How late a setInterval(1000) tick counter finishes, by timer length. Idle and busy columns are projections from the median per-tick rates (5.89 ms and 11.36 ms respectively). The last column is not a projection: it is what a run of that length actually produced, and it is annotated with the tick it was recorded at, which is one or two short of the tick count in the second column precisely because the counter was running late. Measured on Node v22.20.0, win32 10.0.26200; 5 runs per condition, medians shown.
Timer length Ticks Late, idle page Late, busy page Directly measured
1 minute 60 354 ms 681 ms 347.7 ms at tick 59
5 minutes 300 1.8 s 3.4 s 1808.1 ms at tick 298
10 minutes 600 3.5 s 6.8 s —
15 minutes 900 5.3 s 10 s —
20 minutes 1,200 7.1 s 14 s —
25 minutes 1,500 8.8 s 17 s —
30 minutes 1,800 11 s 20 s —
1 hour 3,600 21 s 41 s —

The bottom row is the one worth sitting with. An hour-long countdown written the easy way finishes about 21 s late on a page doing nothing, and about 41 s late on a page doing something. That is the difference between an hour timer you can put in front of an exam hall and one you cannot.

What we did not measure, and will not claim

These runs were made in Node on one Windows desktop, not in a browser on a fleet of machines, and it would be dishonest to present them as anything else. Node and browsers share the underlying behaviour that produces the effect — a repeating timer is re-armed after it fires, and missed firings are coalesced rather than replayed — but the exact per-tick overshoot depends on the engine, the operating system and how busy the machine is. Your numbers will differ. The shape is what transfers: accumulate and you drift, subtract from a fixed instant and you do not.

Browsers additionally throttle timers in tabs you are not looking at, and the policies are both documented by the browser vendors and subject to change between versions. We have not measured those policies ourselves, so this page does not put a number on them. It does not need to: a method that recomputes from the clock is correct regardless of how often it gets to run, which is precisely why it was chosen.

What the page does with all this

  • One fixed end instant. Pressing start records now + remaining. Every frame after that is a subtraction. Pausing captures the exact milliseconds left; resuming books a new end instant from them.
  • The alarm is booked, not played. Four tones are scheduled on the Web Audio clock when the timer starts, so they do not depend on a throttled script noticing zero. They are built from oscillators, so there is no audio file to fetch or fail. What this does not buy you is immunity from the browser suspending a silent audio context in a hidden tab — see the alarm question below, where that is set out in full.
  • The tab title counts down. Deliberately short — the clock and the site name — so it stays readable when the browser has squeezed your tab to a sliver.
  • Full screen with the controls out of the way. Press F, or the button. Space starts and pauses, R resets.
  • Every duration is a link. The seven length pages are permanent URLs, and ?t= takes anything from ?t=45 to ?t=1h30m.

Which length do you want?

Each of these opens preset, carrying notes written for its own length — what people use it for, and how it fails.

  • 1 minute timer — planks, held stretches, rests between sets, and why a freeze hurts short timers most.
  • 5 minute timer — the default "just wait a moment", and the length we measured end to end.
  • 10 minute timer — talk slots, tidies and the arithmetic of six slots to the hour.
  • 15 minute timer — the quarter-hour grid that calendars and timetables are drawn on.
  • 20 minute timer — naps and eye breaks, where the alarm matters more than the display.
  • 30 minute timer — training blocks, oven slots and study sessions.
  • 1 hour timer — exam sections and deep work, where drift finally becomes visible.
  • Pomodoro timer — 25 on, 5 off, alternating on its own, with the cycle schedule worked out to the minute.

Put this timer on your own page

A plain iframe — no script tag, no tracker, nothing to keep up to date. Change the ?t= value for a different length, or point it at any of the duration pages.

<iframe src="https://timerly.pages.dev/?t=5m" width="100%" height="440" style="border:0;border-radius:16px" title="Timerly online countdown timer" loading="lazy" allowfullscreen></iframe>

Online timer: questions people actually ask

Does the timer keep running when I switch to another tab?
Yes, and this is the thing the site is built around. The countdown never adds up ticks; it stores the exact instant your timer is due to finish and subtracts the current clock reading from it on every animation frame. A hidden tab stops repainting and has its scripts throttled, so the picture freezes — but the first sample after you return re-reads the clock and shows the correct number immediately. There is no catch-up animation because there is nothing to catch up.
Will the alarm actually sound if the tab is in the background?
It is scheduled so that it can, and that is as strong a claim as we are willing to make. The tones are never played by JavaScript when it notices the countdown has hit zero — which is exactly the thing a throttled tab is bad at. They are booked on the browser's audio clock at the moment you press start, so they are not subject to script throttling. What we cannot promise is the audio clock itself: a browser may suspend a silent audio context in a hidden tab, and a timer waiting for its alarm is silent for the whole wait. WebKit suspends a non-audible context on visibility change, and Chrome's throttling exemption for pages "making noise" explicitly does not count a silent audio track. While a context is suspended its clock does not advance, so the tones arrive late instead of never — usually when you return to the tab. Three further limits: sound must be unlocked by a user gesture first (pressing Start does it), a tab you have muted produces nothing, and a computer that has gone to sleep produces nothing. If the alarm genuinely matters, keep the tab visible or set a device alarm as well.
Why do I have to click before the alarm works?
Because browsers block pages from making noise until you have interacted with them, which stops every site you open from being able to shout at you. There is no way around it and no reason to want one. The tool tells you in plain words whether sound is armed yet, and pressing Start arms it.
Is there a sound file being downloaded?
No. The alarm is four short tones generated by the Web Audio API from oscillators — a sine wave at 880 Hz with a quieter octave above it, shaped by an exponential decay envelope. Nothing is fetched from the network, so the alarm cannot fail because a file did not load, and it works offline once the page is open.
Can I link to a specific duration?
Yes, two ways. The duration pages are permanent links in their own right — /10-minute-timer/ opens preset to ten minutes. For anything else add ?t=: ?t=90 is ninety seconds (a bare number is always seconds), ?t=1h30m and ?t=1:30:00 are both ninety minutes, and ?t=7:30 is seven and a half minutes. A linked timer opens preset but does not start on its own.
Does anything I time get sent anywhere?
No. This is a set of static files. The countdown runs in your browser, nothing about your durations is transmitted, stored or logged, and there is no account. You can confirm it in your browser's network tab: start a timer and watch nothing happen.
How accurate is it really?
The method's error does not grow with the length of the timer, because nothing accumulates. In the two idle conditions published on this page the timestamp method's worst lateness at any point was 31 ms over five minutes and 30.2 ms over one minute — the length of an animation frame or two, not a function of the duration. The frozen-thread run is the exception and it is on this page too: while the thread was blocked the display was stuck for the full 3009 ms, because nothing can repaint a frozen tab. The difference is what happens next. The very next sample re-read the clock and was immediately correct, losing 0 seconds against the 3 the tick counter never got back. So a stall costs you a stale picture for as long as it lasts, and nothing after that. What can still defeat the whole thing is outside the arithmetic: a closed tab, a sleeping laptop or a muted tab.

More of our small tools

Same idea: one job, no sign-up, works in the browser.

  • Military Time Converter

    Turn 18:45 into 6:45 PM and back, with the 0000/1200 edge cases handled properly. Handy when a shift roster and a kitchen timer disagree about what "1900" means.

  • Days Until Counter

    The same idea as this page on a calendar scale: an exact count of the days from now to a date. For deadlines a 60-minute timer cannot hold.