APNext H3 Beat Emphasis (conditioning audio)
The beat is a suggestion until you spike it
- audio
- audio
- summary
- count
H3 lip-syncs to whatever audio you feed it, and it does "nod along" to the beat. The problem is that nodding isn't landing. The dagthomas pack measured it with its own Sync Check node: on plain song audio, only about 58% of beats triggered a picture change within 80 ms - against 49% by pure chance. The model was basically guessing when the kick landed. H3BeatEmphasis exists to make that impossible: it takes the audio H3 listens to and turns every hit into an unmissable event, while your output video keeps the original song untouched.
This is a conditioning node for the pack's masked-audio music-video pipeline. Load Audio → stem split → this node → the render, and the model stops approximating rhythm and starts actually hitting it.
The one input that matters: the vocal stem, not the song
Here's where people get burned, and the tooltip is refreshingly blunt about it. Feed the vocal stem into audio (AudioSeparation's vocals), not the full song. H3 lip-syncs to whatever it's conditioned on. Spike the whole mix +9 dB and duck it −6 dB and the voice gets buried - the pack's own beatsync example lost its lip-sync exactly that way. The right recipe is the vocal stem as input, boost_db 3, duck_db 0, and the thump layer on: the voice survives intact, the synthetic thumps carry the beat, and the lips follow both.
What the knobs actually do
boost_db(default 9) - how much the gain spikes on every hit, measured dB above the ducked level. The peak lands exactly on the hit.duck_db(default −6) - the level between hits. Lower means more contrast; at −36 the audio is almost only hits.layer- a synthetic transient added per hit:click(a 3 kHz ping),thump(a kick sweep), both, or none. This is what makes a beat legible even to a diffusion model.layer_level(0.5) - how loud that layer is.
The optional stuff is the shape of the spike: attack_ms / hold_ms / decay_ms ramp the boost up to the hit, hold it, and fall back to the ducked level. scale_by_strength (on) scales the boost by each hit's strength so a light hit gets a light nudge. dynamics_curve and normalize (both on by default, keep normalize on) tidy the result. Hits come from Sound Events' events socket, or Beat Grid's grid_json - with both wired, a beat near a real hit defers to the hit.
Wire the audio output into the masked-audio node's master_audio (or Chain Render's conditioning_audio). Never into Save Clip - that's where the original song belongs, and this is only what the model hears. You also get summary and a count of emphasised hits.
Installing it
It's part of the dagthomas pack. ComfyUI Manager → search "comfyui_dagthomas" → Install, or:
cd ComfyUI/custom_nodes
git clone https://github.com/dagthomas/comfyui_dagthomas
cd comfyui_dagthomas
pip install -r requirements.txt
Restart ComfyUI. The node itself is pure torch - no extra dependency beyond the pack - though the H3 model it feeds is a chunky download. (Old builds of the pack had a nasty habit of changing the working directory on load, which broke ComfyUI's Restart button; that was patched years ago via a community PR.)
Is it worth it?
Honest answer: the author treats it as an experiment, and so should you. Render the scene with and without emphasis, run Sync Check on both, and keep whichever measures higher. But when H3 is missing the beat, this is the fastest lever you have - and it's a fun one to hear, since the ducked-between-hits mix is genuinely satisfying to listen to.
Inputs (13)
| Name | Type | Default | Description |
|---|---|---|---|
| audio | AUDIO | What the render will be conditioned on. For a music video feed the VOCAL STEM (AudioSeparation's vocals), not the song: the lips follow the voice, the thumps add the beat - the full mix spiked and ducked buries the voice and the lip-sync with it. | |
| boost_db | FLOAT | 9.00–24 | Gain spike on every hit, dB above the ducked level. The peak lands exactly on the hit. |
| duck_db | FLOAT | -6.0-36–0 | Level between hits, dB. Lower = more contrast; -36 leaves almost only the hits. |
| layer | COMBO | thump | A synthetic transient added on every hit: a click (3 kHz ping), a thump (kick sweep), or both. |
| layer_level | FLOAT | 0.500–1 | How loud the layer is (0-1, before normalising). |
| eventsopt | STRING | Sound Events' `events`: the hits (bass hits, impacts, accents, drops) to emphasise. | |
| grid_jsonopt | STRING | Beat Grid's `grid_json`: every beat gets emphasised (downbeats a little more). With both wired, beats near a hit defer to the hit. | |
| attack_msopt | INT | 51–100 | Ramp up to the spike, ms (the spike peaks ON the hit). |
| hold_msopt | INT | 400–500 | How long the spike stays at full boost, ms. |
| decay_msopt | INT | 1201–2000 | How long the spike takes to fall back to the ducked level, ms. |
| scale_by_strengthopt | BOOLEAN | true | Scale the boost and the layer by each hit's strength, so a light hit gets a light nudge and a heavy one the full spike. |
| dynamics_curveopt | FLOAT | 1.000.25–4 | |x|^curve after the spikes: above 1 expands (the spikes stand even prouder), below 1 compresses. |
| normalizeopt | BOOLEAN | true | Peak-normalise the result to -0.2 dBFS so the boosts cannot clip. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| audio | AUDIO | The emphasised mix - wire into the masked-audio node's `master_audio` (or Chain Render's `conditioning_audio`). NOT into Save Clip: the output video keeps the original song. |
| summary | STRING | What was done. |
| count | INT | How many hits were emphasised. |