JR MiniMax H3 Empty Audio Latent for Tail (Experimental)
37 ticks of nothing, so your tail refine can actually sample
- video_latent
- audio_latent
- status
Here's the annoying part of a tail refinement pass. H3 is omni-modal - one transformer generates
picture and sound together - so its latent is a pair: video at [B,24,T,H,W] and audio at
[B,32,2,T_audio]. The AV Latent Builder will not accept a solo video stream. And when you're refining
a 22-frame tail, you have no audio stream to hand it, because the local timeline is a fifth of a second
long and the original clip's audio latent doesn't fit that length.
This node manufactures the missing half: a correctly sized, zeroed audio latent matching your video latent's dtype and device. It's plumbing, and it's the plumbing that makes the whole two-pass tail workflow close.
Why the length is the whole problem
The two streams are coupled by H3's own grid. Video runs on T = 5k+2 latent tokens, which decode to
17k+5 frames at 24 fps; audio runs at 40 latent ticks per second. So the audio latent length is
derived, not chosen - and if it's wrong, the builder rejects the pair instead of silently padding.
The node reads the token count off your video_latent and computes: frames = 1 if T == 1, otherwise
((T-2)//5)*17 + 5; ticks = round(frames * 40 / 24). Then it allocates zeros([1, 32, 2, ticks]) at
the input's dtype and device. In practice:
T=1(the legacy single frame) → 1 frame → 2 ticksT=7(the usual tail window from Tail Frame Latent) → 22 frames → 37 ticksT=12→ 39 frames → 65 ticks
Validation is strict on purpose: exactly one stream, [1,24,T,H,W], floating point, all finite, T is
1 or 5k+2, and no noise mask. Feed it an AV pair, an audio latent, or something with a mask
attached and it errors instead of guessing.
Inputs and outputs
video_latent(LATENT) - the only input. Connect the same tensor you hand the AV Latent Builder: the tail context, post-upscale, clean and unmasked.audio_latent(LATENT) - the zero initializer. Goes to the AV Latent Builder's audio input.status(STRING) - prints the frame count, the tick count, the actual shape/dtype/device, and a reminder that this is a zero initializer rather than encoded silence.
The wiring, end to end:
Tail Frame Latent.tail_context_latent -> Neural Latent Upscaler ─┐
├-> AV Latent Builder -> SamplerCustomAdvanced
Empty Audio Latent for Tail.audio_latent ------------------------┘
AV Latent Builder -> Split AV Latent.video_latent -> VAE Decode -> last IMAGE
What it is not
Worth being blunt, because these three get conflated constantly. It is not encoded silence - nothing was ever decoded or compressed. It is not a crop of the original clip's audio, which would be the wrong length and the wrong timeline. And it is not an audio lock: it's a zero initializer that still participates in joint sampling, so don't claim the picture is mathematically untouched by it. What you get out of the sampler for audio is meant to be thrown away - the video stream is the product.
If you actually want to pin real audio, that's a different node (JR_H3_AudioDrivenLatentBuilder), which
replaces the audio branch and forces a zero mask so it can't drift.
Install
Same pack, same install as everything else here:
cd ComfyUI/custom_nodes
git clone https://github.com/Goldlionren/ComfyUI_JR_MiniMaxH3Node.git
python -m pip install -r ComfyUI_JR_MiniMaxH3Node/requirements.txt # same Python ComfyUI runs on
Restart, done. No model downloads, no extra dependencies for this one; ComfyUI Manager users can just search JR MiniMax H3 and install.
Where people get burned
- 22 frames vs 37 ticks. 22 frames is 0.917 s, 37 ticks is 0.925 s. That gap is grid rounding, not a bug, and it is inside the tolerance the builder allows.
- Two different tensors. The video you upscale and the video you hand AV Builder must be the same tensor with matching batch/dtype/device. It's easy to wire the pre-upscale latent into one node and the post-upscale one into the other, then blame the builder.
- Don't feed it to the Progressive samplers. Those want an empty target latent, not a latent with
content. This is for an ordinary
SamplerCustomAdvancedsecond pass at a low denoise. - Conditioning doesn't travel. The original clip's first/last-frame anchors, time indices and audio guidance describe a full timeline; a 7-token clip starts a fresh local timeline at zero. Validate with unanchored conditioning first.
- The legacy single-frame path is compatibility, not approval. It still emits
[1,32,2,2]forT=1inputs, but the author's own tests found darkening and striping on the single-frame encode/decode round trip even without upscale or sampling. Keep the 7-token output intact, decode it, take the last frame as a wallpaper, and don't pinch it into a single latent time slice.
Inputs (1)
| Name | Type | Default | Description |
|---|---|---|---|
| video_latent | LATENT | Connect the same video as AV Builder: tail_context_latent [1,24,5k+2,H,W] or legacy T=1. Not an AV pair or masked latent. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| audio_latent | LATENT | — |
| status | STRING | — |