Loudness Normalization for Streaming: EBU R128, -16 LUFS, and Why Viewers Should Never Touch the Volume
Integrated loudness, true peak limits, and how ingest-time normalization ends the 'why is this video so loud' complaint — plus FFmpeg commands that actually work.
Users don’t experience video quality first — they experience loudness first. A catalog where every video plays at a different level is a catalog where every user is riding the volume control and blaming your platform for it. Loudness normalization is the fix, and it’s a solved problem — if you implement it at ingest, not in the player.
The Vocabulary That Matters
| Term | What It Measures | Why It Matters |
|---|---|---|
| LUFS (integrated) | Perceived loudness over the full duration | The normalization target |
| True Peak (dBTP) | Inter-sample peak, not just sample peak | Prevents codec clipping |
| LRA (LU range) | Dynamic range within the program | Whether quiet parts stay quiet |
| Short-term loudness | 3-second sliding window | Catches momentary jumps |
The Target: -16 LUFS Integrated, -1.0 dBTP
-16 LUFS is the streaming sweet spot — loud enough to feel full, quiet enough to leave headroom for mobile speakers and Bluetooth compression. Broadcast uses -23 LUFS (EBU R128) or -24 LKFS (ATSC A/85); streaming platforms converge on -16 (Apple) or -14 (Spotify). Pick -16 for speech-first content — it’s the safer middle.
Ingest-Time Normalization with FFmpeg
The correct approach is two-pass: measure first, then apply a filtered gain with true-peak limiting.
# Pass 1 — measure
ffmpeg -i input.mp4 -af loudnorm=I=-16:TP=-1.0:LRA=11:print_format=json -f null -
# Pass 2 — apply with measured values (the important part)
ffmpeg -i input.mp4 \
-af "loudnorm=I=-16:TP=-1.0:LRA=11:measured_I=<X>:measured_TP=<Y>:measured_LRA=<Z>:measured_thresh=<W>" \
-c:v copy -c:a aac -b:a 160k output.mp4
Single-pass loudnorm works but is worse — it estimates dynamics on the fly and can pump during quiet sections. Two-pass is deterministic.
“Normalization isn’t making everything loud — it’s making everything consistent. The catalog where every video starts at the same perceived volume is the one nobody ever thinks about, which is the point.”
What Happens in the Player
Do normalization at ingest, not playback. Player-side normalization (ReplayGain-style) requires analyzing every stream before playback, which you can’t do reliably on a live or adaptive stream. The player should play what’s delivered; the delivered audio should already be correct.
Our loudness targets per content type, the two-pass FFmpeg pipeline, and the QC checks that catch normalization failures are documented in the loudness normalization implementation guide.