CoProducer Measure a mix
Cyan vocal waveforms in front of a magenta reverb haze over gold-veined black marble, the haze cut off below a thin gold filter line

Mix craft · 19 October 2026 · 8 min

Reverb making the mix muddy? Filter the return, not the vocal

NoDAW Labs · Lacquer Vol. 05

When reverb makes a mix muddy, the fix is usually on the return, not the vocal: a dark hall puts most of its energy below 1 kHz and keeps ringing there in the gaps between notes. High-pass the return around 250–400 Hz, give it enough pre-delay that the dry attack lands first, and time the decay to the song so the tail clears before the next line.

Here’s how it usually goes. The vocal sounds great soloed with its reverb. Then the beat comes in and the whole middle of the mix turns to fog. So you start cutting the vocal at 300 Hz, then the keys, then you try a different reverb, and somehow the vocal ends up thin and the mix is still cloudy.

The vocal was never the problem. Mute the reverb return and the fog goes with it. What you’re hearing is the reverb’s low-mids, smeared across time and parked in exactly the spaces the dry parts used to leave open. So fix it where it lives: on the return.

Why the mud lives in the return

A reverb return is a copy of everything you send it, stretched over a second or three. Two things make that copy heavier in the low-mids than the source.

Average it over a whole chorus and the extra low-mid looks tiny, which is why neither a meter nor an EQ on the mix bus flags it. The damage happens in the gaps: between syllables, between chord hits, in the half-beat before a downbeat. Those little dips are what make a mix sound open. Reverb fills them in.

We measured it on a synthetic ballad

To put numbers on it we built a test session at 48 kHz and 75 BPM: a vocal-like line (harmonics, formants, vibrato and short consonant bursts), piano-ish keys on every beat and a bass. The vocal and the keys (6 dB lower) both feed one synthetic stereo hall with a 2.8 s low decay, 2.0 s mids and 1.0 s highs. The return is set once, unfiltered, 8 dB under the dry vocal, and stays at that gain for every row unless it says level-matched. Synthetic test signals, so trust the shape more than the decimals.

On its own, that unfiltered return put 86% of its energy below 1 kHz and 13% in the 1–6 kHz range where you actually hear space and distance.

Return versionGap fill, 150–500 HzLow-mid contrastReturn 1–6 kHzReturn levelLow-mid tail
No reverb—18.0 dB———
Unfiltered+4.3 dB15.9 dB0.00.0−31.5 dB
HPF 150 Hz+4.1 dB16.0 dB0.0−0.2−32.6 dB
HPF 250 Hz+3.1 dB16.3 dB0.0−0.9−36.5 dB
HPF 350 Hz+2.1 dB16.6 dB0.0−1.7−40.5 dB
HPF 350 Hz, 24 dB/oct+2.0 dB16.5 dB0.0−1.5−44.0 dB
HPF 350 + LPF 7 kHz, level-matched+2.9 dB16.2 dB+1.60.0−38.8 dB
Low decay halved (2.8 → 1.4 s)+2.2 dB17.1 dB0.0−1.4−49.8 dB
HPF 350 + LPF 7 kHz + low decay halved+1.0 dB17.2 dB−0.1−2.7−56.9 dB

Gap fill is how much the return raises the mix’s 150–500 Hz energy in the short gaps between sung notes. Low-mid contrast is how much that band still rises and falls from moment to moment (the spread between loud and quiet 25 ms frames); lower means more filled in. Return 1–6 kHz and Return level are relative to the unfiltered return. Low-mid tail is the return’s 150–500 Hz energy half a beat to a beat and a half after the last note, relative to the dry vocal’s. Filters are 12 dB per octave unless marked.

Three things to take from that table into a session:

The bottom two rows are the other lever. Halving the low decay alone took the leftover tail from −31.5 to −49.8 dB, and with the filter it fell to −56.9: 25 dB less low-mid hanging over the next line, while the 1–6 kHz part moved by 0.1 dB. If your reverb has a bass multiplier or a low-frequency damping control, it is a mud control.

Pre-delay keeps the front of the word dry

Pre-delay doesn’t clean the low-mids (in this test the mix’s 150–500 Hz rise stayed between 0.0 and 0.3 dB whatever the pre-delay). What it buys is articulation. With the reverb arriving a beat-fraction late, the consonant and the attack of each note reach your ear dry, and the wash follows.

Pre-delayDry over wet, first 30 ms of each noteSame, 2.5–9 kHz (consonants)
0 ms+14.8 dB+16.8 dB
10 ms+15.5 dB+18.1 dB
25 ms (1/128 note at 75 BPM)+18.3 dB+20.8 dB
50 ms (1/64 note at 75 BPM)+34.9 dB+41.7 dB
100 ms (1/32 note at 75 BPM)+33.4 dB+39.4 dB

Once the pre-delay is longer than the attack you care about, the front of the note is effectively dry. Past that point more pre-delay stops helping the consonant and starts sounding like a slap. Somewhere between 20 and 60 ms is where most sung vocals land. Syncing it to a small note value keeps it from feeling rhythmically off.

