A clock for 16×16 (and other sizes) WS2812B LED matrices with four clock faces and a live web UI. Comes in two flavours:
| Node.js + WLED | ESP8266 Standalone | |
|---|---|---|
| Runs on | Any PC / server | ESP8266 directly |
| Drives LEDs | Via WLED (UDP or HTTP) | FastLED, no WLED needed |
| Gets time | System clock | NTP over WiFi |
| Web UI | http://localhost:3000 |
http://<esp-ip> |
| Face | Description |
|---|---|
| Digital | 3×5 pixel 7-segment style HH:MM with blinking colon and a seconds progress bar along the bottom row |
| Analog | Hour, minute and second hands drawn with Bresenham lines; hour markers at 12/3/6/9 positions |
| Binary | BCD binary clock — six columns (H tens/units · M tens/units · S tens/units), each bit shown as a 2×2 LED block |
| Rainbow | Same layout as Digital but each lit pixel cycles through the HSV colour wheel |
- Live matrix preview rendered on an HTML canvas with per-LED glow effect
- Click any face tab to preview it instantly on the matrix (without saving)
- Colour pickers for foreground, accent and second-hand colours
- Brightness slider (0–255)
- 12 h / 24 h toggle, show/hide seconds, blinking colon, serpentine wiring
- Configurable matrix width and height (up to 512 LEDs)
- Settings persisted to
config.jsonand survive restarts
Run the controller on any machine (Raspberry Pi, laptop, server). WLED must already be flashed on your ESP and connected to the same network.
- Node.js 18+
- A WLED device on your local network
- WLED "Realtime" UDP enabled: WLED → Config → Sync → Realtime (for UDP mode)
git clone https://github.com/your-username/led-matrix-clock
cd led-matrix-clock
npm install
npm startOpen http://localhost:3000, enter your WLED IP address, choose a clock face, and click Start.
├── server.js # Express + WebSocket server, clock engine
├── public/
│ ├── index.html # Web UI
│ ├── style.css # Dark theme
│ └── app.js # Frontend logic
└── config.json # Auto-generated on first save
All settings are saved automatically when you click Save & Apply. The relevant fields in config.json:
| Field | Default | Description |
|---|---|---|
wledIp |
192.168.1.100 |
IP address of your WLED device |
protocol |
udp |
udp (realtime) or http (JSON API) |
wledPort |
21324 |
UDP port (WLED default) |
face |
digital |
digital / analog / binary / rainbow |
fgColor |
#FFC800 |
Foreground colour (hex) |
accentColor |
#FF2200 |
Accent / colon / seconds bar colour |
secondHandColor |
#FF6600 |
Second hand colour (analog face) |
brightness |
180 |
0–255, scaled before sending to WLED |
show12h |
false |
12-hour format |
showSeconds |
true |
Show seconds bar / hand |
blinkColon |
true |
Blink colon every second |
serpentine |
true |
Zigzag (serpentine) LED wiring |
matrixWidth |
16 |
Number of LED columns |
matrixHeight |
16 |
Number of LED rows |
UDP (recommended) — uses WLED's DRGB realtime protocol on port 21324. Lower latency, no HTTP overhead. LEDs revert to normal ~2 seconds after the clock is stopped.
HTTP — uses WLED's JSON API (POST /json/state). Works without enabling realtime mode but is slightly slower.
The ESP8266 drives the LEDs itself and serves the web UI from its own flash memory — no PC or WLED required after flashing.
- ESP8266 board (NodeMCU v2 / Wemos D1 Mini or equivalent)
- WS2812B LED matrix (16×16 = 256 LEDs recommended, up to 512 supported)
- 5 V power supply (256 LEDs at full white ≈ 15 A max; a 5 V / 4 A PSU is comfortable for typical use at reasonable brightness)
- 300–500 Ω resistor on the data line (recommended)
- 1000 µF capacitor across the 5 V supply (recommended)
Wiring
ESP8266 GPIO 2 (D4) ──[330Ω]── LED strip DIN
ESP8266 GND ────────── LED strip GND (and power supply GND)
5 V power supply ────────── LED strip 5V
The data pin is configurable from the web UI. Common options: GPIO 2 (D4), GPIO 4 (D2), GPIO 5 (D1), GPIO 12 (D6).
Install all four via Arduino IDE → Tools → Manage Libraries:
| Library | Tested version |
|---|---|
| FastLED | 3.6+ |
| ESPAsyncTCP | 1.2+ |
| ESPAsyncWebServer | 1.2+ |
| ArduinoJson | 6.x or 7.x |
- Board: NodeMCU 1.0 (ESP-12E Module) or Lolin(Wemos) D1 R2 & mini
- Flash size: 4MB (FS: 1MB LittleFS)
- CPU frequency: 80 MHz (160 MHz also works)
- Upload speed: 921600
- Open
esp8266_clock/esp8266_clock.inoin Arduino IDE - Select the correct board and port
- Click Upload
That's it — no separate data upload step. The web UI is embedded in the firmware.
On first boot (or whenever no WiFi credentials are saved) the ESP8266 creates an open access point called WLEDClock.
- Connect your phone or laptop to WLEDClock
- Open http://192.168.4.1 in a browser
- Enter your home WiFi SSID and password in the WiFi card
- Click Save WiFi & Restart
- The ESP connects to your network and the web UI is now available at its local IP (shown in the status bar)
After setup, both the home-network IP and the AP fallback (192.168.4.1) remain accessible.
esp8266_clock/
├── esp8266_clock.ino # Main sketch: setup, loop, web server, WiFi, NTP
├── config.h # Config struct, defaults, LittleFS load/save
├── faces.h # All four clock face renderers + LED helpers
└── webpage.h # HTML, CSS and JS embedded as PROGMEM strings
All settings are saved to /config.json in LittleFS.
| Field | Default | Description |
|---|---|---|
ssid / password |
— | Home WiFi credentials |
ntpServer |
pool.ntp.org |
NTP server hostname |
tzOffset |
0 |
UTC offset in minutes (e.g. IST = 330, EST = -300) |
face |
digital |
Clock face |
fgColor |
0xFFC800 |
Foreground colour (stored as 24-bit integer) |
accentColor |
0xFF2200 |
Accent colour |
secondColor |
0xFF6600 |
Second hand colour |
brightness |
180 |
0–255 |
show12h |
false |
12-hour format |
showSeconds |
true |
Show seconds bar / hand |
blinkColon |
true |
Blink colon |
serpentine |
true |
Zigzag LED wiring |
matrixW / matrixH |
16 / 16 |
Matrix dimensions |
ledPin |
2 |
GPIO data pin |
colorOrder |
0 (GRB) |
0=GRB, 1=RGB, 2=BGR |
Changing
ledPin,matrixW/matrixH, orcolorOrderrequires a restart — the web UI will prompt you.
→ → → → → → → → → → → → → → → →
← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ←
→ → → → → → → → → → → → → → → →
← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ←
Enable Serpentine wiring toggle.
→ → → → → → → → → → → → → → → →
→ → → → → → → → → → → → → → → →
→ → → → → → → → → → → → → → → →
Disable the Serpentine wiring toggle.
| Symptom | Fix |
|---|---|
| Display looks correct in preview but garbled on the matrix | Toggle Serpentine wiring to match your physical wiring |
| Some LEDs the wrong colour | Change Color Order to match your strip (WS2812B is usually GRB) |
| Time is wrong by a fixed offset | Set UTC Offset in minutes (e.g. 330 for India, -300 for US Eastern) |
| NTP not syncing | Check WiFi is connected; try a different NTP server (time.google.com) |
| WLED not receiving (Node.js app) | Enable Realtime UDP in WLED sync settings; verify the IP address |
| Web UI unreachable after restart | Connect to the WLEDClock AP and check the new IP in the status bar |
MIT
