Phyllotaxis: An audio-reactive LED display
Phyllotaxis: An audio-reactive LED display
By Jagi Natarajan
"One of nature's beautiful patterns captured in dancing lights"
I have always been driven by a desire to decode the emergent patterns of the universe through the lens of programming. One such captivating phenomenon is phyllotaxis—the expanding, double-spiral arrangement frequently observed in succulents, sunflowers, and various other botanical structures.
🌀 The Mathematical Foundation
The underlying logic of this pattern is surprisingly elegant. By placing points along a radial line and rotating each subsequent point by a multiple of the golden ratio (), a naturalistic point cloud emerges.
The Logic in Code
The following Processing snippet demonstrates how to generate these coordinates:
// 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 ; // The Golden Ratio
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 ;
}
When these points are processed via Voronoi tessellation, the result transforms from a simple cloud into a structure resembling a seed pod.
🛠️ From Digital to Physical
While I had previously created a standard rectangular Neopixel matrix in high school, I wanted a more organic geometry for my next project. I decided to turn this mathematical model into a physical RGB LED display.
The Design Pipeline
The transition from a sketch to a 3D-printable object followed this workflow:
To create the physical housing, I used the CadQuery library in Python. The logic involved creating a total volume and then subtracting the interior of each cell to create walls.
# Create the negative space of a single cell
# shrunk with a negative offset-2d
cell_shape = ( cq.offset2D ( - wall_thickness / 2 , kind = 'intersection' )
.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.translate (( centerX , centerY , 0 )))
Fabrication Details
- Segmentation: The model was split into four quadrants to fit the 3D printer bed.
- Refinement: I used FreeCAD to add screw holes and a thin faceplate.
- Diffusion: I selected mulberry paper for the faceplate due to its organic fibers and excellent light diffusion properties.
💻 Firmware and Visuals
The hardware is powered by an STM32 Blackpill board, utilizing an SPI-based Neopixel driver. To make programming the visuals easier, I implemented a framework that mimics fragment shaders. By using a Look-Up Table (LUT) to store the floating-point positions of each LED as if they were on a unit circle, I could write radial effects easily.
Example: Radial Spirals
The following C++ code calculates brightness based on polar coordinates: and .
void radial_spirals ( LEDBuffer leds ) {
for ( int i = 0 ; i < NUM_LEDS ; i ++ ) {
// 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 );
// 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 );
}
}
🎵 Adding Audio Reactivity
To move beyond static patterns, I integrated real-time audio analysis.
Technical Stack
| Component | Purpose |
|---|---|
| INMP441 | Digital I2S Microphone (reduces analog noise) |
| ARM CMSIS | Library for DSP and signal processing |
| FFT | Fast Fourier Transform for frequency analysis |
| Auto-Gain | Normalizes input volume levels |
By splitting the audio into multiple frequency bands, I could map specific energy levels to the pulsating sine waves radiating from the center of the display.
📦 Final Assembly and Iteration
The final aesthetic combines the 3D-printed core with a routed bamboo cutting board base, which complements the organic feel of the mulberry paper.
The Build Checklist
- 3D print quadrants
- Glue mulberry paper faceplate
- Route bamboo base
- Install I2S Microphone
- Implement audio-reactive sketch
Initial Version: The electronics were built on a perfboard and housed in a 3D-printed box with a guitar pedal switch, but they were janky and prone to flickering noise.
Final Version: I transitioned to a professional custom PCB to mount the Blackpill and microphone. This included a logic-level shifter to ensure a stable 5V signal for the LEDs, eliminating the flicker and improving reliability.