Section project · 23 guided steps

Stilltime

A clock, a countdown timer and an alarm in one calm page.

The brief

A community wellness center wants a focus timer for its study room. Build Stilltime: a quiet web app that shows the current time in a time zone the visitor chooses, lets them pick a focus or rest session and count it down, and gently rings when time is up, with a choice of two sounds. It must stay accurate when the tab is in the background, work on a phone, and be usable from the keyboard.

What it must do

  • Show the current time as HH:MM:SS, updating every second, and today's date in words.
  • Let the visitor pick a time zone; the clock, date and zone's short name all switch to it.
  • Three modes: Focus sets 25 minutes, Rest sets 5, Custom lets them type their own.
  • Six one-click lengths (5, 15, 25, 45, 60, 90 minutes) plus boxes for exact minutes and seconds.
  • Start, pause, resume and reset the countdown. A countdown of zero is refused with a friendly message.
  • Never drift: stay exact even if the tab is hidden for a while.
  • At zero, cover the page with a gentle message and play the chosen sound, repeating until answered.
  • Let the visitor silence the sound without closing the message, or dismiss it to start over.
  • Two sounds, a singing bowl and a temple gong, each with a preview.
  • Space pauses and resumes, Escape dismisses the alarm, and typing in a box never triggers either.
  • Work on a phone, and respect a request for reduced motion.

The functions it needs, and what kind of function each is

tz()

Says which time zone to use.

A reader: no input, gives back a zone name or undefined for the device's own zone.

fmtTime(date), fmtDate(date), getZoneAbbr(date)

Turn a moment into the clock's text, the date in words, and the zone's short name.

Formatters: a Date goes in, text comes out. They change nothing.

updateClock()

Draws whatever the big display should show right now.

A renderer: reads the app's state, writes to the page, returns nothing.

chosenSeconds()

Works out how long the visitor asked for, safely.

A reader and validator: reads two boxes, clamps them, gives back seconds.

setDuration(min, sec)

Fills the boxes and marks the matching preset.

A writer: changes the page, returns nothing.

timerDisplay(seconds)

Turns seconds left into MM:SS or HH:MM:SS.

A formatter: a number in, text out.

showToast(message)

Shows a short message that fades by itself.

A writer with a timer.

startTimer(), pauseTimer(), resumeTimer(), resetTimer(), tick()

Move the timer between its states and run the countdown.

Event handlers and a loop body: each changes the state variables, then updates the page.

unlockAudio(), tone(), stopAudio(), playBowl(), playGong(), playSelected()

Start the sound engine, make tones, build two sounds, silence everything.

Wrappers and composers around the Web Audio API.

startAlarm(), dismissAlarm()

Show and hide the time-is-up screen, with its sound.

State changes: show or hide the overlay, start or stop the sound.

What it keeps track of, and whether it needs a database

No database

Nothing has to survive closing the tab, so there is nothing to save and no server. Everything lives in variables while the page is open. (Remembering the last choice with localStorage is a good extension.)

mode, selectedSound

Which mode and which sound are chosen, as text such as "focus" and "bowl".

running, paused

Two true-or-false values that together say whether the timer is counting, paused, or neither.

endAt, remaining

endAt is the timestamp the countdown finishes; remaining is the seconds left. Keeping an end time, not a counter, is what stops drift.

ticker, alarmRepeater, toastTimer

The ids setInterval and setTimeout handed back, kept so each can be stopped.

audioCtx, activeNodes

The sound engine and every tone currently playing, kept so they can be silenced.

The browser features and outside services it uses

The DOM and events

querySelector, textContent, classList, data attributes and addEventListener.

Documentation ↗

Date and Intl.DateTimeFormat

The current moment as a timestamp, and formatting it for any time zone.

Documentation ↗

Timers

setInterval and setTimeout, and their clear functions.

Documentation ↗

Web Audio API

AudioContext, oscillators and gain nodes make the sounds with no audio files.

Documentation ↗

Page Visibility

document.hidden and visibilitychange, to catch up when the tab returns.

Documentation ↗

How the work is broken down

Finished apps are built one small piece at a time. Each step below is explained in plain language, then in terms of the kind of code it needs, and only then do you write it.

  1. Lay out the page · 4 steps
    Build the structure your JavaScript will talk to, then a calm design. No JavaScript yet.
  2. The live clock · 4 steps
    Make the big display tell the time, in any zone, and keep it ticking.
  3. Choose a duration · 3 steps
    Read what the visitor asked for, and make presets and modes fill it in.
  4. The countdown · 5 steps
    Count down accurately, and let the visitor pause, resume and reset.
  5. The alarm · 4 steps
    Make the sounds, and show the gentle time-is-up screen when the countdown ends.
  6. Finishing touches · 3 steps
    Keyboard shortcuts, catching up when the tab returns, and the care that makes it pleasant for everyone.

Before you start

This project uses ideas from:

See it finished ↗

The finished Stilltime, in one file. Build each part yourself first, then compare.