A sleek, blazing-fast digital clock with a built-in stopwatch — simple on the surface, thoughtfully engineered underneath.
- Digital Clock
Digital Clock is a minimalist web application with two core experiences:
- A live digital clock with a 12-hour/24-hour toggle.
- A stopwatch with start, stop, and reset controls.
The project uses a modular Node.js HTTP server (no framework) and modular browser-side JavaScript.
- Real-time digital clock display.
- 12h/24h format switch with persistence through
localStorage(clockMode). - Stopwatch powered by
performance.now()+requestAnimationFramefor monotonic elapsed timing. - Responsive navigation for desktop and mobile.
- Dynamic runtime config endpoint at
/config.jsfor browser debug behavior. - Static file server with custom 404 handling.
- Containerized runtime with a hardened multi-stage
Dockerfile. - Automated multi-arch image publishing to Docker Hub and GHCR on version tags.
- Node.js 24.13.1 (LTS)
- npm
git clone git@github.com:Amnoor/Digital-Clock.git
cd Digital-Clock
npm ciPORT=5500
DEBUG=truenpm run devnpm starthttp://localhost:80/
http://localhost:80/stopwatch/
If you set a custom PORT, use that port instead.
| Variable | Required | Default | Description |
|---|---|---|---|
PORT |
No | 80 |
HTTP port for the Node server. |
DEBUG |
No | false |
Enables non-essential log levels (debug, trace, log). |
PORT=5500
DEBUG=truenpm run devPORT=5500
DEBUG=truenpm run dev| Route | Method | Behavior |
|---|---|---|
/ |
GET | Serves index.html (clock page). |
/stopwatch |
GET | 301 redirect to /stopwatch/. |
/stopwatch/ |
GET | Serves stopwatch/index.html. |
/config.js |
GET | Serves dynamic config script with window.APP_CONFIG.DEBUG. |
/* |
GET | Serves static assets; returns custom 404 page if missing. |
server-modules/server/: HTTP server bootstrap and port binding.server-modules/router/: request flow orchestration.server-modules/paths/: URL parsing and project root resolution.server-modules/static/: static path resolution, file serving, stopwatch redirect.server-modules/config/: dynamic/config.jsgeneration.server-modules/responses/: shared response helpers (404/500/redirect).server-modules/mime/: MIME type mapping.server-modules/logs/: server logger with debug gating.
client-modules/clock/: live clock updates and formatting.client-modules/preferences/: load/save mode preferences and toggle listeners.client-modules/stopwatch/: stopwatch state and control logic.client-modules/navigation/: mobile menu behavior and ARIA state updates.client-modules/logs/: browser logger with debug gating.
router.jsinitializes logger, creates a request handler, and starts the server.- Router parses the request URL and checks special handlers (
/config.js,/stopwatch). - Static resolver maps path to file path and returns the content with security headers.
- Missing assets return
404.html; unexpected failures return a 500 response.
Digital-Clock/
├─ assets/
│ ├─ Mobile/nav.svg
│ ├─ background.webp
│ └─ favicon.svg
├─ client-modules/
│ ├─ clock/index.js
│ ├─ logs/index.js
│ ├─ navigation/index.js
│ ├─ preferences/index.js
│ └─ stopwatch/index.js
├─ server-modules/
│ ├─ config/index.js
│ ├─ logs/index.js
│ ├─ mime/index.js
│ ├─ paths/index.js
│ ├─ responses/index.js
│ ├─ router/index.js
│ ├─ server/index.js
│ └─ static/index.js
├─ stopwatch/
│ ├─ index.html
│ ├─ index.js
│ └─ style/main.css
├─ style/
│ ├─ body.css
│ ├─ main.css
│ └─ nav.css
├─ .github/workflows/docker-push.yml
├─ Dockerfile
├─ index.html
├─ index.js
├─ router.js
└─ package.json
Your images are published to:
- Docker Hub:
amnoorbrar/digital-clock - GHCR:
ghcr.io/amnoor/digital-clock
docker pull amnoorbrar/digital-clock:latest
docker run --rm --user 1000:1000 --read-only --cap-drop ALL -p 8080:80 --tmpfs /tmp:rw,noexec,nosuid,size=16m --tmpfs /var/tmp:rw,noexec,nosuid,size=16m --name digital-clock-website amnoorbrar/digital-clock:latestdocker pull ghcr.io/amnoor/digital-clock:latest
docker run --rm --user 1000:1000 --read-only --cap-drop ALL -p 8080:80 --tmpfs /tmp:rw,noexec,nosuid,size=16m --tmpfs /var/tmp:rw,noexec,nosuid,size=16m --name digital-clock-website ghcr.io/amnoorbrar/digital-clock:latestdocker run --rm --user 1000:1000 --read-only --cap-drop ALL -p 8080:80 -e PORT=80 -e DEBUG=true --tmpfs /tmp:rw,noexec,nosuid,size=16m --tmpfs /var/tmp:rw,noexec,nosuid,size=16m --name digital-clock-website amnoorbrar/digital-clock:latestdocker build -t digital-clock:local .
docker run --rm --user 1000:1000 --read-only --cap-drop ALL -p 8080:80 --tmpfs /tmp:rw,noexec,nosuid,size=16m --tmpfs /var/tmp:rw,noexec,nosuid,size=16m --name digital-clock-website amnoorbrar/digital-clock:localOpen the Website Home page at localhost:8080 and the the Stopwatch Page at localhost:8080
The GitHub Actions workflow at .github/workflows/docker-push.yml runs on pushes to tags matching v*.
For example: v1.0.0 or v1.2.3
On tag push, the workflow builds and publishes multi-architecture images (linux/amd64, linux/arm64) to Docker Hub and GHCR and generates provenance/SBOM metadata.
- Production-friendly logs are always emitted for
error,warn, andinfolevels. - Verbose logs (
debug,trace,log) are emitted whenDEBUG=true. - Browser runtime receives debug state through
/config.js.
Static file responses include:
X-Content-Type-Options: nosniffX-Frame-Options: DENYReferrer-Policy: no-referrer
See CONTRIBUTING.md for branch, commit, PR, and review requirements.
This project is licensed under the MIT License. See LICENSE for details.