Nodes/SDXL Auto Prompter/APNext H3 Beat Grid (BPM + every beat)
ComfyUI Node

APNext H3 Beat Grid (BPM + every beat)

One node that tells you the song is 128 BPM — and where every beat lands

By dagthomas·Created 3 years ago·Updated about 23 hours ago· 290
APNext H3 Beat Grid (BPM + every beat)
  • audio
  • audio
  • bpm
  • beat_times
  • grid_json
  • summary
  • count
events
bpm_override0.0
beats_per_bar4
offset_ms0
subdivisionbeats

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' events and 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_json so every beat gets emphasised, and into the Cut Plan's beat_grid so 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.

Categorycomfyui_dagthomas/H3

Inputs (6)

NameTypeDefaultDescription
audioAUDIOThe song (Load Audio). Passed through unchanged.
eventsoptSTRINGSound 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_overrideoptFLOAT0.00–300Force the tempo (you know the song is 128). 0 = measure it.
beats_per_baroptINT41–12Beats per bar, for the bar numbers and the downbeat guess.
offset_msoptINT0-500–500Nudge every grid line, positive = later.
subdivisionoptCOMBObeatsWhich grid lines to list: beats, or eighths / sixteenths between them, or every 2nd beat.

Outputs (6)

NameTypeDescription
audioAUDIOThe same audio, passed through.
bpmFLOATThe tempo in BPM (0 = no steady pulse).
beat_timesSTRINGOne line per grid line: [m:ss.xx] BEAT n | bar b | ONE on the downbeats.
grid_jsonSTRINGThe grid as JSON: bpm, period, phase, downbeat, beats[], bars[], lines[]. Wire into Beat Emphasis.
summarySTRINGOne line: tempo, first beat, bars, confidence.
countINTHow many grid lines.