H3 AudioFirst 32x32 Audio Conditioning
The 32×32 Node That Fixes MiniMax H3's Tin-Can Voice
- positive
- image
- vae
- audio_positive
- audio_latent
If you've been pushing MiniMax H3 with fast settings - 4 steps, a 14-second clip in two and a half minutes on a 4070 - you know the sound. The voice comes back like someone talking into a metal pipe. H3 is the model that gave open weights native, jointly-generated audio rather than a bolted-on soundtrack, and the entire pitch is dialogue whose lips match with no second pipeline.
H3AudioFirstConditioning is the first of the three-node chain that fixes that. The idea is embarrassingly simple: generate the voice first, cheaply, at 8 steps on a canvas so small it's nearly free, then refuse to let the video pass touch it. This node builds that cheap first pass.
What it actually does
In stock H3 you sample one nested audio+video latent where the picture side is 544×960 and the audio side is whatever the audio VAE wants. Cut the sampler to 4 steps and the audio doesn't get enough refinement - hello, pipe. Adding steps to the joint pass means paying for extra video compute too.
AudioFirst splits the difference. This node takes your existing conditioning and, without re-running the text encoder at all, produces two things:
- an empty 32×32 audio-pass latent - from
_empty_av_latent(32, 32, length). The audio pass denoises on a canvas small enough that a step costs about 1.2 seconds, so the whole 8-step pass is roughly 15 seconds whether your clip is 5 seconds or 14; - conditioning with a keyframe stuck on it - your input image resized to 32×32 with the crop method set to
disabled, meaning stretched, no cropping. It gets encoded by the VAE you hand it and set asminimax_keyframesat frame 0. That keyframe only ever conditions the audio pass, never the picture, which is why stretching it into a square does no harm.
The encoder runs once, your full-resolution conditioning is reused, and all you've added is a tiny sampling job.
The inputs that matter
Only one input needs judgment: length. The tooltip says it plainly - use the same frame count as the video pass. Default is 124, range 5 to 3600. H3 frame counts follow 17k+5, so 124 is 5 seconds, 243 is 10, 345 is 14 at 24fps. Type 200 here and the pack will slap you: the lock node later raises Audio shape ... does not match the video pass ... use the same frame count in both passes.
The other three are plumbing:
- positive - the CONDITIONING off MiniMaxH3ImageToVideo.
- image - the same scaled-and-cropped IMAGE you fed MiniMaxH3ImageToVideo. In the examples that's ImageScale → ImageCrop → both nodes.
- vae - the video VAE (
minimax_h3_video_vae_int8_convrot.safetensors), not the audio one. It's only there to encode the 32×32 keyframe. The audio VAE is what decodes the finished audio later, via VAEDecodeAudio.
audio_positive goes to the conditioning input of the BasicGuider for your audio pass. audio_latent goes to that sampler's latent input. The sampler's output then does two jobs: into VAEDecodeAudio for the actual soundtrack, and into H3AudioFirstLockAudio's audio_latent to be welded onto the video pass.
Install
In ComfyUI-Manager, search for H3 AudioFirst X2 Stream. You also need H3 X2 Stream - every bundled example uses its save node - and for the fast LBH variant, H3 LBH X2 Stream. Manual route:
cd ComfyUI/custom_nodes
git clone https://github.com/sepiablue-ai/ComfyUI-H3-AudioFirst-X2-Stream
Restart ComfyUI. The pack itself has no Python dependencies (requirements.txt says so explicitly; the av and comfy-kitchen imports come in through X2 Stream). What it does need from ComfyUI is native MiniMax H3 support - MiniMaxH3ImageToVideo, MiniMaxH3SigmaShift and BlockSparseAttention, so ComfyUI 0.38.0 or newer.
Don't mix install methods. Manager installs into custom_nodes/h3-audiofirst-x2-stream; setup.bat looks for ComfyUI-H3-X2-Stream. Run both and the same nodes register twice.
Models are hand-placed into models/ subfolders: INT8 turbo checkpoint, Qwen3-VL text encoder, INT8 video VAE, fp32 audio VAE, X2 Detail VAE. Run 00_prepare_x2.json once first.
Where people get burned
The audio pass needs 6 steps minimum. The author tested 4, 5, 6 and 8; 4 and 5 still sounded metallic. Set the audio scheduler to 4 because "it's only 32×32" and you get exactly the artifact you installed the pack to remove. 8 is the default, and 6 saves about 2.5 seconds if you're impatient.
Length drift. Covered above, but it bites twice in the fast LBH workflow, where the length is set on Shared image conditioning and on this node.
Your image crop carries over. Whatever ImageScale and ImageCrop did upstream is what the audio gets conditioned on, and the video pass wants both dimensions as multiples of 32. Get the crop right - 544×960 in the examples - and both passes agree.
Once the voice is done you're one node away from locking it in. That's H3AudioFirstLockAudio, and it's where the actual "the video pass can't touch this" mechanism lives.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| positive | CONDITIONING | — | |
| image | IMAGE | — | |
| vae | VAE | — | |
| length | INT | 1245–3600 | Use the same frame count as the video pass |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| audio_positive | CONDITIONING | — |
| audio_latent | LATENT | — |