
Albums · 21 August 2026 · 6 min
How loud should each song on an album be? Measure the record as a set
Songs on an album don’t all need to hit the same LUFS. They need to sound right in running order and each one has to be legal on its own. In practice, keep the songs that should feel equally loud within a LU or two of each other, let intros and ballads sit lower on purpose, and keep every true peak under −1 dBTP. The way to check that is to measure the whole record as a set, not one bounce at a time.
Most album loudness problems aren’t about any single master. Each song got finished on a different night, with a different limiter setting and a different idea of “loud enough.” Each one sounds fine solo. Then you play the record top to bottom and the closer jumps out of the speakers, or the ballad is so quiet people skip it. You only see that when you look at all the numbers side by side.
Does every track on an album need to be −14 LUFS?
No, and the reason is in how streaming plays albums back. Spotify’s loudness normalization page says it normalizes an entire album at the same time, “so gain compensation doesn’t change between tracks,” and the softer tracks stay as soft as you intended. When someone shuffles or plays a song in a playlist, it adjusts each track on its own instead.
So your record has to work two ways. In album mode, the level differences you master in are exactly what listeners hear. In playlist mode, every track gets pulled toward −14 LUFS on its own, and a quiet track is only lifted as far as its true peak allows (Spotify keeps 1 dB of headroom). The older ReplayGain standard for music players works the same way, with a separate “album gain” that keeps the volume differences between tracks.
That means −14 isn’t a number each song has to hit. It’s roughly where streaming will put the album as a whole. What you control is how the songs sit next to each other.
What does a consistent album look like in numbers?
Here are the four things worth measuring for each track, on the same meter:
- Integrated LUFS. The loudness of the whole song, measured the ITU-R BS.1770 way.
- True peak. The level between the samples, which lossy encoders have to reproduce. Keep it under −1 dBTP, and under −2 dBTP for songs louder than −14 LUFS, as Spotify recommends.
- Loudness range (LRA). How much the loudness moves within the song, defined in EBU Tech 3342 as the spread between the 10th and 95th percentile of short-term loudness. A ballad with a big build will have a wider range than a wall-to-wall club track.
- The step from the previous track. This is the one listeners actually feel. Two songs that are meant to feel equally loud shouldn’t be more than a LU or two apart.
How do I check a whole album at once?
Here’s a script that measures every file in a folder in running order. It needs Python 3, NumPy and FFmpeg, and it only reads your files. It also estimates how each track will play on Spotify in playlist mode and in album mode. Spotify doesn’t publish exactly how it measures an album’s loudness, so the album column assumes the loudness of all tracks played back to back, like ReplayGain’s album gain. Treat that column as an estimate.
#!/usr/bin/env python3
"""album_check.py: measure an album (or EP, or beat tape) as one set.
Usage: python3 album_check.py FOLDER (WAV/FLAC/AIFF files, in running order by file name)
For each track: integrated loudness, loudness range (LRA), true peak (FFmpeg ebur128, ITU-R BS.1770 /
EBU Tech 3342), distance from the album's median loudness, the step from the previous track, and flags. Then a rough picture of playback
on Spotify: shuffle/playlist mode (each track adjusted to -14 LUFS, quiet tracks lifted only as far as
-1 dBTP allows) versus album mode (one gain for the whole album). Spotify doesn't publish how it measures
an album's loudness, so album mode here ASSUMES the loudness of all tracks played back to back, the way
ReplayGain's album gain works. Treat that column as an estimate. Your files are only read.
Needs: Python 3, NumPy, FFmpeg on PATH."""
import re, subprocess, sys, tempfile, os
from pathlib import Path
import numpy as np
def decode(p):
raw = subprocess.run(["ffmpeg", "-v", "error", "-i", str(p), "-f", "f32le", "-ac", "2", "-ar", "48000", "-"],
capture_output=True, check=True).stdout
return np.frombuffer(raw, np.float32).reshape(-1, 2)
def meter_file(p):
err = subprocess.run(["ffmpeg", "-hide_banner", "-nostats", "-i", str(p), "-af", "ebur128=peak=true", "-f", "null", "-"],
capture_output=True, text=True).stderr
g = lambda pat: float(re.findall(pat, err)[-1])
return g(r"I:\s+(-?[\d.]+) LUFS"), g(r"LRA:\s+([\d.]+) LU"), g(r"Peak:\s+(-?[\d.]+) dBFS")
def track_gain(I, TP, target=-14.0, ceiling=-1.0):
g = target - I
return min(g, ceiling - TP) if g > 0 else g
def main(folder):
files = sorted(p for p in Path(folder).iterdir() if p.suffix.lower() in (".wav", ".flac", ".aif", ".aiff"))
rows = [(p.stem, *meter_file(p)) for p in files]
med = float(np.median([r[1] for r in rows]))
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as fh: tmp = fh.name
subprocess.run(["ffmpeg", "-v", "error", "-y", "-f", "f32le", "-ar", "48000", "-ac", "2", "-i", "-", "-c:a", "pcm_f32le", tmp],
input=np.concatenate([decode(p) for p in files]).tobytes(), check=True)
AI, ALRA, ATP = meter_file(tmp); os.unlink(tmp)
ag = track_gain(AI, ATP)
print(f"album: {len(rows)} tracks, median {med:.1f} LUFS, whole album {AI:.1f} LUFS, max true peak {ATP:+.1f} dBTP\n")
print(f"{'track':14}{'LUFS':>7}{'LRA':>6}{'TP':>7}{'vs med':>8}{'step':>7} {'shuffle':>8}{'album*':>8} flags")
prev = None
for name, I, LRA, TP in rows:
flags = []
if TP > -1.0: flags.append("TP over -1")
if TP > -2.0 and I > -14.0 and TP <= -1.0: flags.append("loud: Spotify asks TP < -2")
if abs(I - med) > 4: flags.append("outlier: intended?")
step = "" if prev is None else f"{I - prev:+7.1f}"
if prev is not None and abs(I - prev) > 6: flags.append("big jump from last track")
prev = I
s = I + track_gain(I, TP); a = I + ag
print(f"{name:14}{I:7.1f}{LRA:6.1f}{TP:+7.1f}{I - med:+8.1f}{step:>7} {s:8.1f}{a:8.1f} {', '.join(flags)}")
print(f"\nshuffle = loudness each track plays at in a playlist; album* = estimated loudness in album mode (one gain, {ag:+.1f} dB)")
if __name__ == "__main__":
main(sys.argv[1])
To show it without anybody’s real record, we generated a synthetic six-track “album”: an ambient intro, an opener, a single, a deep cut, a ballad and a closer, each bounced at whatever level its session ended at. It’s the way a lot of records look the day before mastering. Here’s what the script printed:
$ python3 album_check.py album
album: 6 tracks, median -10.1 LUFS, whole album -9.6 LUFS, max true peak +1.9 dBTP
track LUFS LRA TP vs med step shuffle album* flags
01_intro -20.0 0.7 -5.8 -9.9 -15.2 -24.4 outlier: intended?
02_opener -8.4 0.5 +1.9 +1.7 +11.6 -14.0 -12.8 TP over -1, big jump from last track
03_single -8.9 0.3 +1.4 +1.2 -0.5 -14.0 -13.3 TP over -1
04_deep_cut -11.2 0.4 +0.4 -1.1 -2.3 -14.0 -15.6 TP over -1
05_ballad -17.5 0.6 -2.6 -7.4 -6.3 -15.9 -21.9 outlier: intended?, big jump from last track
06_closer -6.9 0.5 +0.9 +3.2 +10.6 -14.0 -11.3 TP over -1, big jump from last track
shuffle = loudness each track plays at in a playlist; album* = estimated loudness in album mode (one gain, -4.4 dB)
What is this album doing wrong?
Three things, and only one of them is about loudness taste.
Four tracks have illegal true peaks. The opener, single, deep cut and closer all sit over −1 dBTP, the worst at +1.9. Their highest samples were limited to −0.1 dBFS, which looks fine on a sample-peak meter, but the waveform between samples goes higher. That’s a problem for every song on its own, before you think about the album.
The steps are wild. Ballad to closer is a jump of 10.6 LU. In album mode the closer would come in at around −11.3 LUFS straight after a ballad at −21.9. Nobody intended that; the closer’s session just ended with a hot limiter. In playlist mode both get pulled to similar levels, so you’d never notice it from a single-track check.
The ballad is quieter than it needs to be. At −17.5 LUFS it sits 7.4 LU under the album’s median. Some of that may be the point. But in album mode it lands near −22, which is quiet enough that a listener in a car or on earbuds will turn it up and then get blasted by the closer.
The intro is flagged as an outlier too, and that one probably is intentional. A quiet intro is fine. The flag is there so you look at it and decide, not so you flatten it.
How do I fix album loudness without remastering everything?
Plan the lanes first, then fix the outliers. Write down where each song should sit in the running order: the loud songs together, the ballad a few LU under, the intro lower again. Then move only the tracks that miss their lane, and keep every true peak legal.
Here’s that plan applied to the synthetic album with gain only, capped so that loud tracks stay under −2 dBTP and the rest under −1:
#!/usr/bin/env python3
"""fix_album.py: SYNTHETIC demo of planning album lanes with gain only.
Each track gets a planned loudness; the gain is capped so true peak stays at or under -1.0 dBTP, or -2.0 dBTP
for tracks louder than -14 LUFS (Spotify's advice for loud masters).
Writes album_fixed/. In a real session you'd re-master into a true-peak limiter instead of only
turning down, but the plan (which track sits where) is the same. Needs NumPy and FFmpeg."""
import subprocess, os
from pathlib import Path
from album_check import meter_file
PLAN = {"01_intro": -18.0, "02_opener": -11.0, "03_single": -11.0, "04_deep_cut": -12.0, "05_ballad": -15.0, "06_closer": -11.5}
os.makedirs("album_fixed", exist_ok=True)
for p in sorted(Path("album").glob("*.wav")):
I, LRA, TP = meter_file(p)
g = PLAN[p.stem] - I
ceiling = -2.0 if I + g > -14.0 else -1.0
g = min(g, ceiling - TP)
subprocess.run(["ffmpeg", "-v", "error", "-y", "-i", str(p), "-af", f"volume={g:.2f}dB", "-c:a", "pcm_f32le", f"album_fixed/{p.name}"], check=True)
print(f"{p.stem:14} plan {PLAN[p.stem]:6.1f} gain {g:+5.1f} dB")
$ python3 fix_album.py && python3 album_check.py album_fixed
01_intro plan -18.0 gain +2.0 dB
02_opener plan -11.0 gain -3.9 dB
03_single plan -11.0 gain -3.4 dB
04_deep_cut plan -12.0 gain -2.4 dB
05_ballad plan -15.0 gain +1.6 dB
06_closer plan -11.5 gain -4.6 dB
album: 6 tracks, median -12.9 LUFS, whole album -13.2 LUFS, max true peak -1.0 dBTP
track LUFS LRA TP vs med step shuffle album* flags
01_intro -18.0 0.7 -3.8 -5.1 -15.2 -18.8 outlier: intended?
02_opener -12.3 0.5 -2.0 +0.6 +5.7 -14.0 -13.1
03_single -12.3 0.3 -2.0 +0.6 +0.0 -14.0 -13.1
04_deep_cut -13.6 0.4 -2.0 -0.7 -1.3 -14.0 -14.4
05_ballad -15.9 0.6 -1.0 -3.0 -2.3 -15.9 -16.7
06_closer -11.5 0.5 -3.7 +1.4 +4.4 -14.0 -12.3
shuffle = loudness each track plays at in a playlist; album* = estimated loudness in album mode (one gain, -0.8 dB)
Every true peak is now legal, the loud songs sit within about 2 LU of each other, and the ballad-to-closer step shrank from 10.6 LU to 4.4. The ballad couldn’t reach its planned −15 because its peaks hit the ceiling first, so it settled at −15.9, which is still a reasonable distance under the loud songs. The jump from intro to opener is still 5.7 LU, and that’s the drop we want.
In a real session you’d usually re-master the loud tracks into a true-peak limiter rather than just turning them down, which keeps more loudness for the same peak. The plan stays the same either way: decide where each song sits, then change only what misses.
What should I listen for once the numbers look right?
Numbers get you close, and your ears finish the job. Play the last 20 seconds of each song into the first 20 seconds of the next, in order, at one fixed monitor level. Listen for any step that makes you want to reach for the volume. Check the gaps and fades too, because the space between songs changes how big a level change feels (and if the album is gapless, lossy encoding can add gaps you didn’t master in).
Then play two songs on shuffle, from different spots in the album. If each one holds up next to the other at −14, you’re covered both ways.
- Should every song on an album be the same LUFS?
- No. Spotify plays an album with one gain for the whole record, so the differences you master in are the differences listeners hear. A ballad a few LU under the loud songs sounds intentional. What you want to avoid is accidental jumps, like a 10 LU step between two songs that should feel similar.
- Does Spotify normalize every track on an album separately?
- Only when tracks play outside the album. Spotify says it normalizes a whole album at once, so the gain doesn’t change between tracks, and adjusts tracks individually when you shuffle or play a playlist. That means each song needs to work on its own and in running order.
- How loud should a ballad be compared with the rest of the album?
- There’s no rule. A few LU under the main songs is a common starting point, because it sounds softer without making the listener reach for the volume. Check it in running order: listen to the end of the song before it, then the ballad. If the change feels like a mistake, it’s too big.
- How do I check loudness for a whole album at once?
- Measure every track with the same meter and put the numbers side by side: integrated LUFS, true peak and loudness range, plus the step from each song to the next. A batch script or a batch report tool does this in one pass, so you aren’t copying numbers by hand.