index

glowing cells

2026·generative art · audio reactive · physical computing

status — a research build, and the question behind it is a toolchain one: compose a visual somewhere you can see it, and have it arrive intact on physical hardware. both modes run.

it turns a Waveshare P2 64×64 panel into a playable visual instrument. TouchDesigner generates frames on a laptop; a Raspberry Pi Zero 2W receives them over ArtNet UDP and pushes them to an Adafruit RGB Matrix Bonnet at line rate.

each cell is a discrete physical RGB LED, not a sub-pixel — which is why the visual grammar has to be coarse, legible and built for distance.

the panel through one full track

→ open the specimen sheet — full build documentation: bill of materials, signal flow, effect swatches and the complete dev log.

two modes

the interesting constraint was that every visualiser is built twice.

  • mode A — TouchDesigner hosted on a laptop. audio-reactive, 32-band spectrum, GLSL effects.
  • mode B — the Pi standalone and headless, no computer required. five effects reimplemented in Python.

the second mode is the one that could be left running on its own, with nothing attached to it. it also forces every effect to survive being rewritten against a 512 MB single-board computer.

from node to photon

each frame is a 12,288-byte payload — 64 × 64 × 3 bytes of RGB — chopped into 24 sequentially numbered ArtNet universes and unicast to the Pi. a single Python process reassembles the universes, writes into the rpi-rgb-led-matrix offscreen canvas and swaps it on vsync.

latency is one frame at 60 Hz plus one Ethernet hop. fast enough to feel instant.

seven visualisers

  • fx_noise — organic noise with animated hue cycling.
  • fx_metaball — five-ball metaball liquid spheres in GLSL.
  • fx_audio — 32-band spectrum analyzer, log-mapped.
  • fx_text — scrolling marquee with rainbow coloration.
  • fx_audio_metaball — beat-reactive metaballs; blobs jump on each bass kick.
  • fx_ice_metaball — same shape, fixed ice-blue palette.
  • fx_camera — webcam feed with beat-reactive scatter and hue rotation.

it almost didn't work. twice.

a condensed selection from the dev log — the kind of behavior you only find by staring at a black panel at two in the morning.

  • the display showed two half-height repeats. HUB75E panels need the fifth address line. bridging the Bonnet's 16/E/8 jumper — middle pad to 8 — snapped the second half into place.
  • the LEDs flickered during animation. a BCM2835 PWM conflict with rpi-rgb-led-matrix's hardware pulse train. disabled in /boot/firmware/config.txt with dtoverlay=vc4-kms-v3d,noaudio. steady from then on.
  • TouchDesigner froze the scriptTOP output. with no wired TOP input, TD assumes nothing changed and skips re-cooking. feeding a noiseTOP with tz = absTime.seconds into input 0 forces a cook every frame.
  • the FFT was reading the wrong bins. with frequencylog = 1.0 the samples are log-distributed, so what was labelled 60 Hz actually queried around 20 Hz. switching to linear makes idx = int(freq_hz) work directly.
  • the spectrum bars grew downward. copyNumpyArray follows the OpenGL convention where arr[0] is the bottom row. filling arr[0:bar_px] instead of arr[H-bar_px:H] fixed the direction.
  • the bars pinned at 100%. min(1.0, raw * gain) was clipping. replaced with arctan soft-compression, so loud signals approach the ceiling without collapsing onto it.

full documentation, effect swatches and the complete dev log live on the specimen sheet.

tools

TouchDesigner, Python, GLSL, Raspberry Pi Zero 2W, Waveshare P2 64×64 HUB75E panel, Adafruit RGB Matrix Bonnet, ArtNet, rpi-rgb-led-matrix