By Tania Alwan
If you have ever uploaded a song and wondered why it sounds quieter on Spotify than in your DAW, the answer is LUFS. Streaming services do not play files at whatever level they were mastered. They measure loudness in LUFS and turn tracks up or down to a reference. This post explains what LUFS measures, how the measurement works under the hood, and shows a short Python script that measures and normalizes a WAV file.
What LUFS means
LUFS stands for Loudness Units relative to Full Scale. It is defined by the ITU-R BS.1770 recommendation, and the same unit is called LKFS in some broadcast documents.
A few things to keep in mind:
- It is a negative number for normal audio: -14 LUFS is louder than -20 LUFS.
- One LU (loudness unit) corresponds to one decibel of change.
- Unlike a peak meter, it tries to reflect how loud something sounds, not just how high the waveform goes.
Peak level vs loudness
Two tracks can both peak at -1 dBFS and still sound very different in volume. A heavily compressed pop master keeps its level near the peak most of the time; a quiet acoustic recording has occasional peaks and a lot of soft passages. Peak meters cannot see that difference. LUFS can, because it measures energy over time.
How the measurement works
The BS.1770 algorithm is surprisingly simple:
- K-weighting: the signal goes through a filter that reduces very low frequencies and slightly boosts high frequencies, roughly approximating how we hear loudness.
- Mean square per block: the filtered audio is cut into 400 ms blocks with 75% overlap, and the energy of each block is computed (channels are summed with weights).
- Absolute gate: blocks quieter than -70 LUFS are ignored, so silence does not drag the result down.
- Relative gate: blocks more than 10 LU below the average of the remaining blocks are ignored too, so long quiet intros do not dominate.
- Integrated loudness: the average of the surviving blocks, converted to LUFS.
You will also see momentary loudness (400 ms window) and short-term loudness (3 s window) on meters. Integrated loudness is the one streaming platforms care about.
The reference levels you will meet
- Spotify: its artist support page says it adjusts tracks to -14 LUFS by default, with -11 (Loud) and -19 (Quiet) options for Premium listeners. It recommends keeping true peak below -1 dBTP.
- Broadcast: EBU R128 in Europe targets -23 LUFS; the US ATSC A/85 recommendation uses -24 LKFS.
- Other services: targets differ and change over time, so check each platform's current documentation instead of trusting a number from a forum.
Normalization is applied on playback: a master louder than the reference gets turned down, so squashing a mix to be "louder" mostly costs you dynamics.
Python example: measure and normalize a WAV file
This uses pyloudnorm, an open-source implementation of BS.1770, plus soundfile and NumPy.
pip install pyloudnorm soundfile numpy
import numpy as np
import soundfile as sf
import pyloudnorm as pyln
# 1. Make a 10-second test file: a 440 Hz tone at moderate level
rate = 48000
t = np.arange(10 * rate) / rate
tone = 0.25 * np.sin(2 * np.pi * 440 * t)
sf.write("test_tone.wav", tone, rate)
# 2. Load any WAV file and measure integrated loudness (ITU-R BS.1770)
data, rate = sf.read("test_tone.wav")
meter = pyln.Meter(rate) # K-weighting + gating
loudness = meter.integrated_loudness(data)
peak_db = 20 * np.log10(np.max(np.abs(data)))
print(f"Integrated loudness: {loudness:.1f} LUFS")
print(f"Sample peak: {peak_db:.1f} dBFS")
# 3. Normalize to -14 LUFS (a common streaming reference)
target = -14.0
normalized = pyln.normalize.loudness(data, loudness, target)
new_peak = 20 * np.log10(np.max(np.abs(normalized)))
print(f"Gain applied: {target - loudness:+.1f} dB, new sample peak: {new_peak:.1f} dBFS")
if new_peak > -1.0:
print("Warning: peaks are too hot, use a limiter instead of plain gain")
sf.write("test_tone_-14LUFS.wav", normalized, rate)
On the test tone, it prints:
Integrated loudness: -15.8 LUFS
Sample peak: -12.0 dBFS
Gain applied: +1.8 dB, new sample peak: -10.3 dBFS
Replace test_tone.wav with your own mix to check it. Two caveats:
- This script measures sample peak, not true peak. True peak meters oversample to catch peaks between samples, so real inter-sample peaks can be a bit higher.
- Plain gain normalization cannot make a quiet track louder without raising its peaks. If the warning triggers, you need a limiter in your mastering chain, not more gain.
Where clean audio fits in
Loudness meters measure everything in the file, including hiss, hum and room noise. If a vocal recording is noisy, normalizing it simply makes the noise louder too. Clean the source first: a noise gate or Audacity's Noise Reduction for simple cases, iZotope RX for detailed repair, or an AI tool such as the Voicestars noise remover when you want a quick pass. Then measure.
Takeaways
- LUFS measures perceived loudness over time; dBFS peaks do not.
- BS.1770 = K-weighting + 400 ms blocks + two gates.
- Streaming services normalize on playback, so aim for a good-sounding master with safe peaks, not the loudest one.
-
pyloudnormgives you the same measurement in a few lines of Python.
Disclosure: this article was written in partnership with Voicestars.
Top comments (0)