A high-precision millisecond clock overlay system for streaming, recording, or display purposes. Synchronizes with NTP servers for accurate timekeeping.
- Millisecond precision display with smooth rendering
- NTP synchronization for accurate timekeeping
- Configurable appearance via URL parameters
- Multiple timezone support
- Degradation indicators for sync failures
- CORS support for cross-origin usage
- Monotonic time tracking between NTP syncs
- ✅ Removed unused imports
- ✅ Configurable CORS origin via
--cors-originparameter - ✅ Initial sync detection - returns 503 until first successful sync
- ✅ Error logging for debugging sync failures
- ✅ Health check endpoint at
/health - ✅ Consecutive failure tracking
- ✅ Performance optimized rendering with cached time strings
- ✅ Visual degradation indicator when sync fails
- ✅ Optional sync status indicator (green/yellow/red dot)
- ✅ Timezone validation with graceful fallback
- ✅ Memory leak prevention with cleanup on page unload
- ✅ Better initial state ("Syncing..." instead of placeholder)
# Basic usage
node ntp-overlay-server.js
# With custom options
node ntp-overlay-server.js \
--ntp pool.ntp.org \
--port 8080 \
--sync-ms 5000 \
--cors-origin "https://mystream.example.com"
# Using environment variables
NTP=time.google.com PORT=8080 node ntp-overlay-server.js| Option | Default | Description |
|---|---|---|
--ntp |
time.google.com |
NTP server to sync with |
--port |
8088 |
HTTP server port |
--sync-ms |
5000 |
NTP sync interval in milliseconds |
--timeout-ms |
1000 |
NTP query timeout |
--cors-origin |
* |
CORS origin header value |
Open overlay.html in a browser or add as a browser source in OBS:
# Basic usage (assumes server on same host)
overlay.html
# Full configuration example
overlay.html?endpoint=http://localhost:8080/now&tz=America/New_York&format=12&size=72px&alpha=0.9&pos=tr&bgalpha=0.2&indicator=true
| Parameter | Default | Description |
|---|---|---|
endpoint |
Same origin + /now |
Server endpoint URL |
tz |
local |
IANA timezone (e.g., "UTC", "America/Chicago") |
format |
24 |
Time format: "12" or "24" hour |
size |
64px |
Font size (CSS units) |
alpha |
0.75 |
Text opacity (0-1) |
bgalpha |
0.00 |
Background plate opacity (0-1) |
pos |
tr |
Position: tl, tr, bl, br, center |
font |
Monospace stack | Custom font-family CSS |
indicator |
false |
Show sync status indicator ("true" to enable) |
Returns current synchronized time:
{
"epochMs": 1704067200000,
"iso": "2024-01-01T00:00:00.000Z",
"ntpServer": "time.google.com",
"stratum": 1,
"lastSyncAgeMs": 1234,
"consecutiveFailures": 0
}Returns server health status:
{
"status": "healthy",
"initialized": true,
"lastSyncAgeMs": 1234,
"consecutiveFailures": 0
}- Start the NTP server
- Add a "Browser Source" in OBS
- Set URL to
file:///path/to/overlay.html?endpoint=http://localhost:8088/now&tz=UTC - Set Width/Height to match your canvas
- Check "Shutdown source when not visible" and "Refresh browser when scene becomes active"
When indicator=true is set:
- Green dot: Successfully synchronized
- Yellow pulsing dot: Currently synchronizing
- Red dot: Sync failed
- Faded clock: Multiple consecutive sync failures
- Node.js 14+ (for ES modules support)
- Modern browser with
Intl.DateTimeFormatsupport - Network access to NTP servers (UDP port 123)
- The default CORS setting (
*) allows any origin. Restrict this in production. - The server provides no authentication. Use a reverse proxy if needed.
- NTP queries use UDP port 123 - ensure firewall allows this.
MIT