Guides ·

How an Audio Bar Visualizer Splits Frequencies: FFT, Log Bands, and Tilt

A bar visualizer looks simple: low notes on the left, high notes on the right, taller bars for louder sound. Getting from an audio file to those bars takes a few distinct steps, and each one has design choices that change how the result looks. This article walks through them using this visualizer’s actual processing, so you can see why the sliders behave the way they do.

Step 1: Cut the audio into frames

Audio is a stream of samples, typically 44,100 or 48,000 per second. A video is a sequence of still frames, typically 24, 30, or 60 per second. To draw one picture of the spectrum per video frame, the visualizer takes a short slice of audio at the start time of each frame.

The slice is 2,048 samples long, about 46 ms at 44.1 kHz. That is longer than one video frame at 30 fps (33 ms), so consecutive slices overlap a little. Overlap is normal and makes motion smoother.

Before analysis, stereo is mixed down to mono by averaging the channels. Bar visualizers rarely show left and right separately, and mixing first halves the work.

Step 2: Window the slice

Cutting a slice out of a continuous signal creates sharp edges at both ends. Those edges show up in the spectrum as false energy spread across many frequencies, called spectral leakage. To reduce it, each slice is multiplied by a window function that fades the signal smoothly to zero at both ends.

This visualizer uses a Blackman window, the same window the Web Audio API’s AnalyserNode uses. It trades a little frequency sharpness for low leakage, which keeps quiet frequencies from being buried under the skirts of loud ones. For a display that has to look clean, low leakage matters more than sharpness.

Step 3: Run the FFT

A fast Fourier transform (FFT) converts the windowed slice from a list of sample values over time into a list of strengths per frequency. A 2,048-sample FFT produces 1,024 useful frequency bins, spaced evenly from 0 Hz up to half the sample rate. At 44.1 kHz each bin is about 21.5 Hz wide.

The magnitude of each bin is converted to decibels and stored. Decibels match how we hear loudness: a sound ten times stronger in amplitude does not sound ten times louder, and the decibel scale compresses that range into something closer to perception.

The visualizer runs this once per video frame for the whole track when you load it, and keeps the results. Everything after this point happens live, every time a frame is drawn, which is why the reaction sliders take effect without re-analysis.

Step 4: Group bins into log-spaced bars

Now there are 1,024 bins per frame and somewhere between 16 and 64 bars to draw. The obvious approach, giving each bar an equal share of bins, gives poor results. With 40 bars, each bar would cover about 550 Hz. The first bar alone would contain all the bass, kick drum, and most of the low male voice; the right half of the screen would show frequencies above 11 kHz that carry little musical content.

Human pitch perception is logarithmic. Each octave doubles the frequency: 55 Hz to 110 Hz is one octave, and so is 5,000 Hz to 10,000 Hz. Musically, those ranges are equally “wide”. So the bars are spaced logarithmically: each bar covers the same ratio of frequencies rather than the same number of hertz.

This visualizer spans 30 Hz to 16 kHz, a little over nine octaves. Below 30 Hz there is mostly rumble that few speakers reproduce; above 16 kHz there is little musical energy and many lossy files cut it off entirely.

With the default 40 bars, each bar covers a frequency ratio of about 1.17, roughly a sixth of an octave:

  • The first bar spans about 30–35 Hz.
  • A bar in the middle spans about 690–810 Hz.
  • The last bar spans about 13.7–16 kHz.

Each bar’s value is the average of all bins that fall inside its range.

The low-frequency resolution limit

Log spacing creates a mismatch at the bottom. The FFT bins are 21.5 Hz wide everywhere, but the lowest bars are only 5 or 6 Hz wide. Several of the leftmost bars end up reading the same one or two bins. At 40 bars, the first nine or so bars each draw from just one or two bins, and adjacent bars at the far left sometimes show identical heights.

Increasing the FFT size would sharpen the bass resolution, but each slice would then cover a longer stretch of time, making the bars react later to drum hits. 2,048 samples is a common balance between the two. If the identical left bars bother you, reduce the bar count: with fewer bars, each low bar spans more bins.

Step 5: Smooth over time

Raw per-frame spectra flicker, because music changes constantly and each slice is short. Before grouping into bars, each bin is blended with its own value from the previous frame. The Smoothing slider sets the blend: 0 uses only the new frame, 0.92 keeps 92% of the old value.

Smoothing happens on linear magnitudes per bin, before the bars are formed, so it affects both how fast a bar rises and how fast it falls. The same slider also limits how far a bar can climb in a single frame, which keeps noisy, frame-to-frame fluctuations from turning into visible flicker. All of these rates are defined in time, so they behave the same at 24, 30 or 60 fps.

Step 6: Apply the tilt correction

Play almost any music through an untouched spectrum display and the left side towers while the right side barely moves. That is not a display error. In most music, energy falls off as frequency rises: a kick drum and bass line carry far more energy than a hi-hat, even when the hi-hat sounds just as clear.

A display that shows this faithfully looks like a staircase descending to the right, and the treble bars feel dead. The High-frequency boost slider corrects for it by adding a fixed number of decibels per octave above 30 Hz. The lowest bar gets nothing; each octave higher gets more. At the default 3.5 dB per octave, the highest bar receives roughly 30 dB of boost.

This is similar to the slope correction some audio analyzers offer for viewing mixes, and the right value depends on the material. Bright, dense music needs less tilt; dark, rolled-off music needs more.

Step 7: Map to bar heights

After tilt, each bar has a value in decibels. Three more steps turn that into a height on screen:

  1. Sensitivity adds a fixed number of decibels to every bar.
  2. Floor and a reference ceiling of −18 dB define the visible range. A value at Floor maps to zero height, and values above it rise linearly in decibels up to about 72% of the maximum height. Above that point a soft ceiling takes over: louder values keep rising but more and more gently, approaching full height without ever being cut off. This keeps loud bars from all stopping at exactly the same height and forming a flat line across the top. On top of that, an automatic headroom stage watches how far the loudest bar overshoots the ceiling. When a track is mastered very loud, it shifts the whole visible range up by that amount, rising quickly and settling back over a few seconds, so the differences between bars are preserved instead of being squashed against the top. Quieter tracks that stay below the ceiling are not affected.
  3. Decay controls the fall. If the new height is lower than the previous one, the bar falls only to the previous height multiplied by Decay. If it is higher, the bar climbs toward it at a rate set by Smoothing. Small changes in either direction are followed more gently than large ones, which removes the fine shiver you would otherwise see when a sound wobbles slightly from frame to frame, while kicks and other large changes still come through at full speed.

The result is a number between 0 and 1 per bar, multiplied by the Max height setting and drawn as a rounded rectangle.

Why this order matters

Because only steps 1–3 run during analysis and steps 4–7 run at draw time, you can change bar count, smoothing, tilt, sensitivity, floor, and decay while a track plays, and the export will use exactly what you set. The one setting that requires re-analysis is frame rate, because it changes how many slices are taken per second of audio.

Open the visualizer →