Duck the return off the dry vocal

Put a compressor (or a ducker) on the reverb return and key its sidechain from the dry vocal. The reverb sinks while the singer sings and blooms in the spaces. We used a gentle setting: 6 dB at most, 5 ms attack, 250 ms release, on the filtered return with 25 ms of pre-delay.

Ducking doesn’t change the low-mid average much (+0.3 to +0.1 dB in this test). It’s a front-to-back tool: the vocal stays in front, the reverb still reads as big, it just stops talking over the singer.

Time the decay to the song

A tail that’s still ringing when the next phrase starts is mud you added on purpose. A quick way to get a starting point is to let the tempo pick the numbers:

#!/usr/bin/env python3
"""reverb_times.py: tempo-synced pre-delay and decay starting points.
    python3 reverb_times.py 72            # one tempo
    python3 reverb_times.py 72 140 120 85 # several
Decay is the note length minus the pre-delay, so the tail is done by the time
that note value has passed. These are starting points: set them, then listen."""
import sys

def note_ms(bpm, frac):                  # frac of a whole note, e.g. 1/16
    return 60000 / bpm * 4 * frac

for bpm in map(float, sys.argv[1:] or ["72"]):
    print(f"{bpm:g} BPM  (quarter = {note_ms(bpm, 1/4):.0f} ms)")
    pre = {n: note_ms(bpm, f) for n, f in (("1/128", 1/128), ("1/64", 1/64), ("1/32", 1/32))}
    print("  pre-delay  " + "   ".join(f"{n} = {v:.0f} ms" for n, v in pre.items()))
    for name, frac in (("1/4 note", 1/4), ("1/2 note", 1/2), ("1 bar", 1), ("2 bars", 2)):
        total = note_ms(bpm, frac)
        print(f"  decay to clear in {name:8s} {total/1000:5.2f} s total -> "
              f"{(total - pre['1/64'])/1000:4.2f} s decay with 1/64 pre-delay")

Run for four tempos we’ll use in the genre notes below:

$ python3 reverb_times.py 68 140 120 85
68 BPM  (quarter = 882 ms)
  pre-delay  1/128 = 28 ms   1/64 = 55 ms   1/32 = 110 ms
  decay to clear in 1/4 note  0.88 s total -> 0.83 s decay with 1/64 pre-delay
  decay to clear in 1/2 note  1.76 s total -> 1.71 s decay with 1/64 pre-delay
  decay to clear in 1 bar     3.53 s total -> 3.47 s decay with 1/64 pre-delay
  decay to clear in 2 bars    7.06 s total -> 7.00 s decay with 1/64 pre-delay
140 BPM  (quarter = 429 ms)
  pre-delay  1/128 = 13 ms   1/64 = 27 ms   1/32 = 54 ms
  decay to clear in 1/4 note  0.43 s total -> 0.40 s decay with 1/64 pre-delay
  decay to clear in 1/2 note  0.86 s total -> 0.83 s decay with 1/64 pre-delay
  decay to clear in 1 bar     1.71 s total -> 1.69 s decay with 1/64 pre-delay
  decay to clear in 2 bars    3.43 s total -> 3.40 s decay with 1/64 pre-delay
120 BPM  (quarter = 500 ms)
  pre-delay  1/128 = 16 ms   1/64 = 31 ms   1/32 = 62 ms
  decay to clear in 1/4 note  0.50 s total -> 0.47 s decay with 1/64 pre-delay
  decay to clear in 1/2 note  1.00 s total -> 0.97 s decay with 1/64 pre-delay
  decay to clear in 1 bar     2.00 s total -> 1.97 s decay with 1/64 pre-delay
  decay to clear in 2 bars    4.00 s total -> 3.97 s decay with 1/64 pre-delay
85 BPM  (quarter = 706 ms)
  pre-delay  1/128 = 22 ms   1/64 = 44 ms   1/32 = 88 ms
  decay to clear in 1/4 note  0.71 s total -> 0.66 s decay with 1/64 pre-delay
  decay to clear in 1/2 note  1.41 s total -> 1.37 s decay with 1/64 pre-delay
  decay to clear in 1 bar     2.82 s total -> 2.78 s decay with 1/64 pre-delay
  decay to clear in 2 bars    5.65 s total -> 5.60 s decay with 1/64 pre-delay

The decay knob on most reverbs is a 60 dB decay time (RT60). In a busy mix the last 30 dB or so of that tail is masked anyway, so a decay set to one bar sounds like it clears well before the bar ends. Set it from the table, then adjust by ear with the whole mix playing, not soloed.

Check what your return is actually doing

Solo and bounce the dry vocal and the reverb return (100% wet, same start). This shows where the return puts its energy next to the dry part:

