I’ve been fascinated by understanding patterns that appear in nature, through code. Communing with the inherent emergent patterns that exist in the universe.
The phyllotaxis is one such pattern, the expanding double-spiral shape that appears in the center of sunflowers, succulents, other plants. It’s surprisingly simple code:
// Inner and outer are throw-away,
// just for spacing the cells that are used
float[][] points = new float[numCells + numOuter + numInner][2];
float outerRadius = width / 2;
final int total = numCells + numInner + numOuter;
for (int i = 0; i < total; i++)
{
float f = i / float(total);
float a = i * 1.6180339887;
float distance = f * outerRadius;
float x = -cos(a * TWO_PI) * distance;
float y = sin(a * TWO_PI) * distance;
points[i][0] = x;
points[i][1] = y;
}
Take a number of linear points on a radial line of a circle, and rotate each one by an increasing multiple of the golden ratio.
You get a neat point cloud out of this that already looks very much like a sunflower. Voronoi tessellating it looks quite interesting and seed-pod-like:
After experimenting with this for a while, I had the idea: what if I made it a physical object and put an RGB addressable LED in each cell? A kind of cellular LED matrix. I had built a rectangular neopixel matrix in high school, but I’d been waiting to find a more interesting shape for the next one.
From digital to physical
I exported the cell edge data from my processing sketch, and imported it into a python sketch using the CadQuery library.
Wrote some code to build physical geometry out of the data:
# Create the negative space of a single cell
# shrunk with a negative offset-2d
cell_shape = (cq.Workplane("XY")
.polyline(vertices)
.close()
.offset2D(-wall_thickness/2, kind='intersection')
.extrude(total_height)
.translate((0, 0, base_height)))
# Cut it out of the total to create a shell with walls
total_shape = total_shape.cut(cell_shape)
# Cut the shape of the hole to fit the LED in the center of the cell
total_shape = total_shape.cut(
led_hole.rotate((0, 0, 0), (0, 0, 1), math.degrees(angle))
.translate((centerX, centerY, 0)))
This snippet, after constructing the total volume of all merged voronoi cells, subtracts the shrunk volume of each individual cell from the total, and then carves an LED-sized hole in each. Doing this for all cells yields the yellow shape below. I also split the geometry into four roughly equal quadrants at this point, each of which would fit on my 3D printer bed.
I exported the STEP files and did some post processing in FreeCAD, adding screw holes, building a thin version of it that acted as a ‘faceplate’ with paper under it. I cut and glued paper to the bottom of the thin version, and used the screw holes to screw it into the cell walls.
I 3D printed the shapes, and found some translucent paper that diffuses light well and looks organic and playful with light shining through it. I love the look of this mulberry paper, with such beautiful organic fibers.
Soldering 89 LEDs is kinda tedious. But I found my way into the flow state and it became relaxing. I hooked it up to a spare STM32 blackpill board I had lying around, and wrote a little neopixel driver using SPI. It was already cool with some basic test patterns.
I built a simple sketch framework, letting me write processing-style ‘shader’ code for the LEDs. Using a look-up table with the floating point position for every LED as if it was on the unit circle, I could easily write code like this, which feels like a fragment shader:
void radial_spirals(LEDBuffer leds) {
for (int i = 0; i < NUM_LEDS; i++) { // iterate every cell
// Get LED position from LUT
float x = led_positions[i][0];
float y = led_positions[i][1];
// Polar coordinates
float r = sqrtf(x*x + y*y);
float theta = atan2f(y, x); // Angle in radians [-π, π]
// Spiral pattern: combine angle and radius
float spiral = sinf(theta * 2.0f + r * 5.0f - seconds * 2.0f);
float brightness = (spiral + 1.0f) * 0.5f;
uint8_t level = (uint8_t)(brightness * 255.0f);
leds[i] = rgb(level, 0, 0);
}
}
Pretty nifty.
I very quickly discovered this radial pattern that I enjoyed the most, a pulsating sine wave modulating brightness outward from the center. This ended up being what I used for the primary feature of the audio-reactive sketch I did later.
Adding audio
Wouldn’t it be cool if it could react dynamically to the sounds in the room, rather than just displaying predefined patterns? I wanted it to feel alive and dynamic.
The STM32 board driving it had floating point support and ARM DSP instructions, so I could run the fourier transform and do audio analysis. I added a digital I2S mic, the INMP441, to my breadboard circuit driving the thing.
Since it outputs a digital signal, it’s easy to wire up with no risk of analog noise. The ARM CMSIS library came in handy to process the audio signal and do some analysis. With some auto-gain control and multiband splitting to analyze energy at different frequencies, I had a good foundation to write some code that reacted to audio in dynamic ways.
A lot of tuning and a very chaotic sketch with a lot of global timers and messy state produced this audio-reactive program, which is still the basis of what runs on the board to this day. It came alive listening to Jon Hopkins’ Neon Pattern Drum.
Tidying up
Just a few details left. It needs a sturdier surface to mount it to. I found a bamboo cutting board and routed it into a circle; the natural bamboo wood grain complements the organic paper so well.
I had assembled the controller MCU and mic into a perfboard, and stuffed it into a 3D printed box with a guitar pedal switch on it. But the electronics were janky and prone to noise that would cause the LEDs to flicker when the box moved. I decided to learn how to build PCBs, and switched to a proper PCB mounting the blackpill board and mic and a button, and a proper spot for a logic-level shifter to get a good 5V signal out of the microcontroller. It worked well, and making PCBs was a bit easier than I expected, at least for this level of complexity.
Building the second version
I showed it off at a New Year’s party ringing in 2026, and people seemed to like it; some friends even told me that they wanted one. I got inspired to build a second version, which would hopefully be easier to assemble and would be more sturdy. Since I had just learnt PCB design, I decided to try and switch from a 3D printed plate to mount the LEDs on, to using PCBs as a backplate. I tweaked the shape a bit and added 5-fold symmetry, since PCBs are ordered in batches of 5; rather than four unique pieces that I’d 3D print, I’d order five of the same PCB, and assemble them into a ring.
It turns out that hand soldering neopixel LEDs with a soldering iron is even more of a nightmare, as the pads for individual neopixels sit underneath them; they’re intended to be soldered using a method that heats up the PCB rather than an iron. The project went on hiatus for a bit.
I brought the project parts with me when I attended a programming retreat at Recurse Center in the summer of 2026, and a friend I met there was much more handy with a hot air soldering station than I am. We managed to finish all the soldering, and I designed and 3D printed new cell walls to clip onto the PCBs and screw them together.
After finishing the new version during my Recurse batch, I decided to leave it at Recurse Center as an installation. I switched to an ESP32, so it can be on the wifi network there, and rewrote the firmware in Rust. The controller serves a local website where RC attendees can upload sketches and games to it using a tiny webassembly backend and minimal API.
What’s next?
I’m still not fully satisfied with this project.
Enough people have expressed enjoyment over it that I’d love to be able to make more of them, at the very least to gift to friends if not to sell more broadly. But the process has not been ideal; soldering has been tedious both times for different reasons. The process of attaching the paper is tricky, and the paper itself is fragile and difficult to repair if it breaks. I have some ideas for improvements that merit experimenting with: different types of LEDs which are easier to hand solder, using PCB assembly, testing out a layer of clear acrylic to protect the paper with, attaching the paper face plates with magnets.. So many things to test out in the future.
For now, the project deserves a break, but when the time is right it’ll be onto version 3!