32blogby Studio Mitsu

FFmpeg Audio Normalization: The Complete loudnorm Guide

Learn how to normalize audio with FFmpeg's loudnorm filter. Covers single-pass and two-pass EBU R128 normalization, platform-specific LUFS targets, batch processing with ffmpeg-normalize, and more.

by omitsu10 min read
FFmpegCLIaudionormalizationencodingcommands

This article contains affiliate links.

On this page

Ever had one video clip that's whisper-quiet followed by another that blows out your speakers? Audio loudness inconsistency is one of the most common complaints in video production — and one of the most asked-about topics on r/ffmpeg and audio engineering forums.

FFmpeg's loudnorm filter normalizes audio to a target loudness level using the EBU R128 standard. The basic command is ffmpeg -i input.mp4 -af loudnorm=I=-16:TP=-1.5:LRA=11 output.mp4 — this brings your audio to -16 LUFS, which works for YouTube, Spotify, and most streaming platforms.

This guide covers everything from quick single-pass normalization to precise two-pass workflows, platform-specific loudness targets, and batch processing with ffmpeg-normalize.

What you'll learn

  • How the loudnorm filter works and what each parameter means
  • Single-pass vs. two-pass normalization and when to use each
  • Loudness targets for YouTube, Spotify, Apple, TikTok, and broadcast
  • How to batch-normalize with ffmpeg-normalize
  • How loudnorm compares to volume, dynaudnorm, and compand

How the loudnorm Filter Works

The loudnorm filter measures perceived loudness using the ITU-R BS.1770 algorithm (K-weighted filtering with gating) and adjusts gain to hit a target level. Internally, it upsamples to 192 kHz for accurate true-peak detection, then applies a loudness-tuned AGC with a 100 ms lookahead true-peak limiter.

Single-pass normalization

The simplest way to normalize audio:

bash
ffmpeg -i input.mp4 -af loudnorm=I=-16:TP=-1.5:LRA=11 output.mp4

What each parameter does:

ParameterMeaningRangeDefault
ITarget integrated loudness (LUFS)-70.0 to -5.0-24.0
TPMaximum true peak (dBTP)-9.0 to 0.0-2.0
LRATarget loudness range (LU)1.0 to 50.07.0

Single-pass mode uses dynamic normalization — the AGC adjusts gain in real time as it processes the audio. This works well for quick jobs and live streaming, but the result may not be perfectly linear because the filter hasn't seen the entire file yet.

Measuring loudness before normalization

Before normalizing, it's useful to check the current loudness of your file:

bash
ffmpeg -i input.mp4 -af loudnorm=I=-16:TP=-1.5:LRA=11:print_format=json -f null -

This outputs JSON with the measured values:

json
{
  "input_i": "-27.61",
  "input_tp": "-4.47",
  "input_lra": "18.06",
  "input_thresh": "-39.20",
  "output_i": "-16.58",
  "output_tp": "-1.50",
  "output_lra": "14.78",
  "output_thresh": "-27.71",
  "normalization_type": "dynamic",
  "target_offset": "0.58"
}

The input_i tells you how far off the source is from your target. The normalization_type field shows whether linear or dynamic normalization was applied.

Two-Pass Normalization for Precision

Two-pass normalization first measures the entire file, then applies the exact correction. This enables linear normalization — a constant gain applied uniformly, preserving the original dynamics without any AGC coloring.

Pass 1: Measure

bash
ffmpeg -i input.mp4 -af loudnorm=I=-16:TP=-1.5:LRA=11:print_format=json -f null - 2>&1

Save the JSON output. You need input_i, input_tp, input_lra, input_thresh, and target_offset.

Pass 2: Apply

bash
ffmpeg -i input.mp4 -af loudnorm=I=-16:TP=-1.5:LRA=11:measured_I=-27.61:measured_LRA=18.06:measured_TP=-4.47:measured_thresh=-39.20:offset=0.58:linear=true -ar 48000 output.mp4

The measured_* parameters feed the first-pass measurements back into the filter. Setting linear=true requests linear normalization — a single gain value applied to the entire file.

Automating two-pass with a shell script

Parsing JSON and running two commands manually gets tedious. Here's a script that automates it:

bash
#!/bin/bash
# two-pass-normalize.sh — Normalize audio to target LUFS
INPUT="$1"
OUTPUT="$2"
TARGET_I=${3:--16}
TARGET_TP=${4:--1.5}
TARGET_LRA=${5:-11}

