A small single-page application providing multiple independent stopwatches (count-up timers) with:
- Multiple timers, each can be started/stopped independently
- Rename timers by clicking their name; timers have no name by default — the UI shows a placeholder (e.g. "Unnamed Timer")
- Buttons to start all / stop all timers
- Accurate timing (no accumulating time via setInterval) — uses precise timestamps and requestAnimationFrame for display updates
- Persistence via localStorage (timers and settings are restored)
- Compact (single-line) and loose (card) layouts
- Internationalization: English and Japanese provided; easy to add more languages
- Dark mode support with 3 modes: automatic (follows system), light, or dark
This project is intentionally small and framework-agnostic in architecture so you can extend it.
Getting started (development)
-
Install deps:
npm install -
Run dev server:
npm run dev -
Build:
npm run build -
Preview build:
npm run preview
Notes about accuracy
- Timer logic stores precise timestamps and accumulated elapsed milliseconds. When a timer is running we compute elapsed as: accumulated + (Date.now() - startTime)
- requestAnimationFrame is used to refresh the UI. We do not add up seconds using setInterval so elapsed time stays accurate even if the tab is backgrounded or the browser throttles timers.
Internationalization
- Files are in
src/locales/*.json. - Use
src/i18n.tsto add or change translations.
Persistence keys (localStorage)
multi-stopwatch.timers— JSON array of timersmulti-stopwatch.settings— settings (layout, language, theme)
Structure
- index.html
- src/
- main.tsx
- App.tsx
- components/
- TimerItem.tsx
- Controls.tsx
- i18n.ts
- locales/en.json
- locales/ja.json
- styles.css
- types.ts
- utils/time.ts
License: MIT