- HTML 54.3%
- TypeScript 45.4%
- Dockerfile 0.3%
Implement SVG watch dial interface with timezone selectors. - SVG viewBox 220x220 with center at (110, 110) - 24-hour bezel ring at radius 108 with hour markers - Inner dial at radius 93.5 with 12-hour markers - White hands: hour, minute, second (analog movement) - Orange GMT hand (24-hour scale) - Three dropdowns: local time, 1st timezone, 2nd timezone (bezel) - Digital time displays with city/timezone labels - Watch emoji favicon to avoid 404 - Comprehensive timezone coverage across all continents 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> |
||
|---|---|---|
| src | ||
| .dockerignore | ||
| .gitignore | ||
| CLAUDE.md | ||
| docker-compose.yml | ||
| Dockerfile | ||
| index.html | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
| vite.config.ts | ||
GMT Watch Setting Assistant
A browser-based tool for visualizing and setting GMT watches. Shows local time on standard hands, a 24-hour GMT hand for a selected timezone, and a rotatable 24-hour bezel for a second timezone.
What is a GMT Watch?
A GMT watch displays time in multiple timezones simultaneously using three elements:
- Standard hands (hour/minute/second) - Display local time on a 12-hour dial
- 24-hour GMT hand - An additional hand that completes one rotation per 24 hours, reading against the 24-hour bezel
- Rotatable 24-hour bezel - A ring with markers from 0-24 that can be rotated to track a second timezone
The GMT hand reads against the bezel markers to show a second timezone. For example, if your GMT hand shows your home time and you rotate the bezel to align with a destination timezone offset, you can read the destination time by looking at where the GMT hand points on the bezel.
This tool simulates a Tudor Black Bay GMT style watch with discrete (ticking) second hand motion matching quartz watch behavior.
Usage
# Install dependencies
npm install
# Start development server
npm run dev
# Build for production
npm run build
Open the dev server in your browser. Select timezones from the dropdowns:
- GMT Hand Timezone: Sets the timezone for the 24-hour GMT hand
- Bezel Timezone: Rotates the bezel to show offset from your local time
The GMT hand reads against the bezel markers to display the second timezone.
Architecture
index.html
├── <svg id="watch-dial">
│ ├── <g id="bezel"> ─────────────── Rotates for TZ2 offset
│ │ └── 24-hour markers (0-24)
│ ├── <g id="dial-face">
│ │ └── 12-hour markers + indices
│ └── <g id="hands">
│ ├── <line id="hour-hand">
│ ├── <line id="minute-hand">
│ ├── <line id="second-hand">
│ └── <line id="gmt-hand"> ───── 24-hour hand for TZ1
├── <select id="tz1-select"> ────────── GMT hand timezone
└── <select id="tz2-select"> ────────── Bezel timezone
src/
├── main.ts ─────────── Entry point, DOM setup, update loop
├── time.ts ─────────── Time calculations, timezone conversion
└── dial.ts ─────────── SVG manipulation, rotation functions
Data Flow
setInterval(1000ms)
│
▼
Date.now()
│
├──► toLocalTime() ──► hour/min/sec angles ──► CSS rotate(hour-hand, etc)
│
├──► toTimezone(tz1) ──► 24-hour angle ──► CSS rotate(gmt-hand)
│
└──► getOffset(local, tz2) ──► bezel rotation ──► CSS rotate(bezel)
Design Decisions
Why This Structure
- time.ts isolated for testability - Pure functions, no DOM dependencies
- dial.ts handles all SVG mutations - Single place for transform logic
- main.ts orchestrates - Owns the setInterval, coordinates updates
- No state management needed - Time is derived fresh each tick from Date.now()
Invariants
- Hour hand completes 360° in 12 hours (30°/hour, 0.5°/minute for continuous movement)
- Minute hand completes 360° in 60 minutes (6°/minute)
- Second hand completes 360° in 60 seconds (6°/second)
- GMT hand completes 360° in 24 hours (15°/hour)
- Bezel rotation = offset between local timezone and TZ2 (15° per hour of offset)
Tradeoffs
- Discrete vs smooth second hand: Chose discrete (tick) to match quartz watch behavior and reduce update frequency to 1Hz
- Inline SVG vs external file: Inline allows simpler DOM queries but makes HTML longer
- Two separate TZ selectors vs unified UI: Separate is clearer - each control maps to one watch element (GMT hand or bezel)
- Timezone offset via absolute time difference: Handles DST correctly by calculating actual wall-clock times in both zones rather than naive hour subtraction, which fails when one zone has shifted and the other hasn't