A simple, low-friction session timer with a separate display page (projector / big screen) and controller page (phone / laptop) that stay in sync. Designed for talks, presentations, debates, breakout sessions — anywhere you need a visible countdown clock that someone in the room can quietly adjust without touching the projector.
- Large-format countdown with a separate wall clock underneath
- Phone-friendly controller with quick presets (1, 2, 3, 5, 10, 15, 20, 30, 45, 60 min) and a custom-duration field
- Color fade as time runs out — green → yellow → orange → red, configurable threshold
- Add / remove time on the fly — ±30 s and ±1 m, works while running, paused, or stopped
- Audible alert when the countdown hits zero
- Keyboard shortcuts on the display (
Space,R,↑/↓,F) - Cross-device control over your local Wi-Fi via WebSocket
- Same-browser fallback via the BroadcastChannel API — works even with no network
- Node.js 18 or newer (download)
- macOS or Linux for the launcher script (
timer.sh). Windows users can runnode server.jsdirectly. - A modern browser (Chrome, Firefox, Safari, Edge)
git clone https://github.com/holzerjm/CountDownController.git
cd CountDownController
./timer.sh setupsetup checks for Node.js and installs the single dependency (ws).
| Command | What it does |
|---|---|
./timer.sh start |
Start the server in the foreground (Ctrl+C to stop) |
./timer.sh background |
Start the server in the background, log to timer-server.log |
./timer.sh stop |
Stop a background server |
./timer.sh status |
Check if the server is running |
./timer.sh restart |
Stop, then start |
./timer.sh open |
Open both pages in your default browser |
./timer.sh help |
Show usage |
Default port is 8080. Override with an environment variable:
PORT=3000 ./timer.sh startWhen the server is running it prints both URLs to the terminal — open them as needed.
Once the server is up:
-
Display page —
http://localhost:8080/displayOpen this on the projector, big screen, or wherever the audience can see it. PressF(or click "Click for fullscreen") for a clean projector view. -
Controller page —
http://localhost:8080/controllerOpen this on your phone or laptop. Use it to start, pause, adjust, and reconfigure the timer.
./timer.sh start (or status) prints your computer's LAN IP. On your phone, open:
http://<your-computer-ip>:8080/controller
The display can stay on the projector at localhost. Make sure both devices are on the same network and your firewall isn't blocking port 8080.
| Section | What it does |
|---|---|
| Controls | Start, Pause, Stop, Reset |
| Quick Presets | One-tap durations (1–60 min) |
| TOA Launch | TOA-specific preset durations (8, 25, 10, 5 min) |
| Custom Duration | Pick any minutes + seconds (minimum 10 s) |
| Adjust Time | ±30 s and ±1 m. Works while running, paused, or stopped. |
| Color Fade Start | Slider for when the green-to-red fade begins (50–95 % elapsed) |
| Key | Action |
|---|---|
Space |
Start / Pause toggle |
R |
Reset to full duration |
↑ |
+1 minute |
↓ |
−1 minute |
F |
Toggle fullscreen |
The + and - buttons are intentionally asymmetric, matching how you'd actually use them in a session:
+30s/+1mwhile running or paused — extend the budget. Both the countdown and the "of X:XX" total grow by the delta; elapsed time is preserved. Use this when the speaker is going well and you want to give them more room.-30s/-1mwhile running or paused — skip the countdown forward. Only the remaining time shrinks; the total stays the same. This makes the color fade react immediately to the new urgency — if subtracting pushes the countdown past 75 % elapsed (or whatever you set), the colors start fading right away. If-would push remaining at or below zero, the timer stops at 00:00.- Before the timer has started (READY state) — adjust changes the configured total duration directly.
| Symptom | Fix |
|---|---|
| Controller can't reach display from another device | Same Wi-Fi? Firewall blocking port 8080? Using the LAN IP, not localhost? |
| Server won't start | Run ./timer.sh setup to install ws. Check timer-server.log for errors. |
| Port already in use | PORT=3000 ./timer.sh start |
| Background server "stuck" | ./timer.sh stop clears the stale PID file. If that fails, pkill -f "node server.js". |
| Pause from the controller doesn't stop the timer | Fixed in current code. If you see this, you're on an old version — git pull. |
.
├── server.js # HTTP + WebSocket relay
├── timer-display.html # Display page (timer logic)
├── timer-controller.html # Controller page (command emitter)
├── timer.sh # Launcher (setup / start / stop / status / ...)
├── package.json # Node deps (just `ws`)
├── README.md # This file
├── ARCHITECTURE.md # Design + data flow
└── LICENSE # MIT
For the technical design — how the pages talk to each other, the timer state machine, and why both BroadcastChannel and WebSocket are used — see ARCHITECTURE.md.
MIT — see LICENSE.