#!/usr/bin/env python3
"""wet_dry_bands.py: where does your reverb return put its energy, next to the dry vocal?
Solo and bounce the dry vocal and the reverb return (return only, 100% wet), same start, then:
    python3 wet_dry_bands.py vocal_dry.wav reverb_return.wav"""
import sys, subprocess, numpy as np

def load(path):  # any format ffmpeg reads -> stereo float at 48 kHz (mono files are copied to both sides)
    raw = subprocess.run(["ffmpeg", "-v", "error", "-i", path, "-ac", "2", "-ar", "48000", "-f", "f32le", "-"],
                         capture_output=True, check=True).stdout
    return np.frombuffer(raw, np.float32).reshape(-1, 2).astype(np.float64)

def power(x, n):  # power spectrum summed over both channels (a stereo return is not folded to mono)
    return (np.abs(np.fft.rfft(x[:n], axis=0)) ** 2).sum(axis=1)

dry, wet = load(sys.argv[1]), load(sys.argv[2])
n = min(len(dry), len(wet)); f = np.fft.rfftfreq(n, 1 / 48000)
D, W = power(dry, n), power(wet, n)
bands = [("sub 20-150", 20, 150), ("low-mid 150-500", 150, 500), ("mid 500-1k", 500, 1000),
         ("presence 1-6k", 1000, 6000), ("air 6-16k", 6000, 16000)]
print(f"{'band':16s} {'return vs dry':>13s} {'share of return':>16s}")
for name, lo, hi in bands:
    m = (f >= lo) & (f < hi)
    ratio = f"{10*np.log10(W[m].sum() / D[m].sum()):+11.1f} dB" if D[m].sum() > 1e-4 * D.sum() else "   (dry ~none)"
    print(f"{name:16s} {ratio} {100 * W[m].sum() / W.sum():14.0f} %")
print(f"{'broadband':16s} {10*np.log10(W.sum() / D.sum()):+11.1f} dB")

On our session, first unfiltered, then with the 350 Hz high-pass and 7 kHz low-pass:

$ python3 wet_dry_bands.py vocal_dry.wav reverb_return.wav
band             return vs dry  share of return
sub 20-150          (dry ~none)              0 %
low-mid 150-500         -7.9 dB             43 %
mid 500-1k              -8.1 dB             43 %
presence 1-6k           -8.8 dB             14 %
air 6-16k              -11.0 dB              0 %
broadband               -8.1 dB

$ python3 wet_dry_bands.py vocal_dry.wav reverb_return_hpf350.wav
band             return vs dry  share of return
sub 20-150          (dry ~none)              0 %
low-mid 150-500        -12.1 dB             24 %
mid 500-1k              -8.6 dB             56 %
presence 1-6k           -8.8 dB             20 %
air 6-16k              -14.2 dB              0 %
broadband               -9.8 dB

The low-mid share of the return fell from 43% to 24%, and the presence band (−8.8 dB against the dry vocal) didn’t change at all. If your own return shows 150–500 Hz as its biggest band, that’s where to start.

No session, just a bounce? Splitting the vocal out with StemSplit and listening to it on its own gives you a decent feel for how much wash got printed. Separators don’t always agree on where a tail belongs, so use it as a listening aid, not a measurement.

Fold it to mono once

A plain stereo reverb is mostly decorrelated left to right (our return measured −0.09). Fold the mix to mono and it gets quieter relative to the centred vocal: 3.4 dB quieter here. So mono doesn’t make a normal reverb muddier. It makes it drier, which is worth knowing if most people will hear the song on a phone. If your return gets louder or hollower in mono, something on it, a widener or a polarity trick, is fighting itself. Wide mixes fail in mono covers that check.

Genre notes

Why does reverb make my mix muddy?
Because a reverb return is mostly low-mid energy that keeps ringing after the dry note stops. On our synthetic ballad, an unfiltered hall raised the mix’s 150–500 Hz band by only 0.6 dB on average but by 4.3 dB in the gaps between notes, which is where clarity lives.
What frequency should I high-pass a vocal reverb at?
Start around 250–400 Hz on the return, not the vocal. In our test a 350 Hz high-pass halved the low-mid fill in the gaps and cut the leftover tail by 9 dB, while the 1–6 kHz part of the reverb didn’t change. A 150 Hz cut barely helped.
How much pre-delay should vocal reverb have?
Enough that the start of each word lands dry, typically 20–60 ms. In our test 25 ms raised the dry-over-wet ratio in the first 30 ms of each note from 14.8 to 18.3 dB, and 50 ms made it effectively dry. Sync it to a 1/128 or 1/64 note to keep it in time.
Does reverb get muddier in mono?
A plain stereo reverb gets quieter in mono, not muddier. Our return was almost fully decorrelated, and folding to mono dropped it 3.4 dB relative to the centred vocal. If your return gets louder or hollower in mono, something on it is out of polarity between the two sides.

A free one-pager

The checklist we run before every release.

Loudness, true peak, mono, tails, export settings and the metadata basics, on one page. Leave your email and it opens straight away, or read it here first. No email needed for that.