Broadcast loudness is one number for the whole programme: its integrated loudness, measured to ITU-R BS.1770. Europe delivers at −23 LUFS (EBU R128), the US at −24 LKFS (ATSC A/85), and both cap the true peak. Measuring it takes one command or one dropped file. Hitting it takes one gain change, then a second measurement to prove it.
A 12-second track, 6.7 LU louder than the EBU R128 target.
The targets, from the documents themselves:
| Spec | Integrated loudness | True peak | Tolerance |
|---|---|---|---|
| EBU R 128-2023 | −23.0 LUFS | at most −1 dBTP | ±1.0 LU only where the target is "not achievable practically" (live); ±0.2 LU for measurement error in QC |
| ATSC A/85:2026-07 | −24 LKFS | below −2 dBTP | about ±2 dB for measurement variation, "not targeted to the high or low side" |
These are the defaults for content delivered without other arrangements. A broadcaster's own delivery spec can be stricter: if you were sent one, it wins.
The ebur128 filter is an EBU R128 meter. peak=true adds the true peak, and
framelog=quiet drops the reading it otherwise prints every 100 ms, so you get
only the summary at the end:
ffmpeg -hide_banner -nostats -i melody.wav -af ebur128=peak=true:framelog=quiet -f null -
Integrated loudness:
I: -16.3 LUFS
Threshold: -26.4 LUFS
Loudness range:
LRA: 0.5 LU
Threshold: -36.1 LUFS
LRA low: -16.5 LUFS
LRA high: -16.0 LUFS
True peak:
Peak: -1.0 dBFS
I is the number the spec means. Peak is the true peak, in dBTP whatever the
label says. Install with brew install ffmpeg on a Mac or apt install ffmpeg
on Debian and Ubuntu. It reads a video's soundtrack the same way: give it the
.mp4.
Bring it to the target
Integrated loudness moves decibel for decibel with gain. The track measured −16.3 LUFS and the target is −23, so it needs −6.7 dB:
ffmpeg -hide_banner -loglevel error -i melody.wav -af volume=-6.7dB -ar 48000 melody-r128.wav
Then measure the new file with the same ebur128 command:
Integrated loudness:
I: -23.0 LUFS
...
True peak:
Peak: -7.7 dBFS
On target, and the true peak went down by the same 6.7 dB, well under −1 dBTP.
For ATSC A/85 the gain is the target minus the reading in the same way:
−24 − (−16.3) = −7.7 dB. -ar 48000 writes the 48 kHz that broadcast
delivery expects.
Lowering the level can't push the peak over the limit. Raising it can. If the
gain you need would put the true peak above −1 dBTP (or −2 for A/85), plain gain
is not enough: the peaks have to be limited, which is what loudnorm below does
when it can't stay linear.
The two-pass loudnorm route
FFmpeg's loudnorm filter does the measuring and the gain in one tool. The
first pass measures and prints JSON:
ffmpeg -hide_banner -nostats -i melody.wav -af loudnorm=I=-23:TP=-1:LRA=7:print_format=json -f null -
{
"input_i" : "-16.57",
"input_tp" : "-1.00",
"input_lra" : "1.60",
"input_thresh" : "-26.69",
"output_i" : "-23.08",
"output_tp" : "-7.87",
"output_lra" : "0.60",
"output_thresh" : "-33.19",
"normalization_type" : "dynamic",
"target_offset" : "0.08"
}
The second pass feeds the input_* values back as measured_*, with
linear=true so it applies one gain change instead of compressing the audio:
ffmpeg -hide_banner -nostats -i melody.wav -af loudnorm=I=-23:TP=-1:LRA=7:measured_I=-16.57:measured_TP=-1.00:measured_LRA=1.60:measured_thresh=-26.69:offset=0.08:linear=true:print_format=summary -ar 48000 melody-loudnorm.wav
Input Integrated: -16.3 LUFS
Input True Peak: -1.0 dBTP
Input LRA: 0.6 LU
Input Threshold: -26.4 LUFS
Output Integrated: -22.7 LUFS
Output True Peak: -7.4 dBTP
Output LRA: 0.7 LU
Output Threshold: -32.8 LUFS
Normalization Type: Linear
Target Offset: -0.3 LU
It landed at −22.7, and ebur128 on the output agrees. The first pass had read
the 12-second track as −16.57 where ebur128 (and the second pass itself) read
−16.3, so the gain came out 0.3 dB short. On the same track looped to two
minutes, the first pass read −16.4: the gap shrinks on longer material, but
measure the output either way. 0.3 LU is outside R128's ±0.2 LU QC tolerance, so
on this file the plain volume route was the one that delivered.
Two more things about loudnorm. Without linear=true, or when the target
can't be reached linearly, it compresses dynamically. And in dynamic mode it
writes 192 kHz audio: our test output came out at 192000 Hz. Always give it
-ar.
| Route | Integrated | True peak | Off target |
|---|---|---|---|
| Original | −16.3 LUFS | −1.0 dBTP | +6.7 LU |
volume=-6.7dB |
−23.0 LUFS | −7.7 dBTP | 0.0 LU |
loudnorm, two-pass, linear |
−22.7 LUFS | −7.4 dBTP | +0.3 LU |
Measured with FFmpeg 7.1.5 (Debian). The browser meter's code, run on the same track, read −16.3 LUFS integrated and −1.0 dBTP true peak.
In the browser
- Open the LUFS Loudness Meter and drop in the file: audio, or a video with a soundtrack.
- Under Delivery target, pick Broadcast (EBU R128) · -23 LUFS. On a phone it is behind the Settings button.
- Read Integrated loudness. The line under it says how far you are, for example +6.7 LU against target: that is the gain to take off.
- Check True peak against your spec's ceiling. Sample peak is next to it; true peak can be the higher of the two, and it is the one that counts.
- Copy results copies the file name and every reading as text, for a delivery note.
The same track against the default target, Spotify's −14 LUFS.
The meter is built to BS.1770-4 and decodes everything at 48 kHz, where the recommendation's filter coefficients apply exactly. Its tests reproduce EBU Tech 3341 cases 1 to 6 to within 0.1 LU and the Tech 3342 loudness range cases. There is no ATSC entry in the target list: for A/85, choose No target and read the integrated figure against −24 yourself.
The browser meter analyses at most the first 30 minutes of a file and tells you when it has stopped there. Integrated loudness is defined over the whole programme, so for a longer one, measure with FFmpeg.
Once the integrated reading is on target and the true peak is under the
ceiling, the file is ready to deliver. Keep the ebur128 summary of the final
file: it is the figure a broadcaster's QC will check against.