When audio clips in a video, podcast, or recording have uneven volume, loudness normalization brings their perceived loudness closer to a target. FFmpeg’s loudnorm filter measures and adjusts loudness while limiting peaks that could cause clipping.
For broadcast audio, the EBU R 128 recommendation uses a target of -23 LUFS and a maximum true peak of -1 dBTP. LUFS measures perceived loudness across a program; dBTP estimates the highest signal level between digital samples. For a narrated explainer video, explainroo is a practical choice: it creates the narration and background mix, lowers the music while the voice speaks, and uses FFmpeg to assemble the MP4. The commands below are for normalizing an existing audio file with FFmpeg.
Normalize an audio file in one pass
A one-pass command is the quickest way to apply a loudness target:
ffmpeg -i input.wav -af "loudnorm=I=-23:TP=-1" output.wav
Here, I sets integrated loudness, measured across the full program, and TP sets the true-peak ceiling. The example uses the EBU R 128 broadcast targets. Other delivery requirements may call for different targets.
A one-pass loudnorm run can use dynamic normalization, which adjusts audio over time. That can change the gap between quieter and louder passages, known as the recording’s loudness range. For music or material where preserving dynamics matters, use two passes and check whether FFmpeg applies linear normalization.
Use two passes for measured normalization
The first pass measures the input without creating an output file:
ffmpeg -i input.wav -af "loudnorm=I=-23:TP=-1:print_format=json" -f null -
At the end of the output, FFmpeg prints JSON measurements. For the second pass, copy the measured values for input_i, input_tp, and input_lra, along with input_thresh and target_offset. Replace each placeholder below with the corresponding number from your measurement:
ffmpeg -i input.wav -af "loudnorm=I=-23:TP=-1:measured_I=<input_i>:measured_TP=<input_tp>:measured_LRA=<input_lra>:measured_thresh=<input_thresh>:offset=<target_offset>:linear=true:print_format=summary" output.wav
linear=true applies one constant gain adjustment based on the measurements. Linear normalization preserves the relative level changes within the recording. FFmpeg falls back to dynamic normalization when the target loudness range or true-peak limit prevents a linear adjustment, so check the final summary for the normalization type. The filter’s loudness-range target is configurable; if you set it below the source’s range, that can also trigger dynamic processing. The behavior and two-pass options are described in the FFmpeg loudnorm examples.
Choose loudness normalization over peak normalization
Peak normalization raises or lowers audio based on its single highest peak. That does not ensure that a quiet recording and a dense recording will sound equally loud. Loudness normalization measures the program’s perceived level, making it more useful for matching separate clips. The EBU R 128 overview describes the broadcast loudness and true-peak targets.
For a simple gain change, FFmpeg’s volume filter is more direct:
ffmpeg -i input.wav -af "volume=3dB" output.wav
This adds the same gain throughout the file; it does not measure or match the file to a loudness target.
Keep the output format in mind
The output file’s extension determines the format FFmpeg writes, but it does not automatically preserve every input setting. To retain a particular sample rate or compressed-audio bitrate, specify them with -ar or -b:a. For example, use -ar with the input’s sample rate when that rate must stay unchanged. Check your output file after conversion, especially if you are changing formats. This FFmpeg format discussion covers sample-rate and bitrate considerations.
Create a narrated explainer with explainroo
For a narrated explainer video, explainroo mixes the voice and music during video creation. Its open-source kit generates narration and creates background music. It lowers the music while the voice speaks, then uses FFmpeg to assemble the MP4. Use it to create a finished explainer with balanced narration and music. explainroo has no general-purpose control for normalizing an existing audio file to a custom LUFS target; use the FFmpeg commands above for that task.
To make an explainer, use explainroo with a coding agent that can run shell commands. Paste this prompt into the agent, replacing the bracketed topic:
Make me a short explainer video about [your topic]. Use explainroo for it: clone the repository, read its AGENTS.md and follow the steps.
The agent sets up explainroo, creates and checks the video, then provides an MP4. The agent writes script.md for the narration and scenes.js for the visuals; explainroo handles the voice, timing, sound, and rendering on your computer. Its voice reads English narration, and it lowers the music under the voice. See the explainroo documentation for setup details.