H3 Beat Map
Cut the music video on the actual bar lines, not on a guess
- project
- audio
- timeline
- cuts
- report
A music video lives or dies on one thing: does the picture change when the music does. Most multi-shot workflows cut on a duration you typed, which means the cuts land wherever math says they land, not where the kick drum says they should. H3 Beat Map reads the track and lays the segments on the bar grid - no model, no key, no network call.
What it actually does
It runs the whole track through librosa and pulls out three things. First, a tempo and beat list (beat_track). Second, the downbeats - librosa gives you beats but not bar lines, so the node measures onset strength at each position in the bar and picks the phase with the most energy, which is where the kick is. A bar line half a bar out would put every cut off the one, and you'd hear it immediately.
Third, drops: bar lines where sustained RMS energy jumps by 1.25x or more, measured over two bars either side rather than instantaneously. A snare hit is not a drop.
Then it packs whole bars into clips that fit between your minimum and maximum length. Every clip starts on a downbeat.
The trick that makes it usable in ComfyUI
Musical time is not renderable time. MiniMax H3 only renders on a 17-frame ladder (length % 17 == 5), so at 24fps 5.201s is not an option - you get 5.167s or 5.875s and nothing between. Two bars at 92.3 BPM is 5.201s.
So each segment carries the exact musical span as its target_duration and the snapped rung as its render_duration. H3 renders slightly long, and H3 Stitch Timeline trims the overshoot back off. Every cut lands on the beat to the millisecond, and nothing accumulates over a four-minute track. This is the single design decision that stops a music video from drifting 3 seconds by the last chorus.
Inputs worth setting
Wire project from H3 Project and audio from the H3 Cast Board's audio output - the full track, not a clip of it.
min_segment_seconds/max_segment_seconds- a whole number of bars has to fit between these. At 92 BPM a bar is 2.6s, so 5–10s allows 2 or 3 bars. Squeeze the window and you can end up with nothing to lay out.beats_per_bar- 4 for almost everything, 3 for a waltz, 6 for some drill and trap. Defaults to 4 and you should leave it there.bars_per_clip- 0 picks the grouping that best fits your window. Set it to 2 or 4 if you want to force the shot length rather than let the node choose.cut_on_drops- on by default. It shortens the clip before a big energy rise so the next one starts exactly on it. This is the one that makes a chorus land.start_seconds/end_seconds- skip an intro, or stop short.cover_whole_track- the first downbeat is rarely at 0.000. Leave this on or you silently lose whatever comes before it.
Outputs
timeline goes to H3 Timeline (or straight to the dispatcher if you're writing prompts on the cards). cuts is a JSON blob with the tempo, bar length, phase and the full cut list - wire it into H3 Segment Slicer's cuts input and the cuts land on the music while the treatment only says what happens inside them. report tells you the tempo, the bar length, which beat of the bar the downbeat is on, total trim, and every clip with its bar count.
Any drop that couldn't start its own clip without dropping under your minimum gets named in the report instead of quietly ignored.
Install
Beat Map is the one node in the pack with a real dependency, and it's opt-in on purpose:
cd ComfyUI/custom_nodes
git clone https://github.com/AIJigyasa/ComfyUI-H3-Planner
python -m pip install -r ComfyUI/custom_nodes/ComfyUI-H3-Planner/requirements-audio.txt
That installs librosa>=0.10. On Windows portable, run it through the embedded Python (python_embeded\python.exe). The pack ships no requirements.txt specifically so ComfyUI Manager doesn't force librosa on everyone - every other node works without it.
Where people get burned
Run it and get a librosa error: you installed into the wrong Python. It has to be the interpreter that runs ComfyUI, and you must restart after.
"No steady beat was found in this audio" - the track is speech, ambient, or too quiet. There's no grid to find. Feed the treatment to H3 Segment Slicer instead and cut on its shot markers.
"No clips could be laid out" - your min/max window is narrower than a whole number of bars. Widen it.
And the honest advice: if the track has no stable tempo, don't fight it. This node is for music. For anything else, the slicer is the right tool.
Inputs (10)
| Name | Type | Default | Description |
|---|---|---|---|
| project | H3_PROJECT | — | |
| audio | AUDIO | the full track, straight off the Cast Board's audio output | |
| min_segment_seconds | FLOAT | 5.00.5–30 | — |
| max_segment_seconds | FLOAT | 10.01–30 | a whole number of bars must fit between these. At 92 BPM a bar is 2.6s, so 5-10s allows 2 or 3 bars; a narrow window may allow none. |
| beats_per_bar | INT | 41–12 | 4 for almost everything. 3 for a waltz, 6 for some drill and trap. |
| bars_per_clip | INT | 00–16 | 0 = choose the grouping that best fits the length window. Set it to force 2-bar or 4-bar shots. |
| cut_on_drops | BOOLEAN | true | shorten the clip before a big energy rise so the next one starts exactly on it |
| start_seconds | FLOAT | 0.00–600 | skip an intro |
| end_seconds | FLOAT | 0.00–600 | 0 = to the end of the track |
| cover_whole_track | BOOLEAN | true | the first downbeat is rarely at 0.000. On = the lead-in before it and the tail after the last bar are covered too, so no second of the track goes unrendered and no shot is silently dropped. Off = start on the first downbeat and lose whatever comes before it. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| timeline | H3_TIMELINE | — |
| cuts | STRING | — |
| report | STRING | — |