Template
1
Fork 0
stopwatch/README.md
Leonardo Devai 11e5f133b5 Review and modernize all 42 projects to the updated standard
Pinned 2026 toolchains (Go 1.26, Rust 1.98/edition 2024, Python 3.14 + uv, Node 24,
Zig 0.16, NixOS 26.05), postgres 18 / mongo 8, lockfiles built from, non-root
runtimes, .dockerignore, per-project LICENSE, READMEs with the git.devai.io clone
line, checkout@v7 CI. Security fixes in the legacy Rust APIs (any-password login,
self-assigned admin, hard-coded JWT secret), JWT alg/exp/sub enforcement across the
blog series, safe markdown links in the frontends, and many smaller bugs — every
project was built, run and exercised end to end.

Adds scripts/publish.sh + a CI publish job that splits every folder into its own
repo at git.devai.io/templates/<folder>.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01128fhuZbgivaSJvtMf4s1G
2026-09-27 21:10:38 +02:00

2 KiB

stopwatch

A working stopwatch — start, stop, reset and laps — accurate to the hundredth of a second. It teaches the one thing most first attempts get wrong about time: measure it, don't count it.

Run

Get it: git clone https://git.devai.io/templates/stopwatch.git

Double-click index.html — it opens in your browser and works. Nothing to install, no build step.

Or serve it like production:

docker compose up --build

Then open http://localhost:8080.

How it works

The tempting version adds 10 ms every time a 10 ms timer fires. It drifts: timers fire late, and background tabs skip them entirely.

This one remembers when the current run started and subtracts:

elapsed + (performance.now() - startTime)

performance.now() is the browser's clock for measuring durations — it only moves forward, even if the computer's date and time are adjusted mid-run. setInterval(render, 30) only decides how often the display repaints; what it shows is always computed from the clock, so a late tick can never make it wrong. Stopping banks the time so far into elapsed; starting again carries on from there. A lap is just format(currentTime()) added to the list.

Try it: show milliseconds (three digits) instead of hundredths, highlight the fastest lap, or start and stop with the space bar.

Layout

index.html   the display, three buttons and an empty lap list
app.js       the timing state, format() and the button handlers
styles.css   the look — tabular digits so the numbers don't jiggle

Deploy

Push to your own GitHub repo and the shipped workflow (.github/workflows/ci.yml) tests the compose stack, publishes the image to GHCR, and — once you set the DEPLOY_HOST / DEPLOY_USER variables and DEPLOY_KEY secret — deploys it to your server over ssh.


Part of devai.io — the Web Basics track: HTML, CSS & JavaScript, one concept at a time. Next up: color-picker.