Internal · OI-Frame

The Signal Chain and the Update Loop

What the frame is made of and where its delay comes from — and the exact steps to change the software and get that change onto the machine.

01 · The chain today

A microphone hears the room. A Raspberry Pi turns that sound into 45 numbers, draws them as a small picture, and sends the picture out over HDMI. A NovaStar controller reads one corner of that picture and pushes it down an Ethernet cable to a receiving card in the LED cabinet, which drives the panels over HUB75.

Every stage below is a real part with a real part number, taken from the machine and from the repository — not from a plan.

Figure 1 · The chain, with where the time goes Open full size · print A3 landscape ↗

RODE NT-USB USB mic · in use today Behringer UCA202 RCA line in · optional Raspberry Pi 5 · 8 GB Pi OS Lite 64-bit · 10.0.1.156 PipeWire → Chromium capture FFT 4096 points field · 45 cells (5 × 9) canvas 2D · 45 circles one window, 320 × 540 at 0,0 HDMI 1920×1080 @ 60 Hz NovaStar T40 retail name TB40 Taurus player + sender reads top-left corner no scaling 10.0.1.242 Gigabit Ethernet LED CABINET A5s Plus receiving card HUB75E out low-latency capable 3 panels 320 × 540 px WHERE THE TIME GOES · MICROPHONE TO LIT PIXEL Listening 53–68 ms capture buffer + 4096-point FFT window Drawing and sending 42–58 ms frame wait + compositor + HDMI scan-out LED chain 50–67 ms T40 + receiving card + driver IC · 3–4 frames TOTAL ≈145–190 ms Inertia — our own smoothing, already turned down +30 ms this frame runs inertia 0, the minimum — but there is still a 30 ms floor built in. At the 0.45 default it would be 255 ms; at 1.0, 530 ms. As set up today: about 175–220 ms, microphone to light. Inertia is the only lever that needs no rebuild — and it is already at its floor, so everything left costs money or engineering. Every figure is a code constant or a published vendor model. No end-to-end latency has ever been measured on this project.

Two things in that picture are worth saying out loud. The Pi sends a full 1920 × 1080 signal but only paints a 320 × 540 corner of it, because the NovaStar reads from the top-left and does not scale — the other 92 % of the frame is black and thrown away. And the software is one Electron application drawing 45 circles onto a plain canvas, with hardware acceleration off. It hits every refresh anyway.

02 · Where the delay comes from

Sound reaches the light in roughly 175 to 220 milliseconds. Almost all of that is the chain itself, and the one part we control is already turned down as far as it goes.

The smoothing control called inertia is set to 0 on this frame. Even there it is not free: the code keeps a 30 millisecond floor, deliberately, so a cell can never snap instantly and flicker. Turned up to its 0.45 default that floor becomes 255 ms, and at maximum 530 ms — so inertia is by far the biggest lever in the system, and we are already sitting at the fast end of it.

StageDelayWhere the number comes from
Microphone capture buffer10–25 msStandard for a class-compliant USB device. No latency hint is set in the code, so this is device-dependent and unmeasured.
FFT window~43 ms4096 points at 48 kHz spans 85 ms of audio; the effective delay is roughly the middle of that window. Analyser smoothing is explicitly set to zero.
Waiting for the next frame~8 msHalf a frame at 60 Hz, on average.
Drawing 45 circlesunder 1 msMeasured as hitting every vsync, even with hardware acceleration off.
Compositor and HDMI scan-out33–50 msTwo to three frames at 60 Hz. Standard, not measured here.
NovaStar chain50–67 msNovaStar's own model: sender 1 frame + receiving card 2 frames + driver chip 0–1 frame = 3–4 frames.
Inertia, as set today+30 msSet to 0, its minimum. The 30 ms is a floor built into the code so cells cannot snap and flicker. At the 0.45 default this would be 255 ms; at 1.0, 530 ms.
Microphone to light, today≈175–220 msSum of the above.

Two honest caveats. NovaStar publishes no latency figure for the TB40 at all — the words "latency" and "delay" appear nowhere in its specification. The 3–4 frame figure is NovaStar's general model for an LED chain, applied to our parts. And nobody has ever measured this frame end to end; every number above is either a constant read out of our code or a published vendor model.

One thing worth trying

