Build a Word Clock: Laser-Cut Face, WS2812B LEDs, and ESP32
A word clock spells out the time in actual words, lit letter by letter behind a laser-cut face — "IT IS HALF PAST THREE" instead of numbers. It's one of the most satisfying maker-cred builds there is: genuinely striking on a wall, and the whole project is really just an LED matrix, a microcontroller, and a well-designed faceplate.
How It Actually Works
Behind the face is a grid of individually-addressable LEDs (WS2812B/NeoPixel strips or a pre-built matrix), one LED positioned behind each letter of a grid of words. The microcontroller lights up only the LEDs behind the specific letters needed to spell the current time in a sentence, dimming everything else. The "face" is a laser-cut panel with the letter grid engraved or cut through, backed by a diffuser layer so each LED lights its letter as a soft glow rather than a single harsh point.
Designing the Letter Grid
This is the actual design work — laying out a grid of letters that can spell every 5-minute time interval using a shared, overlapping set of words. A standard English word clock layout looks roughly like:
IT IS A QUARTER TWENTY HALF TEN FIVE PAST TO ONE TWO THREE FOUR FIVE SIX SEVEN EIGHT NINE TEN ELEVEN TWELVE O'CLOCKReading "IT IS [visible words] O'CLOCK" for each time — e.g. "IT IS QUARTER PAST THREE" or "IT IS TWENTY TO NINE" — covers every 5-minute increment across 12 hours with a genuinely small set of shared words. Plan your exact grid on paper (or in a spreadsheet mapping each time interval to which words light up) before committing to a physical layout.
Hardware
ComponentNotes ESP32 dev boardPlenty of processing power for this, and WiFi built in for NTP time sync WS2812B LED strip (individually addressable)One LED per letter in your grid — a typical layout needs 100-120 LEDs; buy a strip and cut/re-wire into rows matching your letter positions DS3231 RTC moduleKeeps accurate time between WiFi/NTP syncs — same reasoning as our wall clock + weather display build, a clock that loses track of time when WiFi drops is a real functional failure 5V power supply, sized to your LED countWS2812B LEDs can draw significant current at full brightness — budget roughly 60mA per LED at full white, though a word clock rarely lights more than 15-20 LEDs simultaneously so real draw is much lower in practiceBuilding the Face
- Front layer (laser cut): the visible panel, with letters either cut all the way through (light shines through as a crisp letter shape) or engraved partway (softer glow, less sharp edges) — cut-through with a diffuser behind it generally reads more cleanly at a distance
- Diffuser layer: a thin sheet of frosted acrylic or even a layer of tracing paper, sandwiched behind the front layer — spreads each LED's point-source light into an even glow across its letter
- Light-separator grid: a 3D printed or laser-cut internal grid of small walls between each LED position, preventing light from one letter bleeding into its neighbor
- Back panel: holds the LED strip in position, aligned precisely behind each letter opening — this alignment step is the most fiddly part of the whole build and is worth test-fitting before final assembly
Firmware
#include <WiFi.h> #include <Wire.h> #include <RTClib.h> #include <Adafruit_NeoPixel.h> #define LED_PIN 5 #define NUM_LEDS 114 Adafruit_NeoPixel strip(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800); RTC_DS3231 rtc; // Map each word to its LED index range — build this array to match // your OWN physical LED wiring order, not a generic template struct WordRange { int start; int count; }; WordRange IT = {0, 2}; WordRange IS = {2, 2}; WordRange QUARTER = {4, 7}; WordRange TWENTY = {11, 6}; WordRange HALF = {17, 4}; // ... continue for every word in your grid void lightWords(std::vector<WordRange> words) { strip.clear(); for (auto &w : words) { for (int i = w.start; i < w.start + w.count; i++) { strip.setPixelColor(i, strip.Color(255, 200, 100)); // warm white } } strip.show(); } void updateClockFace() { DateTime now = rtc.now(); int hour = now.hour() % 12; if (hour == 0) hour = 12; int minute = now.minute(); int roundedMinute = (minute / 5) * 5; std::vector<WordRange> active = {IT, IS}; // Add logic here mapping roundedMinute and hour to the correct // word combination for your specific grid layout lightWords(active); } void setup() { strip.begin(); Wire.begin(); rtc.begin(); WiFi.begin("your-ssid", "your-password"); while (WiFi.status() != WL_CONNECTED) delay(500); configTime(-5*3600, 0, "pool.ntp.org"); struct tm timeinfo; if (getLocalTime(&timeinfo)) { rtc.adjust(DateTime(timeinfo.tm_year+1900, timeinfo.tm_mon+1, timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec)); } } void loop() { updateClockFace(); delay(10000); // recheck every 10 seconds — plenty for 5-minute resolution }The actual time-to-words mapping logic is the one piece that's genuinely specific to your exact grid layout — there's no way around manually building that lookup table once, matched to your own word positions.
Nice-to-Haves
- Ambient light sensor + auto-dimming — same reasoning as any always-on wall display, avoid it being glaringly bright in a dark room
- Color themes — since the LEDs are full RGB, nothing stops you from a color-cycling mode or a fixed accent color matching your room
- Minute-indicator dots — a small row of 4 corner LEDs indicating the exact minute within the current 5-minute block, for people who want more precision than "ten past"
The electronics here are genuinely simple — an LED strip and a microcontroller is a beginner-friendly circuit. The real craft is entirely in the face design and the light diffusion, which is exactly the kind of project a laser cutter turns from "theoretically possible" into "an afternoon's work."
Related Guides
- How to Program Addressable LED Strips: WS2812B Patterns, Effects, and Power Design
- Build a Wall-Mounted Smart Clock and Weather Display (ESP32 + RTC)
- Designing Enclosures for Electronics Projects: Materials, IP Ratings, Ventilation, and Cable Entry
- Real-Time Clock Modules for Arduino and ESP32: DS3231 vs DS1307, Battery Backup, and NTP Sync
- Build a Standalone ESP32 WiFi Security Testing Tool with Marauder
- Build a Battery-Powered ESP32 Smart Mailbox Notifier
- Build a Wall-Mounted Dashboard with the ESP32 Cheap Yellow Display (CYD)
- Build a Cyberdeck: Custom Portable Computer with a Laser-Cut and 3D Printed Enclosure