Step by step
- Store endAt = Date.now() + duration in milliseconds.
- Every 250 ms, display Math.max(0, endAt − Date.now()) as mm:ss.
- At zero: play a sound, switch phase, set the next endAt.
At a glance
| Piece | How | Why |
|---|---|---|
| Clock-based countdown | endAt − Date.now() | No drift in background tabs |
| Phase switching | Counter of finished focus blocks | Long break every 4 |
| Alert | Audio and the Notification API | Works when the tab is hidden |
Related questions
Why not setInterval with a counter?
Browsers throttle timers in hidden tabs, so a counter falls behind; the clock does not.
How do I show the time in the tab title?
Set document.title to the remaining mm:ss on each update.
Why does the sound not play?
Browsers block audio until the user clicks; start the timer from a button click.
More questions: pomodoro timer
- What is the Pomodoro technique and a Pomodoro timer?
- How to use a Pomodoro timer step by step
- Why is a Pomodoro 25 minutes, and can you change it?
- Why does the Pomodoro technique work?
- How to use a Pomodoro timer with ADHD
- How to use a Pomodoro timer for studying
- Silent Pomodoro timer: how to use it without sound
- Pomodoro timer not working? Common fixes
- Pomodoro timer on iPhone and Android without an app
- How to embed a Pomodoro timer in Notion
- Pomodoro technique vs Deep Work: which should you use?
- How do you pronounce Pomodoro?
Open the full pomodoro timer →
Updated November 14, 2026.