Vocal and Backing Mix
Mixing a vocal onto a backing track inside ComfyUI
- vocals
- backing
- audio
Sooner or later an audio workflow hands you two things that belong together: a vocal you generated or split out, and a backing track you generated separately. ComfyUI's native audio nodes will happily save each one. Getting them into one file so a video node can mux them is where people start dragging in ffmpeg by hand.
HenjiccAudioMix - labelled Vocal and Backing Mix in the UI, category Henjicc/Audio - is that missing step. Two AUDIO inputs in, one AUDIO out. It is deliberately dumb: no stem separation, no tempo matching, no loudness normalisation to broadcast spec. The README says so out loud, and after a decade of tools promising "AI mastering" that's almost refreshing.
What it actually does to your samples
It reads both inputs as {waveform, sample_rate} dicts and refuses anything that isn't a single-batch mono or stereo tensor of finite floats under five minutes. Then, in order:
If the vocal's sample rate differs from the backing's, the vocal gets resampled to the backing's rate with torchaudio.functional.resample. The backing is the reference clock for the whole node - its rate and its length win. vocal_offset then shifts the vocal: positive pads silence onto the front (the vocal lands later), negative slices samples off the head (the vocal lands earlier). The vocal is zero-padded or truncated to the backing's exact length, so a short vocal doesn't cut the song off and a long one doesn't extend it.
Gains are applied in the linear domain (10^(dB/20)) and summed. Last, a peak limiter: if the mix peaks above 0.98, the whole thing is scaled down to 0.98. That's it. mix_audio() in audio/music.py is about forty lines and you can read all of them.
The inputs that matter
vocalsandbacking(AUDIO) - the two tracks. Wire them from whatever produced them.vocal_gain_dbandbacking_gain_db- −24 to +12 dB in half-dB steps, both default 0. Pull the backing down 3–6 dB before you touch the vocal; the vocal is usually the thing you're protecting.vocal_offset- −30 to +30 seconds, default 0. This is manual timing, and it's the field people misread. It's not beat alignment and it can't detect a downbeat. If the vocal sounds late, you want a negative offset.
Output is a single audio socket. Feed it to SaveAudio, a video muxer, or the pack's own HenjiccH3VideoOutput if you're assembling a MiniMax H3 clip.
Installing the pack
The node ships in henjicc/ComfyUI-Henjicc. Manager: search comfyui-henjicc or Henjicc. Manual:
cd ComfyUI/custom_nodes
git clone https://github.com/henjicc/ComfyUI-Henjicc.git ComfyUI-Henjicc
Then, in the Python environment ComfyUI actually runs on:
python -m pip install aiohttp av numpy Pillow
Those four are the pack's only declared dependencies (pyproject.toml; there's no requirements.txt to find). Restart ComfyUI and hard-refresh the browser. There's no npm step - the frontend extensions are plain JS loaded through ComfyUI's own mechanism.
Where people get burned
torchaudio. The mix function imports torchaudio at call time, and the README is explicit that audio features need a torchaudio matching your PyTorch build. If your install predates the video/audio wave of ComfyUI nodes, this is the one that bites. Don't let pip upgrade torch to satisfy it - match the version you already have.
Duration. The validator caps audio at five minutes per input. Longer files don't get trimmed, they error. Trim upstream.
Channel layout. Batch dimension must be 1 and channels 1 or 2. A batch of two clips concatenated into one tensor fails validation with a message about finite mono/stereo audio, which is accurate but not exactly helpful.
The limiter is not a safety net you should lean on. Push both gains up together and everything above 0.98 gets scaled as a whole, so your carefully built 6 dB of headroom just vanishes. Set the relationship between the two tracks, then back the pair off.
And the honest limit: if your vocal and backing came from unrelated generations, they will drift, and no amount of offset fixes drift - only a cut will. This node is a mixer, not an aligner.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| vocals | AUDIO | — | |
| backing | AUDIO | — | |
| vocal_gain_db | FLOAT | 0.0-24–12 | — |
| backing_gain_db | FLOAT | 0.0-24–12 | — |
| vocal_offset | FLOAT | 0.00-30–30 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| audio | AUDIO | — |