Skip to content
AA

GlobalClock — World Clock Dashboard

Live analog and digital clocks for timezones worldwide. Drag-to-reorder with localStorage persistence, ambient ticking via Web Audio, and a midnight confetti effect per timezone.

3 min read

A world clock dashboard showing live analog and digital clocks for countries across multiple timezones. Clocks update every second, support both display modes, can be reordered by drag and drop, and play an optional ambient ticking sound. Each clock independently watches for its local midnight and fires a confetti burst when it hits. The whole thing scores 100/100/100/100 on Lighthouse.

How It Was Built

Every clock reads time using the browser's Date and Intl.DateTimeFormat APIs — no external time library. Intl.DateTimeFormat with a timeZone option returns the hour, minute, and second for any IANA timezone string. A single setInterval ticking every second updates a shared timestamp in state; each clock component derives its local time from that timestamp on every render. One interval, one state update, all clocks stay in sync.

Analog clock hands are SVG elements. The rotation angle for each hand is calculated from the local time values: seconds map to seconds × 6deg, minutes to (minutes + seconds / 60) × 6deg, and hours to (hours % 12 + minutes / 60) × 30deg. The minute and hour hands interpolate the seconds so they sweep smoothly rather than jumping.

Drag-and-drop reordering uses @dnd-kit/sortable. The clock order is an array of timezone IDs stored in localStorage. On every reorder, the array is written back to localStorage so the order survives a refresh.

The ticking sound is synthesised with the Web Audio API — a short OscillatorNode burst fired once per second. No audio file, no network request. The oscillator creates a brief click, the gain node controls the volume, and both are disconnected immediately after the burst to avoid memory leaks. The toggle lives in localStorage so the preference persists.

Midnight detection runs inside each clock's render cycle. Before updating the displayed time, the component checks whether the previous second was 23:59:59 and the current second is 00:00:00 for that specific timezone. If it is, canvas-confetti fires a burst scoped to that card's position on screen.

Architecture

Single setInterval (1s)
  └─ updates shared timestamp in state
       └─ each ClockCard derives local time via Intl.DateTimeFormat
            ├─ analog mode: SVG hands rotated by computed degrees
            ├─ digital mode: formatted time string
            └─ midnight check: fires canvas-confetti if 00:00:00

Drag-and-drop (@dnd-kit/sortable):
  └─ clock order = array of timezone IDs
       └─ written to localStorage on every reorder
            └─ rehydrated on load

Web Audio ticking:
  └─ OscillatorNode burst per second (no audio file)
       └─ disconnected immediately after firing
            └─ toggle persisted to localStorage

Stack

ToolVersionRole
React18UI layer
TypeScript5Strict mode
VitelatestDev server + build
Tailwind CSSv3Styling + responsive grid
@dnd-kitlatestSortable drag-and-drop
Framer MotionlatestCard transitions
Web Audio APIbrowserSynthesised ticking sound
Canvas ConfettilatestMidnight burst effect