A timer that stays accurate
The obvious implementation drifts. Here's the one that doesn't.
Build time ~2 hrs
A countdown looks like a five-line project. Then it drifts, breaks in a background tab, and loses its place on refresh. Getting it right teaches you something you'll reuse constantly.
1. Never count down by decrementing
The naive version subtracts one from a counter each second. Timers don't fire exactly on time, background tabs throttle them heavily, and errors accumulate. After ten minutes you're seconds out.
Store the end time, and on every tick compute the remaining time from the current clock. Drift becomes impossible because you're never accumulating anything.
Build a countdown timer, one HTML file. - Store the target end time, not a remaining count. Recompute the remainder from the clock each tick. - Update the display roughly every 100ms so the seconds change crisply, but never assume the interval fired on time. - Survive a background tab: correct itself immediately when the tab becomes visible again. - Survive a refresh: persist the end time and resume. - Pause and resume, storing the remaining duration rather than a paused end time. Also: a sound and a visual alert on completion, and handle the browser blocking audio until a user gesture.
2. Handle the tab going away
Browsers throttle timers in background tabs — sometimes to once a minute. Because you're computing from the clock, the value is still correct; you just need to redraw when the tab returns. Listen for the visibility change and recalculate immediately.
3. Announce it accessibly
A visual countdown alone excludes screen reader users. Use a live region that announces at meaningful intervals — not every second, which is unusable — and make sure completion is announced clearly.
4. The details that make it good
A title that shows the remaining time, so it's visible in the tab bar. A URL that encodes the duration so timers are shareable. Keyboard control. And a wake lock, if you want the screen to stay on — useful for a kitchen or workout timer.
Test it by locking your phone for five minutes. That's the case the naive implementation fails, and it's the case people actually use.