This is a Sound Reactive LED Visualizer for Arduino that makes LEDs dance to music or ambient sound.
video_2026-08-05_17-57-06.mp4
The system reads audio from a sound sensor, processes the signal to extract volume and frequency information, then lights up 8 LEDs in a pattern that responds to the music.
Each frame samples for 10ms (100Hz update rate) and tracks peak-to-peak amplitude on the 10-bit ADC (0–1023). This window is short enough for real-time response but long enough to catch a full cycle down to ~50Hz.
On boot, 20 samples (200ms) of ambient noise are averaged, plus a +3 safety margin, capped at 50 to avoid over-filtering in loud rooms. Falls back to 10 if calibration fails.
A running peakHold value auto-adjusts sensitivity, instant attack, 1.5%/frame decay (~86%/sec), so the visualizer stays responsive whether the room is quiet or loud. Floored at 8 to avoid amplifying noise.
Raw amplitude is normalized against the peak, then passed through a soft-knee compression curve:
intensity = x * (1.5 - 0.5x)
This boosts quiet sounds (+38% at low input) while loud sounds naturally saturate at 1.0 : matching how we perceive loudness logarithmically.
Three exponential moving averages extract bass, mid, and treble from the same signal:
| Band | Decay | Time Constant | Behavior |
|---|---|---|---|
| Bass | 0.92 | ~125ms | Smooth, sustained |
| Mid | 0.90 | ~100ms | General rhythm |
| Treble | 0.7 (on attack) / 0.95 (idle) | ~33ms attack / ~200ms fade | Sharp transient detection |
Treble specifically watches for a jump in amplitude between frames (attackSpeed > 5) to catch percussive hits.
Each LED's brightness ("string energy") blends all three bands with position-based weighting:
- Bass weight : strongest on LED 0, fades to 0 by LED 7
- Treble weight : opposite: 0 on LED 0, strongest on LED 7
- Mid : flat 0.3 contribution across all LEDs
stringEnergy = bassEnergy * bassWeight * 0.7
+ midEnergy * 0.3
+ trebleEnergy * trebleWeight * 0.5
Attack/decay physics: brightness jumps instantly to a new peak, then decays exponentially. Lower LEDs decay faster (0.96, ~250ms) to stay punchy with the beat; higher LEDs decay slower (0.98, ~500ms) for a lingering shimmer. this asymmetry is what creates the waterfall effect.
A sustain floor keeps LEDs from going fully dark during sustained notes, and an attack flash briefly overshoots brightness on the lower LEDs when a strong transient hits.
Output is mapped through a PROGMEM gamma table ((value/255)^2.2 * 255) so brightness steps feel linear to the human eye instead of jumping straight to "bright."
A 4-tap moving average filter ((3*old + new) / 4, done with a bit-shift instead of division) prevents flicker between frames.
PWM-capable pins get full analog brightness. Non-PWM pins fall back to on/off, switching at a brightness threshold of 30 (~12%).
| Constant | Value | Purpose |
|---|---|---|
| Sampling window | 10ms | 100Hz sample rate |
| Noise calibration | 20 samples | 200ms ambient average |
| Peak decay | 0.985 | 1.5%/frame |
| Bass decay | 0.92 | Smooth sustained response |
| Mid decay | 0.90 | Medium response |
| Treble decay | 0.95 (idle) | Quick fade after attack |
| Attack threshold | 5 ADC counts | Filters out slow changes |
| Minimum peak | 8 | Prevents over-amplification |
| Gamma exponent | 2.2 | Human perception curve |
| PWM threshold | 30 | ~12% brightness for digital pins |
Set DEBUG = 1 to stream real-time intensity and frequency band percentages to the Serial monitor every 200ms.
| Component | Connection |
|---|---|
| Sound sensor | Analog pin A0 |
| LEDs (x8) | Digital pins 2–9 |
| PWM LEDs | Pins 3, 5, 6, 9 (brightness control) |