Why this exists
Two questions keep arriving in different disguises: "my new high-refresh panel is black" (#259) and "can I run a big wall with this?" They have the same root, and it is not the one people expect. This issue writes down what one Patternflow board can and cannot drive, why, and what the routes to a larger display actually look like.
Short version: nothing about reactive patterns is limited. What is limited is how many pixels one ESP32-S3 can push, and which panel protocol it speaks. Those are two separate walls and they need different answers.
1. The two walls
Wall A — protocol: S-PWM panels cannot be driven directly
Classic panels (74HC595, FM6124, ICN2037, DP5125D…) are dumb shift registers. They have no brightness control of their own, so the controller manufactures PWM by re-sending bit-planes hundreds of times per second. That is what Patternflow does today.
Newer S-PWM / GCLK chips (ICN2053, FM6353, FM6363C, MBI5153, DP3264…) moved that work into the chip: on-chip frame memory plus a PWM engine. The controller writes a frame once and the chip runs its own 3840 Hz output. To talk to them you need a register init sequence, commands encoded as LAT pulse widths, and a continuously supplied grey-scale clock.
Same 16-pin HUB75E connector, same signal names, incompatible conversation. This is why the panel is completely black rather than dim or ugly — it is not a performance shortfall, it is a language mismatch.
Three things worth knowing:
- It is not an ESP32 weakness.
rpi-rgb-led-matrix has no official S-PWM support either, and a Raspberry Pi is far faster. A continuous GCLK is a timing problem, not a throughput problem.
- It is not impossible either. LAutour's fork drives ICN2053/FM6353 on a plain ESP32 with no added hardware. It is just slow (≤13 MHz, 16-bit colour, ~30 Hz at 8192 px) and unmaintained since 2023.
- Upstream declined on purpose. Discussion #324 — supporting memory-based drivers is "a fundamental change to what this library was designed for"; maintainer recommends a separate codebase. #642 closed
wontfix.
Already own one? docs/panel-compatibility.md §11 lists the options.
Wall B — throughput: one board tops out around one 128×64 panel
Because Patternflow manufactures the PWM, work scales directly with pixels per row. Chaining panels divides the available time.
Measured/derived at the shipped settings (16 MHz, 8-bit, min_refresh_rate 240, 1/32 scan):
| Chain |
Resolution |
Refresh |
Properly-weighted colour |
| 1 |
128×64 |
260 Hz |
4 bit |
| 2 |
256×64 |
244 Hz |
1 bit |
| 4 |
512×64 |
122 Hz |
1 bit |
| 8 |
1024×64 |
61 Hz |
1 bit |
Colour collapses at the second panel: the driver raises lsbMsbTransitionBit to hold 240 Hz and the low bits lose their weighting. It is a tunable trade, not a cliff —
min_refresh_rate |
chain 1 |
chain 2 |
chain 4 |
| 240 Hz |
260 Hz / 4 bit |
244 Hz / 1 bit |
122 Hz / 1 bit |
| 120 Hz |
169 Hz / 5 bit |
130 Hz / 4 bit |
122 Hz / 1 bit |
| 60 Hz |
100 Hz / 6 bit |
84 Hz / 5 bit |
65 Hz / 4 bit |
— but every extra panel is paid for in refresh or in colour. The 240 Hz default exists so phone cameras don't band on video; a wall build would relax it.
Two hard limits sit in front of that table.
Memory. SPIRAM_DMA_BUFFER is not defined, so the DMA framebuffer lives in internal RAM — of which a running v3.0 board reports ~11 KB free (/api/status → heapInternal:11420), while 8 MB of PSRAM sits idle. Chain 2 roughly doubles the buffer. Enabling PSRAM for the buffer should move this wall, at the cost of a lower clock ceiling. Untested.
Power. A 128×64 P2.5 panel draws about 2.5 A at 5 V at full brightness (derived from the measured runtime table in the README: 10,000 mAh ≈ 4 h). A typical USB power-bank port tops out at 2.4 A. One panel already sits at the ceiling; two do not start.
Confirmed on hardware: a chain-2 attempt on a power bank never booted — power LED blinking, immediate cut-out, repeating. That is the bank's over-current protection tripping on panel inrush, not a firmware fault. A memory failure looks different: Matrix begin FAILED and a silent hang with the rail still up (core_display.h). We never got far enough to find out whether chain 2 works in firmware.
⚠️ If you do feed two panels: don't run 5 A through J3. Panel current currently flows J4 → board traces → J3 → panel, which is sized for one panel. Split the supply at the PSU — panels direct, board on logic power only.
And note what a wall-powered build costs: Patternflow stops being the thing you carry around on a power bank.
2. What is not limited
Worth stating plainly, because the two walls above get mistaken for a limit on the concept itself.
A Patternflow pattern is a function — (x, y, time, audio, knobs) → colour. That is hardware-independent. The same patterns already run in two unrelated runtimes today: Pattern Lab's browser preview (JS) and the device (C++). Audio reactivity, encoder input and OSC are all upstream of the panel driver.
So: reactive patterns on a large display are not blocked. Only the last step — pushing finished pixels onto specific hardware — is hardware-bound.
3. Routes to a bigger display
A. Tiling — several Patternflow nodes, one surface
Each node drives its own section; a shared clock and per-node coordinate offset make them one canvas. Standard practice for large LED art installations, and the most Patternflow-native answer.
Ingredients that already exist: bidirectional OSC, Wi-Fi, PANEL_RES_W/H/CHAIN as build config, @matrix frames.
Missing, and this is real feature work, not configuration:
- shared time base (frame sync across nodes; Wi-Fi jitter is the hard part)
- per-node canvas offset — a node must render its window of a larger virtual canvas
- one-to-many parameter distribution (a knob turn on one unit reaching all of them)
- provisioning/addressing so nodes know their position in the grid
Nearest existing groundwork: #224.
B. Render elsewhere, feed the wall
Run the pattern function on a PC or Pi, ship frames to whatever already drives the wall (commonly a Colorlight/Novastar receiving card over Ethernet).
The key property: you write the output backend once, not once per pattern. Every existing pattern, and every future one, works the moment the renderer exists. Audio reactivity gets easier here, not harder — the host has a real audio stack.
This is also the only route that reaches genuinely large commercial surfaces, which already have receiving cards in them. Those installations do not want an MCU in the chain.
C. Direct S-PWM support in firmware
Would remove Wall A and substantially soften Wall B — a chip that runs its own PWM means writing a frame once instead of 260 times a second, so one ESP32 covers far more area. Tempting for that reason.
Cost is high: per-family command protocols reconstructed from partly reverse-engineered documentation, two synchronised output streams (frame data + uninterrupted GCLK), a driver abstraction inside Patternflow, and split firmware images since a panel cannot be detected at runtime. Plus at least one physical panel per chip family to develop against.
Realistically an open R&D project with an indefinite maintenance tail — which is exactly why upstream declined it.
D. Add an FPGA/CPLD to the board
Rejected. That is what a receiving card is, and it would delete every property of the current board (all through-hole, 5 unique parts, ~$35, beginner-solderable) to produce a worse Colorlight 5A-75B — a finished board that costs about $20.
4. Where this leaves things
| Question |
Answer |
| Reactive patterns |
✅ Not limited |
| A different classic panel |
✅ Three lines in config.h — #224 |
| Chain of 2 on one board |
❓ Untested — blocked on power before firmware was reached |
| S-PWM panel, direct |
❌ Not supported, see §1 |
| Patternflow patterns on a large wall |
✅ Route A or B |
| One Patternflow board driving a large wall |
❌ Not a goal, and not reachable |
No work is being scheduled from this issue. It exists so the question stops being re-litigated from scratch, and so anyone who wants to attempt route A or B has the numbers in one place.
Corrections and contradicting measurements are welcome — particularly a successful chain-2 with a proper 5 V supply, which would settle the memory question left open in §1.
Why this exists
Two questions keep arriving in different disguises: "my new high-refresh panel is black" (#259) and "can I run a big wall with this?" They have the same root, and it is not the one people expect. This issue writes down what one Patternflow board can and cannot drive, why, and what the routes to a larger display actually look like.
Short version: nothing about reactive patterns is limited. What is limited is how many pixels one ESP32-S3 can push, and which panel protocol it speaks. Those are two separate walls and they need different answers.
1. The two walls
Wall A — protocol: S-PWM panels cannot be driven directly
Classic panels (74HC595, FM6124, ICN2037, DP5125D…) are dumb shift registers. They have no brightness control of their own, so the controller manufactures PWM by re-sending bit-planes hundreds of times per second. That is what Patternflow does today.
Newer S-PWM / GCLK chips (ICN2053, FM6353, FM6363C, MBI5153, DP3264…) moved that work into the chip: on-chip frame memory plus a PWM engine. The controller writes a frame once and the chip runs its own 3840 Hz output. To talk to them you need a register init sequence, commands encoded as LAT pulse widths, and a continuously supplied grey-scale clock.
Same 16-pin HUB75E connector, same signal names, incompatible conversation. This is why the panel is completely black rather than dim or ugly — it is not a performance shortfall, it is a language mismatch.
Three things worth knowing:
rpi-rgb-led-matrixhas no official S-PWM support either, and a Raspberry Pi is far faster. A continuous GCLK is a timing problem, not a throughput problem.wontfix.Already own one?
docs/panel-compatibility.md§11 lists the options.Wall B — throughput: one board tops out around one 128×64 panel
Because Patternflow manufactures the PWM, work scales directly with pixels per row. Chaining panels divides the available time.
Measured/derived at the shipped settings (16 MHz, 8-bit,
min_refresh_rate240, 1/32 scan):Colour collapses at the second panel: the driver raises
lsbMsbTransitionBitto hold 240 Hz and the low bits lose their weighting. It is a tunable trade, not a cliff —min_refresh_rate— but every extra panel is paid for in refresh or in colour. The 240 Hz default exists so phone cameras don't band on video; a wall build would relax it.
Two hard limits sit in front of that table.
Memory.
SPIRAM_DMA_BUFFERis not defined, so the DMA framebuffer lives in internal RAM — of which a running v3.0 board reports ~11 KB free (/api/status→heapInternal:11420), while 8 MB of PSRAM sits idle. Chain 2 roughly doubles the buffer. Enabling PSRAM for the buffer should move this wall, at the cost of a lower clock ceiling. Untested.Power. A 128×64 P2.5 panel draws about 2.5 A at 5 V at full brightness (derived from the measured runtime table in the README: 10,000 mAh ≈ 4 h). A typical USB power-bank port tops out at 2.4 A. One panel already sits at the ceiling; two do not start.
J3. Panel current currently flowsJ4 → board traces → J3 → panel, which is sized for one panel. Split the supply at the PSU — panels direct, board on logic power only.And note what a wall-powered build costs: Patternflow stops being the thing you carry around on a power bank.
2. What is not limited
Worth stating plainly, because the two walls above get mistaken for a limit on the concept itself.
A Patternflow pattern is a function —
(x, y, time, audio, knobs) → colour. That is hardware-independent. The same patterns already run in two unrelated runtimes today: Pattern Lab's browser preview (JS) and the device (C++). Audio reactivity, encoder input and OSC are all upstream of the panel driver.So: reactive patterns on a large display are not blocked. Only the last step — pushing finished pixels onto specific hardware — is hardware-bound.
3. Routes to a bigger display
A. Tiling — several Patternflow nodes, one surface
Each node drives its own section; a shared clock and per-node coordinate offset make them one canvas. Standard practice for large LED art installations, and the most Patternflow-native answer.
Ingredients that already exist: bidirectional OSC, Wi-Fi,
PANEL_RES_W/H/CHAINas build config,@matrixframes.Missing, and this is real feature work, not configuration:
Nearest existing groundwork: #224.
B. Render elsewhere, feed the wall
Run the pattern function on a PC or Pi, ship frames to whatever already drives the wall (commonly a Colorlight/Novastar receiving card over Ethernet).
The key property: you write the output backend once, not once per pattern. Every existing pattern, and every future one, works the moment the renderer exists. Audio reactivity gets easier here, not harder — the host has a real audio stack.
This is also the only route that reaches genuinely large commercial surfaces, which already have receiving cards in them. Those installations do not want an MCU in the chain.
C. Direct S-PWM support in firmware
Would remove Wall A and substantially soften Wall B — a chip that runs its own PWM means writing a frame once instead of 260 times a second, so one ESP32 covers far more area. Tempting for that reason.
Cost is high: per-family command protocols reconstructed from partly reverse-engineered documentation, two synchronised output streams (frame data + uninterrupted GCLK), a driver abstraction inside Patternflow, and split firmware images since a panel cannot be detected at runtime. Plus at least one physical panel per chip family to develop against.
Realistically an open R&D project with an indefinite maintenance tail — which is exactly why upstream declined it.
D. Add an FPGA/CPLD to the board
Rejected. That is what a receiving card is, and it would delete every property of the current board (all through-hole, 5 unique parts, ~$35, beginner-solderable) to produce a worse Colorlight 5A-75B — a finished board that costs about $20.
4. Where this leaves things
config.h— #224No work is being scheduled from this issue. It exists so the question stops being re-litigated from scratch, and so anyone who wants to attempt route A or B has the numbers in one place.
Corrections and contradicting measurements are welcome — particularly a successful chain-2 with a proper 5 V supply, which would settle the memory question left open in §1.