Real-time NDI to Colorlight LED wall — Linux / Raspberry Pi CLI.
Receive an NDI® video stream over the network and drive a Colorlight E-series receiver card directly via raw Ethernet (AF_PACKET).
Status: Working alpha, version 0.1. Tested on Raspberry Pi with Colorlight E120 and a 6×4 wall of 64×32 pixel panels (384×128 px output).
Heads up — this is vibecoded. Most of this code was pair-written with an LLM during quick iteration sessions, not carefully engineered. It works for the author's setup but is not hardened, audited, or production-tested. Constants are reverse-engineered from packet captures, error handling is "good enough", and edge cases are mostly unexplored. Treat it as a starting point, not a finished product. Bug reports and PRs are very welcome.
- NDI 6 receiver — auto-discovery, source selection by name (substring match)
- Colorlight protocol — raw Layer-2 Ethernet, max 497 px/packet, BGR pixel order, sync + brightness control packets
- Scaling and gamma — output is sized to the configured panel grid; per-channel gamma applied before transmission
- Brightness sent as 0x0A control packets, no client-side scaling
- systemd installer — interactive setup that auto-detects interfaces and discovers NDI sources
--listmode for quick NDI source discovery from the terminal
┌─────────────┐ ┌──────────────┐ ┌────────────────┐ ┌───────────┐
│ NDI source │ ───▶ │ ndi_receiver │ ───▶ │ frame_convert │ ───▶ │ colorlight│ ───▶ Wall
│ (network) │ BGRX │ (libndi) │ BGRX │ scale + BGR │ BGR │ AF_PACKET │
└─────────────┘ └──────────────┘ └────────────────┘ └───────────┘
| Source file | Role |
|---|---|
main.c |
CLI parsing, NDI discovery loop, frame pump, signal handling |
ndi_receiver.{c,h} |
NDI SDK integration: discovery, connect, frame callbacks |
frame_convert.{c,h} |
RGB→BGR, scaling, gamma correction |
colorlight_output.{c,h} |
Layer-2 packet builder + AF_PACKET socket |
config.{c,h} |
wall.conf key/value parser |
- Colorlight E-series receiver card. E120 verified — other E-series models using the same Layer-2 protocol should work; please open an issue with results.
- LED panels driven by the receiver card. Panel pixel size and grid layout are configurable.
- Dedicated wired Ethernet path between host and receiver. The Colorlight protocol floods raw frames at line rate; do not share the link with normal LAN traffic. A USB Ethernet adapter on a separate interface is the typical Raspberry Pi setup.
- Linux with
AF_PACKETraw sockets (any modern kernel; tested on Raspberry Pi OS, Debian, Ubuntu) gcc,make- NDI SDK 6 for Linux — headers at
/usr/local/include,libndi.so.6reachable by the dynamic linker. AdjustNDI_INCLUDEin theMakefileif installed elsewhere. SDK download: ndi.video/sdk.
git clone https://github.com/quadjojo/ndi2colorlight.git
cd ndi2colorlight
makeProduces ./ndi-led-cli. make clean removes object files and the binary.
Copy the template and edit it:
cp wall.conf.example wall.conf| Key | Meaning | Default |
|---|---|---|
panel_width |
pixels per panel, horizontal | 64 |
panel_height |
pixels per panel, vertical | 32 |
panels_x |
panel count, horizontal | 6 |
panels_y |
panel count, vertical | 4 |
brightness |
0–100 | 100 |
gamma |
1.0 = linear, 2.2 = typical display | 1.0 |
Output resolution is panels_x × panel_width × panels_y × panel_height.
sudo ./ndi-led-cli --source "MY-NDI-SOURCE" \
--interface eth1 \
--config wall.confsudo is required because AF_PACKET raw sockets need CAP_NET_RAW. To run without sudo, set the capability on the binary:
sudo setcap cap_net_raw+ep ./ndi-led-cli| Flag | Long form | Purpose |
|---|---|---|
-s NAME |
--source NAME |
NDI source name (substring match) |
-i IF |
--interface IF |
Output interface (e.g. eth1) |
-c FILE |
--config FILE |
Config file path (default ./wall.conf) |
-l |
--list |
Discover NDI sources for 5 s and exit |
-b N |
--brightness N |
Override brightness from config |
-h |
--help |
Show usage |
--list is the easiest way to find the exact source name before wiring it into a service.
sudo ./install.shThe installer:
- Lists available network interfaces and prompts for the LED output one (skip the prompt by passing it as
$1). - Discovers NDI sources for 5 s and lets you pick one (skip with
$2). - Copies
ndi-led-clito/usr/local/bin/. - Copies
wall.conf.exampleto/etc/ndi-led-wall/wall.conf(only if not already present — safe to re-run). - Writes a
ndi-led-wall.serviceunit and enables it.
Non-interactive install:
sudo ./install.sh eth1 "MY-NDI-SOURCE"After install:
systemctl status ndi-led-wall # state
journalctl -u ndi-led-wall -f # live logs
systemctl restart ndi-led-wall # reload after config edits
systemctl stop ndi-led-wall # stop outputsudo systemctl disable --now ndi-led-wall
sudo rm /etc/systemd/system/ndi-led-wall.service
sudo rm /usr/local/bin/ndi-led-cli
sudo rm -rf /etc/ndi-led-wall
sudo systemctl daemon-reload- Layer 2, no IP needed. The Colorlight protocol uses raw Ethernet frames addressed to MAC
11:22:33:44:55:66. No DHCP, no static IP, no switch configuration on a direct link. - Direct cable Pi → receiver card is the simplest setup. Switches work but must not filter unknown unicast or rate-limit broadcast.
- Dedicated NIC. A 384×128 wall at 60 fps already pushes ~50 Mbit/s of pure pixel data — keep regular LAN traffic off this link.
- MTU stays at 1500. The protocol caps payload at 497 px × 3 B + headers ≈ 1500 B per packet by design.
Raw Ethernet frames, no EtherType. Header layout:
| Bytes | Field |
|---|---|
| 0–5 | Destination MAC (11:22:33:44:55:66) |
| 6–11 | Source MAC (host NIC) |
| 12 | Packet type (0x55 pixel, 0x01 sync, 0x0A brightness) |
| 13+ | Payload |
A frame is one or more 0x55 pixel packets followed by a 0x01 sync packet (with a small inter-row delay; ~5 ms after the last row in our experience). Brightness changes go out as 0x0A packets and are persisted by the receiver card until power-cycled.
This is reverse-engineered, not vendor-documented. Treat constants as best-effort.
- Only Colorlight E-series has been verified — Z-series uses a different protocol and is not supported.
- No HDR / 10-bit color path — internal pipeline is 8-bit BGR.
- No audio (NDI audio frames are dropped).
- No remote control / OSC / MIDI yet.
Issues and PRs welcome. If you test against a Colorlight model not listed here, please open an issue with: receiver model, panel scan rate, panel pixel dimensions, and whether discovery + pixel output worked.
MIT — © 2026 Tobias Wessely.
- NDI® is a registered trademark of Vizrt Group. This project is not affiliated with or endorsed by Vizrt. The NDI SDK is distributed by Vizrt under its own license — install it separately.
- Colorlight receiver cards are products of Colorlight Cloud Tech Ltd. This project is not affiliated with or endorsed by Colorlight.