Skip to content

About

Procedural LED light show for ESP32 that follows real musical arrangement structure (beats, bars, phrases, sections) instead of random effects — no audio input needed.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

Repository files navigation

Structured LED Sequencer

A procedural light show engine for a row of N simple on/off lamps (relay- or MOSFET-switched LEDs, an old industrial stack light, whatever you've got), running on an ESP32. Instead of picking effects from a flat random list, it arranges patterns using real musical structure — beats, bars, phrases, and sections in an A-A-B-A layout — so the result reads as composed rather than random, without needing any audio input.

No microphone, no audio analysis, no sync to actual playing music. The engine generates its own dramaturgy purely from timing rules and weighted randomness.

Why this is more than a random blinker

Most DIY blink projects pick a new pattern at random on every step. The result almost always feels arbitrary, not "composed" — because there's no musical structure underneath it: no groove that holds for a while, no highlight that stands out from the baseline, no build-up.

This engine instead reproduces the fixed time hierarchy that pop and techno arrangements use:

Beat → Bar (4 beats) → Phrase (4 bars) → Section (2/4/8 phrases)
  • Every section has a base motif A, a contrasting motif B (A-A-B-A), and ends with a fill.
  • Every pattern ("family") belongs to one of three impact tiers (base / noticeable / highlight) — a section's highlight must always outrank the running motif, otherwise it doesn't land as one.
  • Energy (1–4) rises and falls like a staircase, not a smooth wave, and only changes at section boundaries.
  • Right before an energy increase there's a short blackout — the moment that makes the next hit land.
  • Occasionally a multi-bar riser: the same motif gets subdivided more finely bar by bar (quarter → eighth → sixteenth notes) and resolves into a strobe — the classic techno build-up.

More background and the reasoning behind these choices: docs/CONCEPT.md

What it does

  • Manual mode: press the button, one lamp lights up; short presses step through the row (bouncing back and forth)
  • Show mode: double-tap starts the generative sequence. Short press = shuffle (reroll the current section immediately if you don't like it)
  • Off: long-press (~1.8s) from anywhere

13 pattern families, 7 strobe variants, 5 rhythmic subdivisions (down to sixteenth notes) — hundreds of thousands of valid section configurations. Everything is generated at runtime; no frame is hardcoded.

Any number of lamps, in any physical arrangement

The engine works with any lamp count (2 or more). Position-based patterns (Sweep, Bounce, Comet, ...) use whatever order you list your pins in — see config.h. That order should match the physical arrangement of your lamps, not wiring convenience, since it's what "next to" means for the engine.

Hardware

Built and tested on: ESP32 DevKit + low-side MOSFET switch modules + a repurposed 3-zone industrial stack light. None of that is required — any microcontroller with enough digital outputs and any switchable lamps work, as long as you can drive each one from a simple HIGH/LOW pin.

Full build guide for the stack-light version: docs/WIRING.md

Quick start

  1. Open firmware/structured_led_sequencer/structured_led_sequencer.ino in the Arduino IDE (config.h needs to sit in the same folder — the IDE will show both as tabs automatically)
  2. Board: ESP32 Dev Module (may show as "ESP32 Dev Board" on newer core versions)
  3. Edit config.h — at minimum, set NUM_LAMPS, LAMP_PINS[] in your physical order, and BUTTON_PIN
  4. Upload

To preview patterns and tune weightings without hardware:

python3 tools/simulator.py --lamps 3 --duration 60
python3 tools/simulator.py --lamps 8 --duration 60   # try a different lamp count

Prints an ASCII preview of the generated show straight to the terminal — useful for iterating on config.h values without reflashing every time.

Extending it

The pattern logic lives in buildFrames() in the main .ino. Everything above it (timing, section structure, impact rules) is independent of lamp count and pattern content, so adding a new family only means adding a case to that one function and, if it should be usable as a base motif or fill, adding it to MOTIF_FAMILIES or FILL_FAMILIES.

License

MIT — see LICENSE. Do whatever you want with it.

About

Procedural LED light show for ESP32 that follows real musical arrangement structure (beats, bars, phrases, sections) instead of random effects — no audio input needed.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages