APNext H3 Beat Grid (BPM + every beat)
One node that tells you the song is 128 BPM — and where every beat lands
- audio
- audio
- bpm
- beat_times
- grid_json
- summary
- count
Every music-video workflow in the dagthomas pack eventually needs to know two things about the song: how fast it is, and where the beats actually fall. H3BeatGrid is the node that answers both. It's a straight BPM detector - no AI, no API, no download - that measures the tempo and hands you a grid of every beat as a timestamp you can wire into the rest of the pipeline.
What it does
Feed it the song from Load Audio and it measures the tempo by autocorrelating the onset envelope - the same math the pack's Song Analysis readout uses. That gives you the BPM, but tempo alone won't sync anything, because the beat's phase matters: a song at 128 BPM can have its kicks starting anywhere. So the node fits the phase to the song's own onsets, then guesses the downbeat by which beat carries the most energy. That last part is honestly labelled a guess, because a backbeat can put the snare on 2 and 4 - check the summary output before you trust the ONE markers.
Inputs that matter
audio- the song. Passed through unchanged, so you can chain it.events(optional) - wire in Sound Events'eventsand the phase is fitted to the detected hits instead of the raw onsets. The summary then tells you how many of the hits actually sit on the grid (81/83 detected hits sit on the grid). That's the setup in the pack's beatsync example.bpm_override- force the tempo when you know the song is 128 and the detector wobbles. 0 = measure it.beats_per_bar(default 4) - for the bar numbers and the downbeat guess.offset_ms- nudge every grid line, positive = later. For when the phase fit is a few milliseconds off what your ears say.subdivision- which lines to list: beats, eighths, sixteenths, or half notes.
Outputs
bpm- the tempo as a float; 0 means no steady pulse was found.beat_times- one line per grid line:[0:00.23] BEAT 1 | bar 1 | ONE. Great for reading, or pasting somewhere.grid_json- the whole grid as JSON (bpm, period, phase, downbeat, beats, bars, lines). This is the one that drives things: wire it into Beat Emphasis'grid_jsonso every beat gets emphasised, and into the Cut Plan'sbeat_gridso beat-exact cuts can snap scene boundaries onto the pulse.audio(pass-through),summary,count.
The grid it draws is the same one the Sound Events editor overlays, so what you see in the preview editor matches what this node reports.
Why you'd bother
On its own it's a curiosity - a nice BPM readout on the canvas. It earns its keep wired into the beat-sync chain: Beat Grid feeds the Cut Plan's beat_grid with beat_snap = nearest beat, so every scene opens on the pulse to within half a frame (about 21 ms), and it feeds Beat Emphasis so the model can't miss a beat it's been told about twice. In that context it replaces guesswork about "is this song cuttable" with a number you can plan around.
Installing
It's part of the dagthomas pack - ComfyUI Manager, search "comfyui_dagthomas", install, restart. Or manually:
cd ComfyUI/custom_nodes
git clone https://github.com/dagthomas/comfyui_dagthomas
cd comfyui_dagthomas
pip install -r requirements.txt
No extra dependencies beyond the pack, no models to download - it's pure torch on the waveform. For a 200-second song the analysis is effectively instant, so you can throw it into any workflow without worrying about queue time. If you only ever use one of the pack's audio-analysis nodes, this is the friendliest entry point.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| audio | AUDIO | The song (Load Audio). Passed through unchanged. | |
| eventsopt | STRING | Sound Events' `events` (or `events_json`). With it the grid's phase is fitted to the detected hits and the table reports how many sit on it; without it the phase is fitted to the song's own onset envelope. | |
| bpm_overrideopt | FLOAT | 0.00–300 | Force the tempo (you know the song is 128). 0 = measure it. |
| beats_per_baropt | INT | 41–12 | Beats per bar, for the bar numbers and the downbeat guess. |
| offset_msopt | INT | 0-500–500 | Nudge every grid line, positive = later. |
| subdivisionopt | COMBO | beats | Which grid lines to list: beats, or eighths / sixteenths between them, or every 2nd beat. |
Outputs (6)
| Name | Type | Description |
|---|---|---|
| audio | AUDIO | The same audio, passed through. |
| bpm | FLOAT | The tempo in BPM (0 = no steady pulse). |
| beat_times | STRING | One line per grid line: [m:ss.xx] BEAT n | bar b | ONE on the downbeats. |
| grid_json | STRING | The grid as JSON: bpm, period, phase, downbeat, beats[], bars[], lines[]. Wire into Beat Emphasis. |
| summary | STRING | One line: tempo, first beat, bars, confidence. |
| count | INT | How many grid lines. |