webdevpuneet.com

Workout Interval Timer — Free HTML CSS JS Snippet

Workout Interval Timer · Tools · Plain HTML, CSS & JS · Live preview

Copy & paste

Source Code

<div class="wint-card">
  <div class="wint-config" id="wintConfig">
    <div class="wint-field">
      <label>Work (sec)</label>
      <input type="number" id="wintWork" value="20" min="5" max="180">
    </div>
    <div class="wint-field">
      <label>Rest (sec)</label>
      <input type="number" id="wintRest" value="10" min="5" max="120">
    </div>
    <div class="wint-field">
      <label>Rounds</label>
      <input type="number" id="wintRounds" value="8" min="1" max="30">
    </div>
  </div>

  <div class="wint-ring-wrap" id="wintRingWrap">
    <svg class="wint-ring" viewBox="0 0 200 200">
      <circle class="wint-ring-track" cx="100" cy="100" r="86"></circle>
      <circle class="wint-ring-fill" id="wintRingFill" cx="100" cy="100" r="86"></circle>
    </svg>
    <div class="wint-center">
      <span class="wint-phase" id="wintPhase">READY</span>
      <span class="wint-clock" id="wintClock">20</span>
      <span class="wint-round" id="wintRound">Round 1 of 8</span>
    </div>
  </div>

  <div class="wint-controls">
    <button type="button" class="wint-btn primary" id="wintStart">Start</button>
    <button type="button" class="wint-btn" id="wintReset">Reset</button>
  </div>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month, 7 days free), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Single state machine
One state object and one tick() function drive every phase transition predictably.
Phase-colored ring
The progress ring changes color by phase — orange work, blue rest, green done.
Scale-pulse transition cue
A brief scale animation on the phase label marks every transition without audio.
Pause and resume
Pausing preserves the exact time remaining; resuming continues from that point.
Locked config while running
Work/rest/round inputs disable during a run so the ring math can never desync.
Configurable rounds
Work duration, rest duration, and round count are all plain number inputs.
Per-phase ring reset
The ring fills and drains fresh each phase instead of draining across the whole workout.
Clear completion state
A checkmark and "Workout complete" message replace the countdown when finished.

About this UI Snippet

Workout Interval Timer — Configurable Work/Rest Rounds With a Phase-Coded Ring

Screenshot of the Workout Interval Timer snippet rendered live

Interval training lives or dies on the timer: work and rest phases need to be unmistakable at a glance, transitions need a cue you can catch out of the corner of your eye mid-exercise, and the round count needs to be right there so you know how much is left. This snippet builds a Tabata-style interval timer in plain HTML, CSS, and vanilla JavaScript, with configurable work duration, rest duration, and round count. Pair it with a water intake tracker or sleep cycle chart in a broader fitness dashboard, or compare it with pomodoro timer and countdown timer for related countdown patterns.

A small state machine, not scattered flags

The timer's entire behavior lives in one state object — phase (ready/work/rest/done), the current round, and timeLeft — updated by a single tick() function that runs every second. When timeLeft runs out, tick() decides the next phase: work rolls into rest, rest either advances to the next round's work phase or, on the final round, calls finish(). Centralizing this logic in one function is what makes the timer's behavior predictable and easy to extend (adding a warm-up phase, for instance, is one more branch).

A ring that changes color with the phase

The same SVG stroke-dashoffset technique used in a progress ring drives the countdown visually, but here the ring's *color* changes with the phase — orange during work, blue during rest, green when the workout completes — so you can tell which phase you're in from peripheral vision without reading the phase label. The ring resets to full and counts back down to empty on every new phase, rather than continuing to drain across the whole workout.

A pulse cue at every transition

At the exact moment a phase changes — work to rest, rest to the next round, or completion — the phase label briefly scales up and back down via a CSS class toggle (pulse(), removed after 250ms with setTimeout). This is the audio-free equivalent of a haptic buzz: a sharp, brief visual jolt exactly when your attention should shift, which matters far more mid-workout than a color that merely changes gradually.

Pause and resume, not just start

Clicking the primary button while running pauses the countdown in place (clearing the interval without resetting timeLeft) and relabels itself "Resume"; clicking again picks up exactly where it left off. The config inputs (work/rest/round duration) lock as soon as a workout starts, preventing an accidental mid-workout edit from desyncing the ring's percentage math, and unlock again on Reset.

Extending it

Add a short countdown "warm-up" phase before round 1, a distinct color for the final round to build urgency, or swap the visual pulse for the Web Vibration API's navigator.vibrate() on supporting mobile browsers for a real haptic cue alongside the visual one.

Step by step

How to Use

  1. 1
    Set work, rest, and roundsAdjust the three number inputs before starting — they lock once the timer is running.
  2. 2
    Click StartThe ring fills orange and counts down the first work interval.
  3. 3
    Watch the phase changeAt zero, the label pulses, the ring turns blue, and rest begins counting down.
  4. 4
    Follow the roundsWork and rest alternate automatically until the configured round count is reached.
  5. 5
    Pause and resumeClick the primary button mid-countdown to pause in place; click again to resume.
  6. 6
    Finish or resetThe ring turns green with a checkmark on completion, or click Reset any time to start over.

Real-world uses

Common Use Cases

HIIT and Tabata workouts
Run classic 20 seconds on and 10 seconds off intervals, with a configurable round count and the round shown prominently.
Circuit training apps
Adapt the phases to cue exercise stations, with orange work and blue rest colours that are unmistakable at a glance.
Fitness dashboard widgets
Combine with a water intake tracker and a sleep cycle chart for a complete health screen.
Work-sprint timers
Reuse the work and rest ring for focus sprints next to a Pomodoro timer, or time laps with a stopwatch.
Countdown state machine reference
Study one state object and one tick() function driving every phase, with a brief scale pulse cueing each transition.

Got questions?

Frequently Asked Questions

tick() runs every second, decrementing timeLeft. When timeLeft drops below zero, a single block of logic decides the next phase: if the current phase was "work," it switches to "rest" with the configured rest duration; if it was "rest," it either advances to the next round's work phase or, if the last round just finished resting, calls finish(). This keeps all transition logic in one place instead of scattered across separate timers.

During an actual workout you're rarely looking directly at the timer — you catch it in peripheral vision between reps. A phase-appropriate ring color (orange for work, blue for rest, green for done) is readable at a glance without focusing on or reading the text label, which is the whole point of a color-coded cue: it works even when you can't spare full attention.

Pausing calls clearInterval() on the running timer but does not touch timeLeft, phase, or round — so the countdown is frozen exactly where it was. The button relabels itself "Resume," and clicking it again starts a new setInterval that continues decrementing from the preserved timeLeft, rather than restarting the current phase from its full duration.

The ring's fill percentage is calculated as timeLeft divided by the current phase's configured duration (work or rest). If you changed the work duration mid-countdown, that denominator would suddenly not match the timeLeft value that was already counting down, producing a ring that jumps or shows an inaccurate percentage. Locking the inputs during a run prevents that mismatch; Reset re-enables them.

Inside pulse() (or wherever a phase transition is triggered), call navigator.vibrate(200) — or a short pattern array like [100, 50, 100] — guarded by a check for navigator.vibrate existing, since the Vibration API is only supported on some mobile browsers and requires a user gesture in the page's history. Keep the visual scale-pulse as the primary cue since vibration support and permissions are inconsistent across browsers.