Our A5s Plus is on NovaStar's list of receiving cards that support low latency mode, and the driver chips in our panels have the built-in memory that mode requires. Ticking "enable receiving card low latency" in NovaLCT should remove one frame — about 17 ms — for the cost of a checkbox. It cannot be combined with Genlock, and it limits each port to 512 pixels wide, neither of which affects us. [Likely — worth testing, not yet tried.]

03 · Changing the software

All the work happens on the Windows machine, in one folder. There is one branch, main, no review process and no build server. You edit, you build, you copy it over. That simplicity is deliberate — it means one person can carry a change from idea to wall in about ten minutes.

The tool that does the editing is Claude Code, running inside VS Code. You do not need to read or write any code yourself. You describe the change in ordinary language, it finds the right files, makes the edit, and shows you what it touched before anything is built.

  1. Open the project. Start VS Code, then File → Open Folder → C:\Users\Armin Seltz\Git\OI-Frame. Everything — the app, the documents, the Pi's configuration — lives in that one folder.
  2. Open Claude Code from the sidebar. Carry on in an existing conversation if you are continuing something, or start a new one for an unrelated change. A fresh conversation for a fresh topic gives better results, because it is not carrying yesterday's context.
  3. Say what you want, in plain words. "Make the circles fade out more slowly when the music stops." "Add a second row of cells." It will ask if something is ambiguous, then make the change and explain what it did.
  4. Ask it to bump the version and write the changelog. Both matter more than they look. The version number lives in one line of one file and ends up in the name of the file you send to the Pi — and the Pi decides whether a build is new by looking at that name. Ship twice with the same number and the second one is silently ignored. The changelog is how anyone later works out what a version actually contains; it has been skipped once and that version is still a blank in the record.
  5. Ask it to run the file check. A one-second scan for invisible corruption in the files the app serves. It exists because a single stray byte once made the phone page render completely blank, while the desktop, the server and every other test passed happily.
  6. Ask it to commit and push. This is the save point. If a change turns out badly, having it committed is what makes going back easy.

None of these steps needs a terminal. Ask for them in the conversation and Claude runs them, shows you the output, and tells you if something failed.

04 · Getting the change onto the Pi

The Raspberry Pi has no keyboard, no mouse and no screen of its own — it only drives the wall. There are two ways to reach it, and the first one needs no computer skills at all.

The USB stick — the way anyone can do it

Copy one file onto the stick. Push the stick into the Pi. Wait. Pull it out.

  1. Build the file. One command turns the project into a single compressed file. Ask Claude Code to do it — the only thing worth knowing is that it makes two versions, one for a normal PC and one for the Pi, and the Pi's is the one marked arm64. The wrong one fails in a way that explains nothing.
  2. Copy it into the installer folder on the stick labelled OI-FRAME. That is the only folder that matters and the only stick that works.
  3. Eject the stick properly — the Windows "safely remove" — so the copy is genuinely finished and not still sitting in memory.
  4. Push it into the Pi. Nothing else. No reboot, no keyboard, no laptop. Plugging the stick in is the instruction.
  5. Wait for the wall. It goes black and comes back showing the new version number. Measured on the real machine: twelve seconds. Then pull the stick out.

Afterwards there is a file called update-log.txt on the stick, with one line per attempt saying what happened. It is written by the Pi and read back on your laptop, and it is the only feedback that leaves the room with you.

Going back is the same act as going forward. Put the older file in installer, delete the newer one, and plug the stick in again — the stick decides which build runs.

Over the network — the developer path

The same thing can be done from the Windows machine across the network, and that is how a developer works, because it needs no walking. One thing is worth knowing about it: the Pi does not accept passwords. It only accepts a key, and exactly one computer's key is registered today. A different computer cannot connect at all until its key is added — no password will help.

If that computer is ever lost, the way back in is connect.raspberrypi.com. The Pi is signed in to Raspberry Pi's own service, so that page gives a working terminal on it from any browser, with no key at all. Add the new computer's key from there and normal access resumes.

This is the reason the stick exists. The network path is faster for someone who already has access, and completely closed to someone who does not.

Figure 2 · The update loop Open full size · print A3 landscape ↗

