LED matrix cross

LED matrix in cross formation and how to pixel map using AI

In this guide you’ll build a cross-shaped display using five OzzMaker 16×16 RGB LED Matrix Panels — three panels across the middle, one on top and one on the bottom — driven by a Raspberry Pi.
You’ll then run a set of old-school demo effects (plasma, rainbow, expanding rings and more) that flow seamlessly across the whole cross, as if it were one display. 

A straight strip of panels is easy — the hzeller library treats a chain as one long display out of the box. A cross is not a simple rectangle, two of the panels are mounted upside down, and the chain order doesn’t match the visual layout. Solving that is where this guide gets interesting, and it’s also where AI (Claude) did most of the heavy lifting — more on that below.

What you’ll build

A 48×48 pixel cross made from five 16×16 panels:

OzzMaker LED matrix
LED Matrix cross orientation

The above image was provided by Claude The top and bottom panels are deliberately rotated 180 degrees. This lets the FFC cables reach their neighbouring panels and keeps the cables hidden behind the display when viewed from the front. The code corrects for the rotation, so the patterns still appear the right way up.

OzzMaker RGB LED matrix cross back
Rear view showing how panels are connected

 

Bill of materials

Assemble hardware

1. Fit the connector to the Pi

Power off the Pi. Sit the OzzMaker LED connector down onto the 40-pin GPIO header. The connector breaks out the unused GPIO and includes a QWIIC socket for I²C.

2. Lay out the panels

Place the three middle panels (LEFT, CENTRE, RIGHT) face down in a row, all the same way up. Now place the TOP and BOTTOM panels face down above and below the centre panel, but rotated 180 degrees relative to the middle row. Why the rotation? Each panel’s IN and OUT FFC ports sit near opposite edges of the PCB. With the top and bottom panels flipped, their ports end up facing the centre panel, so the short FFC cables can reach — and the whole loom stays tucked behind the display.

3. Chain the panels

Each panel has an IN and OUT FFC port, and the arrow on the back of the PCB shows the direction data flows. The chain order for the cross is: Pi → TOP → LEFT → CENTRE → RIGHT → BOTTOM. Plug an FFC cable from the connector on the Pi into the IN port of the TOP panel, then OUT of TOP into IN of LEFT, and continue through CENTRE and RIGHT, finishing at BOTTOM. Note that the chain order is not the same as the visual order — the software takes care of putting the right pixels on the right panel. A common mistake is getting one of the FFC cables in backwards or 180° rotated. The contacts on the cable should always face the same side of the connector latch. If one panel shows garbage, that’s almost always the cause.

4. Power

The FFC cable carries 5 V and GND alongside the data lines, and five panels is within what the FFC can deliver for typical animations. However, at 100% brightness with lots of LEDs lit, five panels can pull around 3.5 A — far more than the Pi’s 5 V rail can supply. The demo script defaults to 50% brightness for this reason. If you see flickering or garbage on panels at the end of the chain, feed 5 V and GND directly to the 5V and GND pads on the back of one of the later panels in the chain, using the same supply that powers the Pi (or a second 5 V supply with a common ground — never two supplies without a common ground). If you’re using a second supply, cut the trace between the green-highlighted pads on the back of the panel where you have connected the second power supply. See the LED matrix overview guide for the pad locations.

5. Soldering panels together

First place some solder on the pads near the edge of the panels, then align the panels together and add more solder to each pair of pads until the solder flows across both pads, bridging them. For the cross you’ll solder along four joints: TOP-to-CENTRE, LEFT-to-CENTRE, RIGHT-to-CENTRE and BOTTOM-to-CENTRE. Take extra care aligning the top and bottom panels — remember they are rotated 180°, so double-check the LED side lines up square before the solder goes on.

OzzMaker 16x16 RGB LED panel
Example of soldering two panels together using Solder-joinable tabs

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:

pberrypi ~ $ git clone https://github.com/ozzmaker/ozzMaker-LED-matrix

cd into the pacman folder and run the script

pi@raspberrypi ~ $ cd ozzMaker-LED-matrix
pi@raspberrypi ~ $ cd cross
pi@raspberrypi ~ $ sudo python cross.py

sudo is required by the rpi-rgb-led-matrix library so it can use realtime scheduling on the GPIO. If you see flicker or ghosting, the two knobs to turn are –gpio-slowdown (a Pi 4 likes 2–4, a Pi 3 likes 1–2, a Pi Zero usually 0–1) and the hardware mapping setting — see the getting-started guide above.

OzzMake LED matrix
4 LED panels connected in a cross

How the pixel mapping works

This is the clever part of the project. As far as hzeller’s library is concerned, the five panels are just one long 80×16 strip — it has no idea they’re arranged in a cross, and no idea two of them are upside down. The solution is a logical canvas. All of the animation code draws onto a simple 48×48 buffer using normal (x, y) coordinates, exactly as if the cross were one square display. A small mapping layer then translates each logical pixel to its physical location in the chain on every frame. Each panel is described by one line in a table: where it sits in the FFC chain, where its top-left corner lands on the 48×48 canvas, and whether it’s rotated:

