Four LED panels behind a 3D-printed diffuser, driven by a Raspberry Pi. Bright white digits on a shifting field of color — or flip it and the numbers go negative.

Underneath it’s just 1,024 LEDs, but in front of them sits a 3D-printed diffuser that gives every LED its own little cell — so the whole thing reads as crisp pixel art instead of a scatter of bright dots. The default look leans right into that: bright white digits on a background color that slowly wanders through a pastel palette. The diffuser’s hard grid between pixels is exactly what makes the contrast pop — white numeral, colored field, a clean edge around every square. And when you want a different mood, one toggle inverts it: the digit LEDs switch off and read as dark cut-outs in the lit field
The idea: two ways to read the same grid The spark was a blank RGB panel glowing a flat color on the bench. A 64×16 grid of individually LEDs is really just a tiny screen — so what should the time actually look like on it? We landed on two answers and kept both. The headline look lights the digits up in bright white and lets the background carry the color: a soft pastel field that drifts, with clean white numerals on top. Behind the diffuser those white pixels sit in their own crisp cells, and the border between a lit-white digit and the colored surroundings is razor sharp — that contrast is the whole charm. The second look is the inverse: leave the field lit and switch the digit LEDs off, so the time reads as dark holes punched out of the color. That’s the negative mode — quieter, but just as striking. One toggle swaps between them.

The idea: two ways to read the same grid
The headline look lights the digits up in bright white and lets the background carry the color: a soft pastel field that drifts, with clean white numerals on top. Behind the diffuser those white pixels sit in their own crisp cells, and the border between a lit-white digit and the colored surroundings is razor sharp — that contrast is the whole charm. The second look is the inverse: leave the field lit and switch the digit LEDs off, so the time reads as dark holes punched out of the color. That’s the negative mode — quieter, but just as striking. One toggle swaps between them.The hardware
The canvas is four OzzMaker 16×16 RGB LED panels chained left-to-right into a single 64×16 display — 1,024 pixels. Driving them is a Raspberry Pi through OzzMaker’s LED connector , using Henner Zeller’s excellent rpi-rgb-led-matrix library to actually push bits to the panels at speed. Chain the panels OUT → IN, give them proper power, and the Pi sees them as one wide strip.


Drawing the digits
Everything is rendered into a plain in-memory buffer first — a 64×16 list of RGB tuples — and only pushed to the hardware at the very end. The digits themselves are an old-school seven-segment renderer. Each numeral is just a set of little rectangles; each digit says which segments it needs: # which of the 7 segments each digit lights (a=top, g=middle, …) SEGMENTS = { “0”: “abcdef”, “1”: “bc”, “2”: “abged”, “3”: “abgcd”, “4”: “fgbc”, “8”: “abcdefg”, # … } By default we draw those segments in lit white on top of the colored background, so the time The digits themselves are an old-school seven-segment renderer. Each numeral is just a set of little rectangles; each digit says which segments it needs:
# which of the 7 segments each digit lights (a=top, g=middle, ...)
SEGMENTS = {
"0": "abcdef", "1": "bc", "2": "abged",
"3": "abgcd", "4": "fgbc", "8": "abcdefg", # ...
}
An effects gallery got out of hand
This is the part where a “simple clock” quietly became a hobby. The background doesn’t have to be a flat color — it just has to be something, with the time (white or negative) laid on top. So we kept adding “somethings.” By default the field cycles through an editable pastel palette once an hour, easing between colors with a fade, a diagonal swipe, or a top-to-bottom wipe. Then came the animated backgrounds:- Plasma — the classic rolling sine-wave field, colored by your palette.
- Aurora — soft bands drifting sideways like northern lights.
- Lava lamp — a few blobs floating and merging (metaballs, if you’re fancy).
- Clouds — slow, morphing pastel noise.
- Twinkle — a calm wash with sparse stars blinking in and out.
- Ripples — two emitters of concentric waves, interfering.
- Spiral — arms winding out from the center and rotating.
- Scanner — a single comet sweeping edge to edge on black
A little web panel to tune it live
Fiddling with settings by editing files and restarting is miserable, so the same program also runs a tiny Flask web panel. Openhttp://<pi-ip>:8080 from any phone or laptop on the network and you get sliders and dropdowns for brightness, palette, effect, transitions, the lot — with a live preview of the actual frame the panel is drawing. Every change is saved to a settings.json and survives a reboot. Under the hood it’s one process with two threads that never touch each other’s stuff: a render thread that solely owns the matrix and runs at ~20 fps, and the web server. They talk only through a small set of thread-safe objects — settings in, status frames out. Keeping that boundary clean is what made it safe to keep piling on features without the display ever stuttering. Software setup
1. Install libraries
Follow this guide to get hzeller’s library installed, which will be used to drive the RGB LED panels.
2. Download and run the code
You can download the cross demo from our GitHub repository:
cd into the clock folder and run the script
pi@raspberrypi ~ $ cd clock
pi@raspberrypi ~ $ sudo python clock.py
Built with four OzzMaker 16×16 panels, a Raspberry Pi, and hzeller’s rpi-rgb-led-matrix. Digits by seven segments, color by pastels, time by NTP.