AT THE COMPUTER AT THE FRAME CARRY IT read the version on the wall, and update-log.txt on the stick, then decide what to change next 1 · Edit describe it to Claude Code new version number + changelog 2 · Build one file, for arm64 not x64 3 copy to installer\ 4 · Push the stick in — and that is the whole instruction the Pi installs it and restarts itself · about 12 seconds no reboot, no keyboard, no laptop, no terminal Nothing on the Pi updates itself, and there is no update button in its web page. A build arrives because somebody carried it there. A damaged file is refused and the wall keeps running what it has. The same build twice does nothing. The stick decides which build runs.

05 · Glossary

arm64
The processor family the Raspberry Pi uses. A build for a normal PC (x64) will not run on it, and the mistake is silent.
field
The 45 numbers the software produces from the sound — one per circle, each between 0 and 1. It is the seam between listening and drawing.
FFT
Fast Fourier Transform. The maths that splits a sound into how much of each pitch it contains. Ours looks at 4096 samples at a time, about 85 ms of audio.
frame
One picture. At 60 Hz a frame lasts 16.7 ms, which is the unit LED people quote delays in.
HUB75
The standard ribbon-cable connection between a receiving card and an LED panel.
inertia
Our smoothing control. Higher means the light follows the music more slowly. Set to 0 on this frame; the default is 0.45, the maximum 1.0.
NovaLCT
NovaStar's Windows configuration tool for controllers and receiving cards. Where the low-latency setting lives.
Pi Connect
Raspberry Pi's own remote-access service. This Pi is signed in to it, so a browser at connect.raspberrypi.com gives a terminal on the machine without needing a key first.
receiving card
The board inside the LED cabinet that turns the Ethernet signal into panel signals. Ours is an A5s Plus.
SSH
Opening a command line on another computer over the network. The Pi allows it by key only — passwords are switched off.
SSH key
A matched pair of files that replaces a password. The secret half never leaves your computer; the public half is registered on the Pi. One is registered today.
SCP
Copying a file to another computer over the same connection SSH uses. Same door, different use.
service
A program Linux starts at boot and restarts if it stops. The frame is one, which is why updating it ends with "restart the service" rather than "open the app".
vsync
Waiting for the screen's next refresh before showing a new picture. It costs a little delay and prevents tearing.
OI-Frame · The Signal Chain and the Update Loop Designed for A3 landscape print ← back to the document

Figure 1 · The chain, with where the time goes

RODE NT-USB USB mic · in use today Behringer UCA202 RCA line in · optional Raspberry Pi 5 · 8 GB Pi OS Lite 64-bit · 10.0.1.156 PipeWire → Chromium capture FFT 4096 points field · 45 cells (5 × 9) canvas 2D · 45 circles one window, 320 × 540 at 0,0 HDMI 1920×1080 @ 60 Hz NovaStar T40 retail name TB40 Taurus player + sender reads top-left corner no scaling 10.0.1.242 Gigabit Ethernet LED CABINET A5s Plus receiving card HUB75E out low-latency capable 3 panels 320 × 540 px WHERE THE TIME GOES · MICROPHONE TO LIT PIXEL Listening 53–68 ms capture buffer + 4096-point FFT window Drawing and sending 42–58 ms frame wait + compositor + HDMI scan-out LED chain 50–67 ms T40 + receiving card + driver IC · 3–4 frames TOTAL ≈145–190 ms Inertia — our own smoothing, already turned down +30 ms this frame runs inertia 0, the minimum — but there is still a 30 ms floor built in. At the 0.45 default it would be 255 ms; at 1.0, 530 ms. As set up today: about 175–220 ms, microphone to light. Inertia is the only lever that needs no rebuild — and it is already at its floor, so everything left costs money or engineering. Every figure is a code constant or a published vendor model. No end-to-end latency has ever been measured on this project.
OI-Frame · The Signal Chain and the Update Loop Designed for A3 landscape print ← back to the document

Figure 2 · The update loop

AT THE COMPUTER AT THE FRAME CARRY IT read the version on the wall, and update-log.txt on the stick, then decide what to change next 1 · Edit describe it to Claude Code new version number + changelog 2 · Build one file, for arm64 not x64 3 copy to installer\ 4 · Push the stick in — and that is the whole instruction the Pi installs it and restarts itself · about 12 seconds no reboot, no keyboard, no laptop, no terminal Nothing on the Pi updates itself, and there is no update button in its web page. A build arrives because somebody carried it there. A damaged file is refused and the wall keeps running what it has. The same build twice does nothing. The stick decides which build runs.