# Pass 1: measure
JSON=$(ffmpeg -i "$INPUT" -af "loudnorm=I=$TARGET_I:TP=$TARGET_TP:LRA=$TARGET_LRA:print_format=json" -f null - 2>&1 | sed -n '/{/,/}/p')

# Extract measured values
measured_I=$(echo "$JSON" | grep '"input_i"' | sed 's/.*: "//;s/".*//')
measured_TP=$(echo "$JSON" | grep '"input_tp"' | sed 's/.*: "//;s/".*//')
measured_LRA=$(echo "$JSON" | grep '"input_lra"' | sed 's/.*: "//;s/".*//')
measured_thresh=$(echo "$JSON" | grep '"input_thresh"' | sed 's/.*: "//;s/".*//')
offset=$(echo "$JSON" | grep '"target_offset"' | sed 's/.*: "//;s/".*//')

# Pass 2: apply
ffmpeg -i "$INPUT" -af "loudnorm=I=$TARGET_I:TP=$TARGET_TP:LRA=$TARGET_LRA:measured_I=$measured_I:measured_LRA=$measured_LRA:measured_TP=$measured_TP:measured_thresh=$measured_thresh:offset=$offset:linear=true" -ar 48000 "$OUTPUT"

Usage:

bash
chmod +x two-pass-normalize.sh
./two-pass-normalize.sh input.mp4 output.mp4 -16 -1.5 11

For more advanced scripting with Python, see Automating FFmpeg with Python.

Platform-Specific Loudness Targets

Different platforms normalize audio to different targets. Delivering audio that already meets the target prevents the platform from applying its own normalization, which can introduce artifacts.

PlatformTarget LUFSTrue PeakNotes
YouTube-14 LUFS-1.5 dBTPApplies normalization on playback
Spotify-14 LUFS-1 dBTPUses ReplayGain-style normalization
Apple Music-16 LUFS-1 dBTPSound Check feature
Apple Podcasts-16 LUFS-1 dBTPSame as Apple Music
Amazon Music-14 LUFS-2 dBTPOnly turns volume down, never up
TikTok~-14 LUFS-1 dBTPNo official spec; industry consensus
Instagram Reels~-14 LUFS-1 dBTPMeta manages via xHE-AAC
EBU R128 (broadcast)-23 LUFS-1 dBTPEuropean broadcast standard
ARIB TR-B32 (Japan)-24 LKFSper specJapanese broadcast standard (since 2012)

Source: EBU Tech R128, platform developer documentation, and industry measurements.

Ready-to-use commands for common targets

YouTube / Spotify / TikTok (-14 LUFS):

bash
ffmpeg -i input.mp4 -af loudnorm=I=-14:TP=-1.5:LRA=11 output.mp4

Apple Music / Podcasts (-16 LUFS):

bash
ffmpeg -i input.mp4 -af loudnorm=I=-16:TP=-1:LRA=11 output.mp4

Broadcast (EBU R128, -23 LUFS):

bash
ffmpeg -i input.mp4 -af loudnorm=I=-23:TP=-1:LRA=7 output.mp4

Batch Normalization with ffmpeg-normalize

For normalizing multiple files, ffmpeg-normalize is a Python CLI that wraps FFmpeg's loudnorm filter with sensible defaults and built-in two-pass support.

Installation

bash
pip install ffmpeg-normalize

Basic usage

bash
# Normalize a single file to -16 LUFS (two-pass, EBU R128)
ffmpeg-normalize input.mp4 -o output.mp4

# Normalize to YouTube's -14 LUFS
ffmpeg-normalize input.mp4 -o output.mp4 -t -14

# Normalize all MP4 files in a directory
ffmpeg-normalize *.mp4 -of normalized/ -ext mp4

Built-in presets (v1.36+)

bash
# Podcast (AES streaming standard)
ffmpeg-normalize input.wav -o output.wav --preset podcast

# Music (RMS-based)
ffmpeg-normalize input.wav -o output.wav --preset music

# Streaming video
ffmpeg-normalize input.mp4 -o output.mp4 --preset streaming-video

Album normalization (v1.35+)

To keep relative loudness between tracks while bringing the album to a target level:

bash
ffmpeg-normalize track1.wav track2.wav track3.wav -of album/ -ext wav --album

For a deeper dive into batch workflows, see Automating FFmpeg with Python.

