Build an ESP32 Audio Spectrum Analyzer / VU Meter: FFT, I2S Audio, and Addressable LED Bar Graphs
An audio-reactive LED display — bars that jump with the bass, colors that shift with the treble — is one of the most satisfying "instant gratification" electronics builds because the payoff is immediate and visible the moment it's wired up. This project builds a real spectrum analyzer, not just a volume-triggered blink: an ESP32 sampling audio through an I2S microphone or line-in input, running an FFT to break the signal into frequency bands, and driving an addressable LED strip or matrix so each band of LEDs actually responds to its corresponding frequency range rather than the whole display just pulsing with overall loudness.
Two Approaches: Dedicated Chip vs. Software FFT
There are two reasonable ways to get frequency-band data out of an audio signal on an ESP32, and they trade off simplicity against flexibility. The MSGEQ7 is a dedicated 7-band graphic equalizer chip that does the frequency analysis in analog hardware and outputs each band's level sequentially on a single analog pin — wiring is trivial (a strobe pin, a reset pin, one analog output) and there's no DSP code to write, but you're locked into its fixed 7 bands and its response curve. The alternative is sampling raw audio through an I2S microphone (like the INMP441 this site already covers for ESP32 I2S audio projects) and running a software FFT using the arduinoFFT or ESP-DSP library, which gives you full control over band count, frequency ranges, and update rate at the cost of real firmware work and enough free RAM/CPU headroom to run the transform continuously without stuttering the LED output.
ApproachProsCons MSGEQ7 hardware chipSimple wiring, no DSP code, low CPU loadFixed 7 bands, limited customization of response curve I2S mic + software FFTFully customizable bands and smoothing, higher resolutionMore firmware complexity, needs consistent sample timingBuilding the Software FFT Version
Sample audio continuously from an INMP441 I2S microphone into a buffer (256 or 512 samples is a reasonable starting size — it's a tradeoff between frequency resolution and update rate), apply a Hann or Hamming window to reduce spectral leakage, then run the FFT and bucket the resulting frequency bins into however many display bands your LED layout uses (8, 16, or 32 bands map naturally onto common LED bar/matrix layouts). Apply logarithmic band spacing rather than linear — human hearing and most music content are heavily weighted toward the lower and midrange frequencies, so linear bucketing wastes most of your display's resolution on a narrow slice of high frequencies that rarely have much energy. Smooth each band's output over a few frames (a simple exponential decay works well) so the display doesn't flicker erratically frame to frame — raw FFT output on music is noisier than it looks in a demo video.
Driving the LED Output
Use FastLED or the Adafruit NeoPixel library to drive a WS2812B strip or matrix, mapping each frequency band to either a column height (bar graph style) or a section of a matrix. Keep the LED update loop separate from — and running faster than — the audio sampling/FFT loop using FreeRTOS tasks on the ESP32's second core, since blocking the LED refresh while waiting on the next audio buffer produces visibly stuttery animation. Power the LED strip from its own adequately-sized 5V supply rather than the ESP32's onboard regulator — a strip of any real length pulling full brightness on all channels draws far more current than a dev board's 5V pin can supply, and this is one of the most common reasons a first build works on the bench with a few LEDs lit and then browns out or resets randomly once the full strip is connected.
Line-In vs. Microphone Input
A microphone picks up room audio, which is convenient (no wiring into an audio source) but is also picking up room acoustics, mic placement, and background noise — for a display meant to react cleanly to music from a specific source, tapping the audio electrically (via a line-level input with a simple attenuation/biasing circuit to bring it into the ESP32 ADC's safe input range, or through an I2S DAC/ADC combo board) gives a cleaner, more consistent signal. If you go the electrical line-in route, never connect a line-level or speaker-level signal directly to a microcontroller's analog input without a proper attenuation and DC-biasing network — an unexpectedly loud signal or a wiring mistake can exceed the ADC's voltage tolerance and damage the input pin.
Safety Notes
If you're tapping audio from a powered speaker or amplifier's output rather than a proper line-level jack, be aware that some amplifier outputs are not ground-referenced the same way as line-level signals, and connecting one directly can create a ground loop or damage your ESP32 — use a dedicated line-in input or an isolation/attenuation circuit rather than wiring straight into a speaker output. Keep LED strip power wiring properly gauged for the current draw of a full-brightness strip — a long run of thin wire under high current is both a performance problem (voltage droop causing color shift down the strip) and a heat/fire risk at the connector.
Wrapping Up
A software-FFT spectrum analyzer is more work than wiring up an MSGEQ7, but the payoff is a display that actually looks like it's listening to the music rather than just reacting to volume — and it's a genuinely useful, approachable introduction to digital signal processing on a microcontroller that has obvious, immediate visual feedback when you get the windowing and band-mapping right.
Related Guides
- How to Program Addressable LED Strips: WS2812B Patterns, Effects, and Power Design
- Build a Word Clock: Laser-Cut Face, WS2812B LEDs, and ESP32
- Build a Meshtastic Off-Grid Mesh Messaging Node with ESP32 and LoRa
- I2C vs SPI vs UART: How to Choose and Use Serial Communication Protocols
- How to Use Sensors with Arduino and ESP32: Temperature, Distance, Load, Current, and Hall Effect
- How to Control Motors with Arduino and ESP32: Stepper, DC, and Servo Drivers
- How to Hack Wi-Fi and Bluetooth with the Flipper Zero and Wi-Fi Marauder
- ESP32: Setting Up for Arduino IDE