How to Create a Stopwatch or Countdown Timer (Free, In Your Browser)
Stopwatches, countdown timers, and interval clocks are daily tools for cooking, fitness, studying, and meetings. This guide covers when to use which, the right tool, and how to build one yourself in JavaScript.
A stopwatch counts up from zero (used for measuring how long something takes). A countdown timer counts down from a set duration (used for limiting how long you do something). A pomodoro timer alternates work and break intervals. The Uttir Stopwatch, Countdown Timer, and Pomodoro Timer cover the three cases, all in your browser, no install. For developers, the same tools are 30 lines of JavaScript using setInterval and Date.now().
Stopwatches, countdown timers, and interval clocks are the unsung heroes of daily productivity. Cooking, exercising, studying, presenting, meditating — all of them need a timer. This guide covers the three types, when to use which, the right tool, and how to build one yourself if you want to understand the internals.
The three timer types
Stopwatch (count up)
A stopwatch counts up from zero, showing elapsed time. Used to measure how long something takes: a run, a workout, a work task, a phone call. The display is "MM:SS" or "HH:MM:SS" — minutes, seconds, and sometimes hundredths of a second. The standard controls: start, stop, lap, reset.
Best for: measuring duration when the endpoint is unknown. A 5K run ends when you cross the finish line, not at a preset time. A study session ends when you decide it does, not at a fixed minute.
Countdown timer (count down)
A countdown timer counts down from a set duration to zero. Used to limit how long you do something: a 25-minute pomodoro, a 10-minute break, a 5-minute meditation, a 90-minute meeting that needs to end. The display is the remaining time. The standard controls: set duration, start, pause, reset. An alert when the timer hits zero.
Best for: time-boxing. The decision is made in advance ("I will work for 25 minutes") and the timer enforces it. The endpoint is known, the question is just "how much time is left".
Interval timer (cycles)
An interval timer alternates between two or more durations, repeating forever (or for a set number of cycles). Used for: a pomodoro (25 min work, 5 min break, repeat), a HIIT workout (30s on, 30s off, repeat), a circuit training session, a study session with breaks. The standard controls: define intervals, start, pause, skip to next.
Best for: structured routines that alternate. The pattern is the point; the timer just enforces the alternation.
When to use which
The rule of thumb:
- "How long did that take?" — stopwatch.
- "Tell me when X minutes are up." — countdown timer.
- "Tell me to switch every X minutes, for Y cycles." — interval timer.
The pomodoro is a special case: a 25-minute work interval followed by a 5-minute break interval, repeating. It is the most common interval timer.
The right tool for the job
For a quick stopwatch (no install)
The Uttir Stopwatch is a browser-based stopwatch with start, stop, lap, and reset. No install, no account. Open the page, click start, get on with your work.
For a quick countdown (no install)
The Uttir Countdown Timer is a browser-based countdown with custom duration, alert sound, and pause/resume. Set a duration, click start, get a notification when it's done.
For pomodoro and interval work (no install)
The Uttir Pomodoro Timer is a browser-based interval timer with configurable work/break durations, cycle count, and auto-start for the next interval. The standard pomodoro pattern is the default; you can customize for HIIT, study, or any other pattern.
For a hardware option (more accurate)
Phone timers and smartwatch timers are more accurate than browser timers, because they run in the OS. The browser timer is fine for most uses (within 1 second per minute), but for precise timing (a competition, a science experiment), use the OS or a dedicated device.
Browser timer accuracy
A browser timer is not perfectly accurate. The reasons:
- Throttling — when the tab is in the background, browsers throttle
setIntervalandsetTimeoutto 1 per second or less. The timer keeps "wall clock" time but the UI updates less often. - JavaScript event loop — if the main thread is busy (a long-running task, a heavy calculation), the timer callback does not fire until the thread is free. The displayed time can lag by 100-200 ms.
- System clock changes — if the system clock is adjusted, a naive timer (using
setInterval) will be wrong. A correct timer usesDate.now()to compute elapsed time and ignores the system clock.
The reliable pattern: store the start time with performance.now() or Date.now(), then in each tick compute elapsed = now - startTime. The timer shows the actual elapsed time, regardless of how often the tick fires.
How to build a stopwatch in JavaScript
Minimal version (about 30 lines):
let startTime = 0;
let elapsed = 0;
let running = false;
let rafId = null;
const display = document.getElementById('display');
function format(ms) {
const total = Math.floor(ms / 10); // hundredths
const cs = total % 100;
const totalSec = Math.floor(total / 100);
const s = totalSec % 60;
const m = Math.floor(totalSec / 60) % 60;
const h = Math.floor(totalSec / 3600);
const pad = (n) => String(n).padStart(2, '0');
return h > 0 ? `${pad(h)}:${pad(m)}:${pad(s)}` : `${pad(m)}:${pad(s)}.${pad(cs)}`;
}
function tick() {
const now = performance.now();
display.textContent = format(elapsed + (now - startTime));
rafId = requestAnimationFrame(tick);
}
function start() {
if (running) return;
startTime = performance.now();
running = true;
tick();
}
function stop() {
if (!running) return;
elapsed += performance.now() - startTime;
running = false;
cancelAnimationFrame(rafId);
}
function reset() {
stop();
elapsed = 0;
display.textContent = format(0);
}
Three details to notice:
performance.now()— gives a monotonic time in milliseconds, immune to system clock changes.Date.now()works too but is affected by clock adjustments.requestAnimationFrame— fires at the display refresh rate (typically 60Hz), giving a smooth display without wasting CPU.setIntervalworks but creates unnecessary wake-ups.- Accumulated elapsed — store the elapsed time when the user pauses, and add to it when they resume. This avoids drift from the long task boundary.
How to build a countdown timer in JavaScript
The countdown is the same pattern, but with a target end time:
let endTime = 0;
let remaining = 0;
let running = false;
let rafId = null;
function tick() {
const now = Date.now();
const left = Math.max(0, endTime - now);
display.textContent = format(left);
if (left <= 0) {
alert();
stop();
return;
}
rafId = requestAnimationFrame(tick);
}
function start(durationMs) {
if (running) return;
endTime = Date.now() + remaining + durationMs;
running = true;
tick();
}
function stop() {
if (!running) return;
remaining = Math.max(0, endTime - Date.now());
running = false;
cancelAnimationFrame(rafId);
}
The key: the endTime is computed once when start is called. Every tick computes the remaining time as endTime - now. The display is accurate even if the tick fires late.
How to build an interval timer (pomodoro)
The interval timer is the same pattern with a list of intervals:
const intervals = [
{ name: 'Work', duration: 25 * 60 * 1000 },
{ name: 'Break', duration: 5 * 60 * 1000 }
];
let currentIndex = 0;
let cycleCount = 0;
let endTime = 0;
function startCycle() {
const current = intervals[currentIndex % intervals.length];
endTime = Date.now() + current.duration;
label.textContent = current.name;
// ... tick logic ...
}
function onIntervalEnd() {
currentIndex++;
if (currentIndex % intervals.length === 0) cycleCount++;
startCycle();
}
The Uttir Pomodoro Timer implements this with the standard 25/5 default, configurable durations, and configurable cycle count. The full version is about 200 lines including the UI.
Common pitfalls
Using setInterval without storing elapsed time
The most common bug: setInterval(() => display.textContent = counter++, 1000). This counts the number of times the callback fired, not the actual elapsed time. If the tab is throttled (in the background), the counter slows down. The fix: use the Date.now() pattern above.
Forgetting to clear the interval
If the user navigates away or the component unmounts without clearing the interval, the timer keeps firing forever, leaking memory. Always clearInterval in the cleanup function.
Not handling the "fire while tab is hidden" case
When the tab is in the background, setInterval is throttled to 1Hz. When the user comes back, the display may be wrong. The fix: use the Date.now() pattern, which is self-correcting. The display will jump to the correct time on the next tick.
No visual or audio alert
A countdown that silently hits zero is useless. The fix: beep (Web Audio API), vibrate (mobile), or change the title bar (e.g. document.title = "TIME'S UP — Uttir").
Bottom line
Stopwatch for measuring, countdown for time-boxing, interval timer for routines. The Uttir Stopwatch, Countdown Timer, and Pomodoro Timer cover the three cases, all in your browser, no install. For developers, the patterns above (use Date.now(), not setInterval for elapsed time) are the right way to build a reliable timer.