Comparing Audio Filters: loudnorm vs. Others

"Should I use loudnorm or just volume?" — this question comes up constantly on Stack Overflow and audio production forums. FFmpeg has several audio adjustment filters. Here's when to use each:

FilterMethodBest forTrade-off
loudnormEBU R128 perceptual loudnessStreaming, broadcast, podcastsMost accurate; upsamples to 192 kHz internally
volumeFixed gain multiplierSimple adjustments ("make it louder")No loudness awareness; can clip
dynaudnormPer-chunk dynamic gainConversation, interviews~4x faster than loudnorm; no standard compliance
compandDynamic range compressionHeavy compression effectsCan distort; complex syntax

When to use volume instead of loudnorm

If you just need to bump the volume by a known amount, volume is simpler and faster:

bash
# Increase by 6 dB
ffmpeg -i input.mp4 -af volume=6dB output.mp4

# Decrease by 3 dB
ffmpeg -i input.mp4 -af volume=-3dB output.mp4

Use volumedetect first to measure peak and mean volume:

bash
ffmpeg -i input.mp4 -af volumedetect -f null -

For more basic FFmpeg audio commands, see the audio section in FFmpeg Commands: A Practical Guide.

When to use dynaudnorm

dynaudnorm adjusts gain on a per-chunk basis (default ~8 seconds). It's faster than loudnorm and good for making quiet speech audible, but it doesn't follow any standard:

bash
ffmpeg -i input.mp4 -af dynaudnorm=f=200:g=5 output.mp4
  • f = frame length in milliseconds (default 500)
  • g = Gaussian filter size (default 31)

Frequently Asked Questions

What's the difference between LUFS and LKFS?

They're the same measurement. LUFS (Loudness Units Full Scale) is the EBU term; LKFS (Loudness K-weighted Full Scale) is the ITU term. -14 LUFS = -14 LKFS.

Why does my normalized audio sound quieter than the original?

Loudness normalization targets perceived loudness, not peak level. A heavily compressed (dynamically) source may have a high peak but low perceived loudness. After normalization, the peak may be lower even though perceived loudness matches the target.

Should I normalize before or after compression/encoding?

Normalize before final encoding. If you normalize after lossy compression, you're applying gain to already-degraded audio. Normalize the highest quality source you have, then encode.

Can I normalize audio without re-encoding the video?

Yes. Copy the video stream and only re-encode audio:

bash
ffmpeg -i input.mp4 -c:v copy -af loudnorm=I=-16:TP=-1.5:LRA=11 -c:a aac -b:a 192k output.mp4

What sample rate should I use for the output?

48 kHz is the standard for video. For music-only files, match the source sample rate. The loudnorm filter internally upsamples to 192 kHz regardless, so adding -ar 48000 to the output is recommended to avoid unexpected sample rate changes.

Does loudnorm work with surround sound (5.1/7.1)?

Yes. The filter handles multi-channel audio, but set dual_mono=true if your source is mono content in a stereo container, so the measurement accounts for the single-channel playback context.

How do I verify the result?

Run loudnorm in measurement mode on the output file:

bash
ffmpeg -i output.mp4 -af loudnorm=print_format=json -f null -

Check that input_i matches your target. A deviation of ±0.5 LU is normal.

Wrapping Up

Here's a quick reference for the most common normalization tasks:

TaskCommand
Quick normalize to -16 LUFSloudnorm=I=-16:TP=-1.5:LRA=11
YouTube/Spotify (-14 LUFS)loudnorm=I=-14:TP=-1.5:LRA=11
Apple Podcasts (-16 LUFS)loudnorm=I=-16:TP=-1:LRA=11
Broadcast (EBU R128)loudnorm=I=-23:TP=-1:LRA=7
Measure only (no output)loudnorm=print_format=json -f null -
Keep video, normalize audio-c:v copy -af loudnorm=... -c:a aac
Batch normalizeffmpeg-normalize *.mp4 -of out/ -ext mp4

The loudnorm filter handles most normalization needs out of the box. For batch workflows or advanced automation, ffmpeg-normalize saves significant time. And for basic volume adjustments that don't need loudness standards compliance, the simpler volume filter works fine.

Kamatera

Enterprise-grade cloud VPS with global data centers

  • 13 data centers (US, EU, Asia, Middle East)
  • Starting at $4/month for 1GB RAM — pay-as-you-go
  • 30-day free trial available

Related articles: