Internal · OI-Frame Engineering
Three Machines and the Controller We Wanted to Delete
The frame now runs unattended on three very different computers, and none of them is the expensive part. Neither, it turns out, is the screen resolution we have been treating as a requirement.
The machine was never the bottleneck
OI-Frame is an audio-reactive light sculpture: an LED screen behind two laser-cut plexi sheets, showing a grid of soft circles that move with sound in the room. A small computer sits inside the piece, listens to a microphone, and turns what it hears into light. Over the past weeks that computer has been three different machines — a Windows mini PC, a vendor-supplied Debian media timer, and a Raspberry Pi 5 — and the piece looks identical on all three.
That sentence is the finding, and it took some work to earn. For most of the project the assumption was that rendering was the hard part and the machine was the constraint. A measurement seemed to confirm it: the display appeared to be running at 19–23 frames per second against a 30 fps target, on a 1.5 GHz Celeron with graphics acceleration switched off. That number shaped a week of planning and very nearly bought a rewrite.
It was wrong. The frame was rendering every refresh and always had. The gate that limits how often the display reports its state was set to a threshold three hundredths of a millisecond away from the machine's actual frame interval, so ordinary jitter pushed about half the reports under the line and they were silently dropped. The renderer was fine. The ruler was broken.
"A rate derived from a throttled stream is not the rate of the thing producing it."
Once that was understood the whole performance question went flat. The Celeron draws 45 circles and runs a 4096-point frequency analysis at 60 fps with no graphics acceleration at all; the Raspberry Pi holds the same stream at its cap, measured at 30.0 messages per second. Neither machine is working hard. So the choice of computer cannot be made on speed, and has to be made on everything else — what it costs, who controls the image it boots, and what it can be made to do next.
That pattern repeats, and it is the reason this document exists. Several things in the frame that look like requirements turn out to be accidents of what was on the shelf. The machine is one. The screen is another.
How a wall of light gets driven
To see where the cost actually sits, follow a single value from the software to a photon.
The software's central idea is the field: one number between 0 and 1 for every circle in the grid, laid out row by row. At the current build that is 45 numbers. Everything upstream — microphone, frequency analysis, the rule that turns sound into movement — exists to produce it, and everything downstream exists to consume it.
Downstream, the field becomes pixels, and the arrangement gets strange. The wall is three LED panels of 320 × 180 pixels stacked vertically, 320 × 540 in total. But the computer does not send 320 × 540 to the wall. It sends a standard 1920 × 1080 video signal with the field drawn in the top-left corner and the remaining 91.7% left black, because the LED controller reads the top-left region of whatever arrives and offers no way to say which region to take.
That controller — a NovaStar unit — extracts the corner and sends the result over Ethernet to a receiving card inside the LED cabinet. The receiving card is what actually talks to the LEDs, over a ribbon-cable protocol called HUB75. HUB75 is old, simple and openly documented.
Figure 1 · From field to photons — the chain all three machines share Open full size · print A3 landscape ↗
Figure 1 is the argument in one picture. Three interchangeable computers produce an identical video frame; the frame is 91.7% waste by area; a costly box extracts the corner and converts it to a protocol that is free and open. Forty-five numbers make a round trip through a two-megapixel video signal to arrive where they started.
What three machines taught us
All three run the same software, and all three now start unattended and recover from a reboot without anybody touching them. What differs is what it cost to get there, and what each one leaves possible afterwards.
| Machine | Operating system | Becoming an appliance | What it leaves open |
|---|---|---|---|
| Blackview MP80 | Windows 11 IoT Enterprise LTSC | Shell replacement, custom logon, unbranded boot, autologon, registry edits to silence a startup chime. Roughly a thousand lines of script. | Windows-only drivers and audio interfaces. Little else. |
| Adaptables media timer | Debian 12, vendor image | A single service definition, no administrator rights needed anywhere. | Very little. We do not own the image, have no remote access, and there is no sound server at all. |
| Raspberry Pi 5 | Pi OS Lite 64-bit | One service definition and two configuration files, both now in the repository. | Sensors, direct panel drive, scheduled power-on, faster storage. |
The Pi was not free — it cost a working day, and four of its obstacles were unique to it, from a graphics driver that picks the wrong device on a minimal image to a missing sound server that made the frame fall back to a demo without the wall looking any different. All four are written up in the project's plan documents and none will cost a day again. The relevant point here is the last column: two of these machines are dead ends that happen to work, and one of them is a starting position.
The Pi as the default machine
With speed off the table, the case for the Raspberry Pi rests on four things, and only the first is obvious.
It is cheap, and the piece may be built more than once. We own the image — the constraint that shaped the media timer work was not owning that machine, and most of the resulting caution is a constraint rather than a principle, so it should not be carried onto hardware we flash ourselves. It can grow sensors, which is the argument that actually matters: the frame's plausible future is presence detection and ambient light, dimming or blanking when a room is empty or bright, and getting a sensor into a Windows box is a project where on a Pi it is two wires. That is the difference between a feature being tried and not being tried.
And it can schedule its own power-on, which we tested rather than assumed. The Pi 5 carries a real-time clock that can wake it from a halted state; a Pi 4 does not. Set an alarm, shut down, and it brings itself back — ours returned to a correct wall within eight seconds of the time it was given. Two limits are worth stating plainly: the power supply must stay connected, so this is a low-power sleep rather than a cold start from no mains; and the alarm fires once, so something has to set tomorrow's before shutting down tonight. For a venue that kills power at the breaker, a timed mains socket is the more robust scheduler, because the Pi starts when power is applied.
Specifying the machine
The machine to build on next is specifiable now, and mostly from measurement rather than preference.
| Item | Recommendation | Why |
|---|---|---|
| Board | Raspberry Pi 5, 2 GB | Everything the frame needs measures at about 370 MB; the whole running system is 465 MB. On the 8 GB board we have, 7.2 GB sits idle. 2 GB is four times the headroom in use, at roughly half the price. |
| Power supply | Official 27 W (5 V / 5 A) USB-C | A 20 W unit recorded under-voltage and throttling on two boots out of three, always around 4.5 seconds in — the moment USB enumerates. It read clean at idle before a microphone was attached, which is exactly how this hides. The 27 W supply is also the only way total USB current rises above 600 mA. |
| RTC battery | Fit it | It does not let the frame power itself on after an outage — the board still needs mains. It means the machine knows the time the moment power returns, which matters for scheduling and for logs that can be compared across machines. Nobody fits this after the piece is on a wall. |
| Cooling | Active cooler | The Pi 5 is designed around it, and a throttled Pi reports healthy. |
| Storage | SD card is adequate | Under 2 MB written per boot, essentially nothing at idle, and settings are saved in a way a power cut cannot corrupt. If it ever becomes a concern the answer is an SSD, not a filesystem project. |
One growth vector deserves naming rather than assuming away: memory use tracks the picture library, because uploaded images are held decoded in the display. The test machine has none loaded. A frame carrying a dozen pictures will want meaningfully more than 370 MB — still nowhere near 2 GB, but that is the number to re-measure, not the processor.
Driving the panels directly
If HUB75 is a commodity and the Pi has the pins, why not drive the panels ourselves and delete the controller? It would remove the most expensive component, the warmest one, and — with no video signal in the chain — an entire category of problems around display modes and pixel-exact corner alignment that consumed most of a day.
The research says this is real, and constrained in a specific, quantifiable way.
The reference implementation is hzeller's rpi-rgb-led-matrix, a decade-old and heavily used library. It does not support the Raspberry Pi 5: the Pi 5 moved its pin control behind a separate chip, so the direct hardware access the library relies on is gone. The alternative is Adafruit's Piomatter, which drives the panels from small programmable state machines inside that chip. It reached version 1.0.0 in July 2025 and is documented as lacking features the older library has. For this one future, then, the maturity runs backwards — the Pi 4 has the battle-tested path.
The harder limit is scale, and it is arithmetic. HUB75 refresh falls as more pixels go on one chain, because every pixel is shifted out serially, over and over, fast enough to avoid visible flicker. The documented thresholds: about 16,000 pixels on one chain is already pushing it, and the practical maximum is around 100,000 pixels across three parallel chains — the most a standard Pi supports.
Our wall is 320 × 540 = 172,800 pixels, about 1.7× beyond that.
Figure 2 · The parallelism gap — why the controller exists Open full size · print A3 landscape ↗
Figure 2 also reframes the controller. It is easy to look at a NovaStar and see a box doing video processing, colour calibration and 3D — none of which this piece uses — and conclude it is 97% waste. The measurement says otherwise. Its central job is fan-out: a card of that class drives eight connectors carrying sixteen sets of parallel data, where a standard Pi drives three chains. It is not mostly features we ignore; it is mostly parallelism we would inherit.
"The expensive box is not solving a problem we invented. It is solving the one we would take on."
The resolution nobody chose
Which invites the question the previous section carefully did not ask: why are there 172,800 pixels?
The same way there were three computers. The wall has a 1.8 mm pixel pitch because that is the panel that was available. Nobody derived it from what the piece needs, and if you ask what it needs, the answer is startling.
The field is 45 numbers. The screen shows a 5 × 9 grid of soft circles across roughly 58 × 97 cm — physical circles about 9 cm across, spaced about 11 cm apart. And the circles are not even what the audience sees: they are seen through laser-cut holes in plexi, and the hole defines the shape. The LED behind it needs to be bright, roughly the right size, and smoothly variable in brightness over time. It does not need a 1.8 mm pitch to do that. It is running at roughly the resolution of a phone screen to draw forty-five blobs.
Lower the pitch and the numbers collapse:
| Pixel pitch | Panel 600 × 1200 mm | Pixels | Against a single Pi |
|---|---|---|---|
| 1.8 mm | today's wall, 320 × 540 | 172,800 | 1.7× beyond three chains |
| 5 mm | 120 × 240 | 28,800 | comfortable on three chains |
| 10 mm | 60 × 120 | 7,200 | one chain, with room to spare |
At 10 mm pitch the wall is 24 times fewer pixels, which is well inside the 16,000-pixel single-chain threshold. There is no parallelism problem to solve. No second controller, no Compute Module, no synchronised drivers — one Pi, one ribbon cable, and a refresh rate high enough that bit depth stops being a compromise.
And the constraint on how far this can go is not electronic, it is the laser cutter. The plexi is 3 to 5 mm thick, which puts a floor of roughly a centimetre on how fine a hole pattern can be cut and still look like holes rather than perforation. A hole grid that cannot go below a centimetre does not need a screen that resolves below a centimetre.
"We were sizing the driver to the screen we own, when the screen is the cheapest thing in the chain to change."
Two honest caveats. A coarser screen means each circle is drawn with fewer pixels — at 60 × 120 a circle spans about ten pixels — and whether that reads as a soft disc or a staircase behind a given plexi sheet is an experiment, not a calculation. And a coarse, very bright panel is a slightly less common product than a fine one, so it needs sourcing rather than assuming. Neither is a reason not to test it; both are reasons to test it with a real sheet in front of a real panel before ordering anything.
The reward is not only cost. A lower-resolution panel makes the whole downstream chain ordinary: one Pi, one open protocol, no controller, no receiving card, no video signal, and none of the mode-negotiation and corner-alignment problems that fill this project's technical notes.
One panel, one Pi, one unit
There is a second way to make the pixel count somebody else's problem, and it composes with the first rather than competing.
Stop treating the wall as one screen. A single 320 × 180 panel is 57,600 pixels — already inside one Pi's ceiling, at today's absurd resolution. So the unit of the system becomes one panel and one Raspberry Pi, and a bigger frame is more units rather than a bigger controller.
Figure 3 · The unit — one panel, one Pi, and the field over the network Open full size · print A3 landscape ↗
The software is already most of the way there. The field is published over the network today — that is how the phone draws its live preview — so a follower unit is a client that already exists, subscribing to the same stream and drawing its own slice of the grid instead of the whole thing. Only one unit needs a microphone, and only one needs to be the thing the phone talks to.
What this buys is not only technical. It makes the frame a product with a unit: one panel, one Pi, one ribbon cable, one power supply, testable on a bench on its own. A larger piece is more of them. A different shape is a different arrangement of them. Nothing about the electronics changes when the artwork does — which is the property that separates a piece of equipment from an installation that happens to work.
And it gives a first experiment that is small enough to actually do: take one of the spare panels, connect one Raspberry Pi, and light it. That single test answers the questions no amount of research can.
The first experiment, concretely
Everything needed is either already on a shelf or is a small order. Nothing here commits us to anything — if direct drive turns out to be wrong, a 2 GB Pi is the board we want for the ordinary build anyway.
| Item | Status | Note |
|---|---|---|
| One spare LED panel + its receiving card | have | Open the cabinet and record what is inside before anything is connected. |
| Raspberry Pi 5, 2 GB | buy | The board the ordinary build wants regardless. Roughly half the price of the 8 GB. |
| Official 27 W USB-C supply | buy | Needed anyway — the 20 W unit under-volts. |
| Active cooler | buy | Cheap, and a throttled Pi reports healthy. |
| HUB75 adapter with level shifting | buy | The Pi's pins are 3.3 V and HUB75 panels expect 5 V logic. An adapter board (Adafruit's RGB Matrix Bonnet or equivalent) handles this and the wiring; driving the ribbon straight off the header is the usual cause of "works but flickers". |
| RTC battery | buy | While ordering. Nobody fits it later. |
What the bench test answers, in order of how much it changes the plan:
- What is actually in the cabinet. Receiving card model, how many modules, their size and scan rate. This decides whether "drive it directly" means a ribbon cable or a redesign.
- How many pixels one Pi drives well in practice, as opposed to in a documentation table — measured as refresh rate and visible flicker on the real panel.
- How coarse a circle can get before it stops reading as a soft disc behind a real plexi sheet. Cut two or three test sheets at different hole grids and look at them. This is the question that decides the resolution, and it cannot be answered on paper.
It is also the point at which everything in this document either holds together or does not. The machine is chosen and measured. The controller's role is understood rather than assumed. The resolution has gone from a requirement to a variable. What remains is to put one panel on a bench and find out which of the two ways of shrinking the problem — fewer pixels, or more units — we actually need. The honest guess is both, and that the combination makes the frame simpler than it has ever been.
Glossary
Terms used in this document, in plain language.
- Cell
- One circle position in the grid. The current wall has 45.
- Field
- The state of the whole frame: one value from 0 to 1 per cell. The seam between the logic and the rendering.
- GPIO
- General-purpose input/output. The pin header on a Raspberry Pi, used for sensors and for driving panels directly.
- HUB75
- The ribbon-cable protocol LED matrix panels speak. Open, simple, widely supported.
- NovaStar
- Manufacturer of the LED controller and receiving cards in this build.
- Pixel pitch
- The distance between neighbouring LEDs, in millimetres. Smaller means finer and more expensive; the current wall is 1.8 mm.
- Receiving card
- The board inside an LED cabinet that turns the controller's network stream into HUB75 signals.
- Rig
- The settings describing the physical frame — panel size and count, grid, circle size, row spread. Set once at installation.
- RTC
- Real-time clock. Keeps time while the machine is off; on a Pi 5 it can also wake it.
- Unit
- Proposed here: one LED panel plus one Raspberry Pi, the smallest thing that can be built and tested on its own.
- WLED
- Widely used open-source firmware for LED projects, mainly on ESP32 microcontrollers.
Settled
The Pi is the default
Measured rather than argued: 30.0 updates per second, 465 MB used, a 28-second boot, unattended recovery, and scheduled power-on proven on the hardware. A 2 GB board with the 27 W supply.
Next
One panel on a bench
A spare panel, its receiving card and one Pi. It answers what is inside the cabinet, how many pixels drive well in practice, and how a coarser circle reads behind real plexi.
Later
Fewer pixels, more units
A 10 mm pitch panel is 24× fewer pixels and fits one ribbon cable. A bigger frame becomes more units rather than a bigger controller.