Skip to main content
Back to blog

Welcome to MakeMyTimer (and why most online timers are wrong about time)

Why setInterval-based timers drift in backgrounded tabs, why we use requestAnimationFrame plus Date.now() deltas instead, and how MakeMyTimer ships zero timezone data of its own.

If you search the web for "online stopwatch," the first result will probably take fifteen seconds to load, ask twice for cookie consent, render a 60fps autoplay video ad over the timer face, and then miscount seconds when you switch tabs. We built MakeMyTimer because none of that needs to happen. A stopwatch is hundreds of lines of code, not megabytes of tracking pixels, and it should work whether your tab is focused, backgrounded, or asleep.

This post explains what MakeMyTimer is, what we built it to fix, and the specific technical choices that make it behave the way it does. If you only ever use the tools and never read this, that's fine — they should just work. But if you've ever wondered why a 5-minute countdown ends up running closer to 5 minutes 12 seconds, the explanation is here.

Why most online timers are wrong about time

The standard way to build a timer in JavaScript is to call setInterval every 100 or 1000 milliseconds and decrement a counter. This works perfectly when the page has focus. It breaks the moment the user switches tabs, minimizes the window, or locks their screen.

Modern browsers throttle setInterval in backgrounded tabs to save battery and CPU. Chrome clamps the minimum interval to 1000ms for hidden tabs. After about 5 minutes in the background, Chromium applies intensive throttling, which limits timer fires to roughly once per minute. Firefox and Safari implement similar policies. A timer that decrements a counter every second tick will simply stop decrementing at the rate it expects. When you flip back to the tab, the displayed time is wrong — sometimes by minutes, sometimes by nothing at all, depending on how long you were away and what other work the browser was doing.

The fix is well-known but rarely implemented: instead of counting ticks, store the absolute target timestamp when the timer starts (e.g. Date.now() + remainingMs) and on every animation frame compute the difference between the current timestamp and the target. Frame skips no longer matter because each frame recomputes from real wall-clock time. This is what every MakeMyTimer countdown and stopwatch does. Switch tabs for half an hour, come back, and the display catches up immediately.

Why we use requestAnimationFrame, not setInterval

Even when a tab is focused, setIntervalis the wrong primitive for visual updates. It schedules callbacks at fixed intervals in JavaScript time, but the browser only paints at the display's refresh rate (typically 60Hz, 120Hz on newer hardware). An interval firing every 16ms doesn't guarantee a paint every 16ms — it just guarantees the JavaScript runs, which the browser may then skip rendering for, leading to choppy or stuttery numbers.

requestAnimationFramehooks directly into the browser's paint cycle. Each callback fires immediately before the next render, so the displayed milliseconds always reflect the most recent repaint-ready frame. The result is a stopwatch that updates as smoothly as your monitor allows, with no double-rendered frames and no skipped updates.

There's a second benefit: when the tab is hidden, requestAnimationFramestops firing entirely. The display loop sleeps. We don't do unnecessary work in the background — the timer's state is just (targetTimestamp, isRunning) and is always recomputable. When the tab becomes visible again, the loop resumes and the display jumps to the correct time on the next frame.

Timezones without a 200KB dependency

For timezone work, the JavaScript ecosystem historically reached for moment-timezone or its successors, all of which bundle the IANA timezone database (about 200KB minified) into the page weight. For a simple timezone converter, this is overkill.

Every modern browser ships with Intl.DateTimeFormat and Intl.supportedValuesOf('timeZone'), both backed by the host operating system's timezone data. We use these directly. Our Timezone Converter and World Clockship zero timezone data of their own — the browser already has it, and it's updated every time your OS gets a tzdata patch. Daylight saving transitions are handled correctly because they're handled by the standard library.

The five tools, briefly

  • Countdown Timer — set a duration in hours/minutes/seconds, get an audible beep when it finishes. Background-tab safe.
  • Stopwatch — millisecond precision with lap tracking. Highlights your fastest and slowest splits after you stop.
  • Timezone Converter — pick a source time and any IANA timezone, see equivalents in any other zone with current UTC offset.
  • Pomodoro Timer — automatic 25/5/15 work/short-break/long-break cycle, configurable. Desktop notifications optional.
  • World Clock — track up to ten cities side by side. Your selection persists in localStorage.

What we don't do

No accounts. No backend. No telemetry, period — there is no analytics endpoint to send data to, even if we wanted to. The only network requests after the initial page load are for static assets like fonts. The only data persisted between sessions is your world-clock city list and theme preference, both stored in your browser's localStorage under keys prefixed ttk-.

We do show ads. They load only after you accept the cookie banner, and they sit below the tool — never overlaying it, never autoplaying video. If you decline the banner, no ad scripts load at all. The site is designed to be functional with zero third-party scripts running.

Where to start

If you came here for a specific tool, the navigation up top will get you there in one click. If you want to learn something specific: timezone bugs and how to avoid them are covered in our timezone math article; the history and psychology behind the Pomodoro Technique are in this post; runners wanting to use lap times to track interval workouts can read our piece on it.

MakeMyTimer is built and maintained by File-Toolkits on GitHub. Bug reports, feature requests, and corrections go there.