
Mixing · 9 November 2026 · 6 min
Vocal sounding thin? Your high-pass filter is probably set too high
A high-pass on a vocal is there for rumble and pops, not for the voice, so set it just under the singer’s lowest note instead of at a number you read somewhere. In our test a steep 80 Hz high-pass took 15.7 dB of junk off a low male line and cost its fundamentals 0.3 dB. Move it to 150 Hz and the same line lost 6.9 dB of its fundamentals. That’s the thin, phone-call sound.
Everybody learns the same first move: high-pass everything that isn’t kick or bass. So the vocal gets one. Maybe at 100 Hz, maybe higher because the preset said so, maybe a second one inside the channel strip you forgot about. Then the vocal sounds small next to the beat and you start boosting low mids to get it back.
Often the fix is to undo the first move, at least partly.
What the high-pass is actually for
A close vocal mic picks up stuff that isn’t singing: the mic stand thumping when someone taps the desk, traffic and air handling, and the “P” and “B” pops that hit the capsule as a burst of air. Almost all of that lives below about 60 to 80 Hz. A singer’s lowest notes don’t. A low male voice bottoms out somewhere around 100 Hz, and a higher voice sits up around 200 Hz and above.
So the right high-pass depends on who’s singing. It’s set from the voice, not from a rule.
We wrote hpf_check.py to put numbers on it. It builds two synthetic sung lines as harmonic tones, a low one from G2 to D3 (98 to 147 Hz) and a high one from G3 to D4 (196 to 294 Hz), plus the junk: rumble under 60 Hz and four 30 Hz pops. Each filter is a plain Butterworth high-pass, so the script runs the voice and the junk through it separately and reports what’s left of each.
#!/usr/bin/env python3
"""Where should the high-pass on a vocal go?
python3 hpf_check.py
Synthetic signals, not a real take. Two sung lines as harmonic tones (12 harmonics, falling 4 dB
each): a low male line G2-A2-C3-D3 (98-147 Hz) and a higher line G3-A3-C4-D4 (196-294 Hz).
Plus the low junk a home vocal picks up: mic-stand rumble (noise under 60 Hz) and "P" pops
(30 Hz-centred thumps, 120 ms each). Every high-pass is a Butterworth, so we run the parts
through it separately and report what's left of each, in dB relative to before.
"body" = the 100-300 Hz band of the line (where a voice gets its weight).
"""
import numpy as np
from scipy import signal
SR = 48000
rng = np.random.default_rng(7)
def line(f0s, note_s=1.0):
t = np.arange(int(SR * note_s)) / SR; out = []
for f0 in f0s:
x = sum(10 ** (-4 * (k - 1) / 20) * np.sin(2 * np.pi * k * f0 * t + k) for k in range(1, 13))
out.append(x * np.hanning(len(t)) ** 0.2)
return np.concatenate(out)
LOW = line([98.0, 110.0, 130.81, 146.83])
HIGH = line([196.0, 220.0, 261.63, 293.66])
N = len(LOW)
rumble = signal.sosfilt(signal.butter(4, 60, "low", fs=SR, output="sos"), rng.standard_normal(N))
pops = np.zeros(N)
for c in (0.3, 1.4, 2.6, 3.5):
i = int(c * SR); L = int(0.12 * SR); tt = np.arange(L) / SR
pops[i:i + L] += np.sin(2 * np.pi * 30 * tt) * np.exp(-tt / 0.03)
junk = rumble / np.std(rumble) * 0.05 + pops * 0.5
def db(a, b): return 10 * np.log10(np.sum(a ** 2) / np.sum(b ** 2))
def band(x, lo, hi): return signal.sosfiltfilt(signal.butter(4, [lo, hi], "bandpass", fs=SR, output="sos"), x)
def fund(x, f0s):
# level of the fundamentals only (narrow band around each note's f0)
seg = N // 4; return np.concatenate([band(x[i * seg:(i + 1) * seg], f * 0.94, f * 1.06) for i, f in enumerate(f0s)])
LF, HF = [98.0, 110.0, 130.81, 146.83], [196.0, 220.0, 261.63, 293.66]
print("Low line G2-D3 (98-147 Hz), high line G3-D4 (196-294 Hz); junk = stand rumble + P pops")
print(f"{'high-pass':22s} {'junk left':>10s} {'pop peak':>9s} {'low: fund':>10s} {'low: body':>10s} {'high: fund':>11s} {'high: body':>11s}")
for fc, order in ((60, 2), (80, 2), (80, 4), (100, 4), (120, 2), (150, 4), (200, 4)):
sos = signal.butter(order, fc, "high", fs=SR, output="sos")
f = lambda x: signal.sosfilt(sos, x)
j, l, h, p = f(junk), f(LOW), f(HIGH), f(pops * 0.5)
pk = 20 * np.log10(np.max(np.abs(p)) / np.max(np.abs(pops * 0.5)))
print(f"{fc:4d} Hz, {6*order:2d} dB/oct {db(j, junk):8.1f} dB {pk:6.1f} dB {db(fund(l, LF), fund(LOW, LF)):7.1f} dB "
f"{db(band(l, 100, 300), band(LOW, 100, 300)):7.1f} dB {db(fund(h, HF), fund(HIGH, HF)):8.1f} dB {db(band(h, 100, 300), band(HIGH, 100, 300)):8.1f} dB")
$ python3 hpf_check.py
Low line G2-D3 (98-147 Hz), high line G3-D4 (196-294 Hz); junk = stand rumble + P pops
high-pass junk left pop peak low: fund low: body high: fund high: body
60 Hz, 12 dB/oct -8.8 dB -12.1 dB -0.3 dB -0.2 dB -0.0 dB -0.0 dB
80 Hz, 12 dB/oct -12.2 dB -14.2 dB -0.9 dB -0.5 dB -0.1 dB -0.1 dB
80 Hz, 24 dB/oct -15.7 dB -17.9 dB -0.3 dB -0.1 dB -0.0 dB -0.0 dB
100 Hz, 24 dB/oct -20.7 dB -20.1 dB -1.3 dB -0.5 dB -0.0 dB -0.0 dB
120 Hz, 12 dB/oct -17.9 dB -17.3 dB -3.0 dB -1.7 dB -0.3 dB -0.4 dB
150 Hz, 24 dB/oct -30.2 dB -24.1 dB -6.9 dB -3.2 dB -0.2 dB -0.2 dB
200 Hz, 24 dB/oct -36.1 dB -26.8 dB -15.1 dB -6.0 dB -1.3 dB -1.6 dB
Reading the numbers
A steep filter at 80 Hz is nearly free. 24 dB per octave at 80 Hz took 15.7 dB of junk off and knocked the pop peaks down 17.9 dB, and the low line’s fundamentals moved 0.3 dB. You won’t hear that.
Gentle and higher is worse than steep and low. A 12 dB per octave filter at 120 Hz removes about the same junk, 17.9 dB, but costs the low voice 3.0 dB of fundamental. Even at 80 Hz, the gentle slope costs 0.9 dB where the steep one costs 0.3, because a gentle filter starts cutting further up.
150 Hz is where a low voice gets thin. The low line lost 6.9 dB of fundamental and 3.2 dB in the 100 to 300 Hz band where a voice gets its weight. At 200 Hz that’s 15.1 dB of fundamental. That’s the phone-call vocal.
A higher voice can take a higher filter. The same 150 Hz filter cost the high line 0.2 dB. Push it to 200 Hz and it starts to cost there too, 1.3 dB.
These are synthetic voices and synthetic junk, not a real take, so read it for the shape. Real voices have breath and chest resonance under the notes too. But the pattern is the point: set the filter from the lowest note, and use a steeper slope rather than a higher frequency.
How to set it in your session
- Find the lowest note. Loop the lowest line in the song and look at an analyzer, or work it out from the note: G2 is 98 Hz, A2 is 110 Hz, C3 is about 131 Hz.
- Set the high-pass under it. Start somewhere below that fundamental, with a steep slope, 18 or 24 dB per octave.
- Sweep up until you hear the voice change, then back off. Do it with the beat playing. Soloed, a vocal can lose a lot of low end before you notice.
- Look for a second filter. The mic’s low-cut switch, the interface, the channel strip and a preset can all stack. Two gentle filters at 100 Hz add up to a steep one.
- Fix pops at the source if they’re still there. A pop filter and a little angle off the mic beat any EQ. For a few leftover pops, clip-gain or a dynamic EQ on that one word beats raising the filter for the whole song.
Check what you gave back
The test that matters is the vocal against the beat. Bounce a version with your old filter and one with the new one, level-match them, and flip between them in CoProducer’s A/B. If the new one sounds fuller without getting muddy, keep it. If the low end of the mix gets cloudy, the beat probably needs room, not the vocal less body.
- Where should I high-pass vocals?
- Just under the singer’s lowest note, with a steep slope. In our test a 24 dB/oct high-pass at 80 Hz removed 15.7 dB of rumble and pops and cost a low male line 0.3 dB of fundamental.
- Why does my vocal sound thin?
- Often because the high-pass is too high or there are two of them. A 150 Hz high-pass cost our low test voice 6.9 dB of its fundamentals.
- Is 100 Hz too high for a vocal high-pass?
- For a low voice it’s close to the edge: a steep filter at 100 Hz cost our low line 1.3 dB. For a higher voice it’s fine. Set it from the lowest note, not a fixed number.
- Should I use a steep or gentle high-pass on vocals?
- Steep and lower usually beats gentle and higher. A 12 dB/oct filter at 120 Hz removed about the same junk as a steep 80 Hz one but cost the low voice 3.0 dB instead of 0.3.