Don’t worry if below appears complex, we don’t have to worry about it as Claude did all the work for us.

# ---------------------------------------------------------------------------
# Cross canvas
# ---------------------------------------------------------------------------
# Each panel's top-left corner in "cross-space" coordinates.
# Centre panel sits at (0, 0).
PANEL_OFFSETS = {
    "centre": ( 0,          0),
    "top":    ( 0,         -PANEL_SIZE),
    "bottom": ( 0,          PANEL_SIZE),
    "left":   (-PANEL_SIZE, 0),
    "right":  ( PANEL_SIZE, 0),
}
CROSS_MIN_X = -PANEL_SIZE
CROSS_MAX_X =  PANEL_SIZE * 2
CROSS_MIN_Y = -PANEL_SIZE
CROSS_MAX_Y =  PANEL_SIZE * 2
CROSS_W     = CROSS_MAX_X - CROSS_MIN_X   # 48
CROSS_H     = CROSS_MAX_Y - CROSS_MIN_Y   # 48

def _make_cross_mask():
    mask = np.zeros((CROSS_H, CROSS_W), dtype=bool)
    for ox, oy in PANEL_OFFSETS.values():
        px = ox - CROSS_MIN_X
        py = oy - CROSS_MIN_Y
        mask[py:py + PANEL_SIZE, px:px + PANEL_SIZE] = True
    return mask

CROSS_MASK = _make_cross_mask()

class CrossCanvas:
    """
    A 48x48 numpy RGB buffer representing the full cross.
    Only pixels inside CROSS_MASK are physically illuminated.
    """
    def __init__(self):
        self.buf = np.zeros((CROSS_H, CROSS_W, 3), dtype=np.uint8)
    def clear(self):
        self.buf[:] = 0
    def set(self, x, y, r, g, b):
        bx = int(x) - CROSS_MIN_X
        by = int(y) - CROSS_MIN_Y
        if 0 <= bx < CROSS_W and 0 <= by < CROSS_H and CROSS_MASK[by, bx]:
            self.buf[by, bx] = (r, g, b)
    def fill(self, r, g, b):
        self.buf[CROSS_MASK] = (r, g, b)
    def fill_panel(self, panel, r, g, b):
        ox, oy = PANEL_OFFSETS[panel]
        px = ox - CROSS_MIN_X
        py = oy - CROSS_MIN_Y
        self.buf[py:py + PANEL_SIZE, px:px + PANEL_SIZE] = (r, g, b)
    def apply_mask(self):
        """Zero out pixels outside the cross shape, then apply global brightness."""
        self.buf[~CROSS_MASK] = 0
        if BRIGHTNESS < 1.0:
            self.buf[CROSS_MASK] = (
                self.buf[CROSS_MASK].astype(np.float32) * max(0.0, min(1.0, BRIGHTNESS))
            ).astype(np.uint8)

For an upright panel, a logical pixel maps straight through to the same position on the physical panel. For a rotated panel, the coordinates are mirrored in both axes — (x, y) becomes (15 – x, 15 – y) within the panel — which is exactly what a 180° rotation looks like in code. The physical x position is then the panel’s chain index × 16 plus the local x.

The corners of the 48×48 canvas don’t exist physically (that’s what makes it a cross), so the mapping layer also keeps a mask of which logical pixels fall on a real LED, and simply skips the rest. The animations don’t need to care — they can draw to the full square and only the cross lights up.

The payoff is that every effect in the script — plasma, rainbow, expanding rings, the lot — is written in plain, readable code against a square canvas, with not a single mention of chains, panels or rotations.

 

Using AI (Claude) to create the patterns

This project was built with the help of Claude. I didn’t write the mapping layer or the animations by hand — I described the physical setup and let Claude work out the geometry.

This was the actual prompt:

I am using the RGB LED matrix in the attached URL. I have 5 setup in a cross formation. The panels would be setup in this order. Top is first, left is second, centre is third, right is fourth, bottom is fifth, and the top panel and bottom panel are upside down. Create some old school animation effects to show off the panels, like rainbow, plasma, etc.. Add a setting for brightness. Power for these panels will come from the FFC cable connected to the raspberry Pi. Show an image of how the panels should be connected.

A few things worth noting about how that went:

  • Claude read the product page first. By including the URL, Claude picked up the panel specs and even checked the FFC power limits against the page before writing any code.
  • It flagged the power problem unprompted. Claude pointed out that five panels at full white can draw around 4 A through the FFC — more than the Pi can supply — and made the brightness default conservative (50%) on its own.
  • The chain-order and upside-down mapping came out right first go. The sentence “the top panel and bottom panel are upside down” was enough for Claude to build the coordinate remapping table above, including the 180° flip and the cross mask.
  • It drew the wiring diagram. The connection diagram showing the Pi and the five panels in chain order was generated by Claude as part of the same conversation.

 

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.