Tabata is a form of high-intensity interval training (HIIT). A standard Tabata session consists of 8 rounds of 20 seconds of intense exercise followed by 10 seconds of rest β totalling just 4 minutes of pure effort.
This app is a clean, no-nonsense timer built to help you focus entirely on your workout. No ads, no sign-ups, no distractions. Just set your intervals, hit start, and go.
- βοΈ Fully customizable β set your own preparation time, work time, rest time, and number of rounds
- π Audio cues β beeps for countdowns, phase transitions, and workout completion
- π Visual progress ring β a smooth circular timer so you always know how much time is left
- π Dark & Light mode β pick the theme that suits your gym or your screen
- πΎ Settings saved automatically β your configuration is remembered between sessions
- π΄ Works offline β fully functional without an internet connection (PWA)
- π² Installable on any device β add it to your home screen like a native app
- Set your intervals using the
+/βbuttons on the main screen.- Preparation: warm-up countdown before the first round starts
- Work: the duration of each effort interval
- Rest: recovery time between rounds
- Rounds: total number of work/rest cycles
- The summary panel shows you the total workout time before you begin.
- Press Start Workout and follow the on-screen timer and audio cues.
- During the workout, you can pause, skip the current phase, or stop at any time.
- When all rounds are done, a completion screen (and a little melody πΆ) will let you know you're finished.
Tip: You can install this app on your phone by tapping "Add to Home Screen" in your browser β it works just like a native app, even offline!
This section is intended for developers who want to understand, modify, or self-host this project.
| Layer | Technology |
|---|---|
| Markup | HTML5 (Semantic) |
| Styling | Vanilla CSS3 (Custom Properties, CSS Grid, Flexbox) |
| Logic | Vanilla JavaScript (ES6+) |
| Audio | Web Audio API |
| Offline | Service Worker + Cache API |
| Installable | Web App Manifest (PWA) |
No frameworks, no build tools, no dependencies. Zero
npm installrequired.
/
βββ index.html # Single-page application shell
βββ manifest.json # PWA manifest (name, icons, theme, display mode)
βββ sw.js # Service Worker (offline caching strategy)
βββ css/
β βββ styles.css # All styles β theming, layout, animations
βββ js/
β βββ main.js # All application logic
βββ images/
βββ favicon.ico
βββ logo.png
βββ logo-white.png
βββ logo-black.png
This is a single-page application (SPA) with no routing library. Three screens are rendered in the DOM simultaneously and toggled via CSS classes:
#configScreenβ workout configuration (default active screen)#timerScreenβ live countdown timer#completedScreenβ post-workout summary
Screen transitions use a CSS opacity fade (250ms) managed by the switchScreen() function.
All logic lives in js/main.js and is organized around a few key areas:
The application uses two plain objects to manage its state:
// Static configuration (user-defined, persisted to localStorage)
const config = { preparation, work, rest, rounds };
// Dynamic runtime state (updated every second during workout)
let state = { phase, currentRound, timeLeft, paused, interval };The timer is driven by a standard setInterval at 1-second resolution. On each tick:
- Countdown beeps play for the last 3 seconds of any phase.
timeLeftis decremented.- When it hits 0,
nextPhase()is called to transition the state machine.
The phase state machine follows this flow:
[preparation] β [work] β [rest] β [work] β ... β [work] β [completed]
If preparation or rest time is set to 0, those phases are skipped automatically.
The circular progress indicator is an SVG <circle> animated via CSS stroke-dashoffset. Instead of updating it every second (which would be janky), a single CSS transition is applied for the full duration of the phase:
ring.style.transition = `stroke-dashoffset ${total}s linear`;
ring.style.strokeDashoffset = 0;On pause, the current computed strokeDashoffset is captured and the transition is frozen. On resume, a new transition is applied for the remaining timeLeft.
All sounds are generated programmatically β no audio files are used. The playSound(frequency, duration) function creates an OscillatorNode connected to a GainNode with an exponential ramp for a natural fade-out. The AudioContext is initialized lazily on the first user interaction to comply with browser autoplay policies.
User preferences (config values, theme, sound state) are saved to localStorage on every change and loaded on page init via loadPreferences().
During an active workout, the app requests a WakeLock via the Screen Wake Lock API to prevent the device from sleeping. The lock is released when the workout ends or is stopped.
The app is a fully compliant PWA:
manifest.jsonβ defines the app name, icons, theme color, andstandalonedisplay mode (removes the browser UI when installed).sw.jsβ implements a cache-first strategy. On install, all static assets are pre-cached. Subsequent requests are served from cache, falling back to the network if a resource is not found.
Install β Cache all static assets
Fetch β Cache hit? Serve from cache : fetch from network
Activate β Delete stale caches from previous versions
No build step, no dependencies, no terminal required.
Option 1 β Just open the file (recommended for most cases)
Double-click index.html to open it directly in your browser. The timer will work fully. This is the simplest way to use or test the app locally.
Option 2 β Local HTTP server (required for PWA/Service Worker)
The Service Worker (offline support, installability) requires the page to be served over http:// β it will not register when opened via file://. If you want to test PWA features, use any static server:
# Using Python
python -m http.server 8000
# Using Node.js (npx)
npx serve .
# Using VS Code
# Install the "Live Server" extension and click "Go Live"Then open http://localhost:8000 in your browser.
The app relies on standard modern web APIs and is compatible with all current major browsers:
| Feature | Chrome | Firefox | Safari | Edge |
|---|---|---|---|---|
| Core Timer | β | β | β | β |
| Web Audio API | β | β | β | β |
| Service Worker / PWA | β | β | β | β |
| Screen Wake Lock API | β | β | β | |
| CSS Custom Properties | β | β | β | β |
The Screen Wake Lock API has limited support on some iOS versions. The app degrades gracefully β the timer continues to function normally, but the screen may turn off during workouts on affected devices.
Initial release.
- βοΈ Configurable preparation, work, rest time and number of rounds
- π Circular progress ring with smooth CSS animation
- π Procedural audio cues via Web Audio API (countdown beeps, phase transitions, completion melody)
- π Dark and light theme with persistent preference
- πΎ Settings saved to localStorage
- π² PWA support β installable and fully offline-capable via Service Worker
- π Screen Wake Lock to prevent device sleep during workouts
Copyright (c) 2026 Devilquest β MIT License
If you enjoy this project, any support is greatly appreciated!

