TS H3 Audio Inject
Make MiniMax H3 say the line you actually recorded
- latent
- audio
- audio_vae
- latent
MiniMax H3 makes the picture and a stereo soundtrack in the same pass. Great for a clip of someone talking; useless for a clip of someone talking your line - ask for a sentence and you get a voice that sounds close, saying something adjacent. No prompt fixes that.
The stock Add Guide for MiniMax H3 doesn't either: it hands the audio in as a condition, the model listens, then generates its own soundtrack anyway. TS H3 Audio Inject writes your track into the latent and makes the sampler hold it, so the mouth has to match the words.
Where this sits in the audio-video zoo
Audio-driven video splits into two camps: models that invent sound with the picture (LTX-2, H3), and avatar models that consume a track you already have (InfiniteTalk, MultiTalk, Wan S2V). This node is a third option - put the track into the generator that makes sound, and H3's motion and mouth fall in line. No avatar pass, no dubbing workflow. Reach for it when a character has to deliver a recorded voice-over, a TTS line, or a sung phrase.
How it works
H3 samples video and audio as one latent: a nested pair, video [B,24,T,H/16,W/16] and audio [B,32,2,T*40], with attention running across both. Because they share a sequence, pinning the audio rows at a known soundtrack leaves the video rows nothing to do but agree with them.
The node encodes your waveform with the H3 audio VAE, in stereo, and writes it into the audio half. Then it attaches a nested noise_mask: ones over the video stream (denoise freely), zeros over the audio stream (preserve this).
A stock KSampler does the injecting through ComfyUI's own masked-sampling path - no model patching, no sampler wrapper. Core's MiniMaxH3.scale_latent_inpaint splits the nested latent, converts the audio between H3's two flow schedules (video shift 12.0, audio 3.0), and re-injects it clean. The decoded clip therefore carries exactly your track through the VAE, not something resembling it.
MiniMax H3 Image to Video -> latent -> TS H3 Audio Inject -> KSampler -> VAEDecode
^ audio ^ audio_vae
The three inputs
All required, no knobs, because there aren't any worth having:
- latent - the H3 audio-video latent, from
Empty MiniMax H3 Latent AVorMiniMax H3 Image to Video. Anything else and it raises rather than producing nonsense. - audio - the soundtrack to lock to. Shorter than the clip is padded with real silence, and that padding happens on the waveform before encoding precisely because the VAE normalises its latents - zero latent frames would decode as a hum at the tail. Longer is trimmed. Mono is duplicated to stereo; a wrong sample rate is resampled to the VAE's 32 kHz.
- audio_vae - the same H3 audio VAE you decode with.
One output: latent, your latent with the soundtrack written in and the audio stream marked preserved.
Get the frame count right
H3 only accepts clip lengths of 17n + 5 at 24 fps, so video length is derived from audio length. Use the exact duration - duration_seconds from the pack's TS Audio Loader, not the whole-second duration, which rounds up and leaves a tail the model invents for nothing:
max(5, ceil(a * 24 + 0.2)) + (5 - (max(5, ceil(a * 24 + 0.2)) % 17)) % 17
That 0.2 isn't a safety fudge. Audio runs on its own grid of 40 latent frames per second and the slot only ever misses the ideal by 1/120 s, so 0.2 of a frame covers it exactly. Without it, phrase endings get clipped.
Unless the frame count divides by 3, audio and video land up to 8.33 ms apart - a fifth of a frame, and not worth forcing, since making it divide costs 16 extra frames on average. Lay the original track at frame 0 and let it be.
Install
ComfyUI Manager → search Timesaver, install, restart. Or:
cd ComfyUI/custom_nodes
git clone https://github.com/AlexYez/comfyui-timesaver
cd comfyui-timesaver
python -m pip install -r requirements.txt
Restart fully, not just the browser tab. This node downloads no models of its own - you need the H3 weights and audio VAE for the rest of the graph anyway - and none of the pack's optional dependencies apply. On the Windows portable build, run pip from python_embeded\python.exe.
When it goes wrong
- "expects a MiniMax H3 audio-video latent" - you fed it a plain video latent, or something upstream didn't produce the nested pair.
- Encoded soundtrack shape doesn't match the audio stream - wrong
audio_vae. It wants H3's audio VAE, nothing else. - A phantom word, or an invented tail - frames computed from the rounded
duration, or a track that starts late. The node logs exactly what it locked: seconds, latent frames, padded or trimmed, frame count, and the audio-video offset in milliseconds. - Muxing the original file back on at the end - the clip's audio is already your track. Connect audio to a saver only if you mean to replace it; a connected input always wins.
For anything else, start ComfyUI with TS_VERBOSE_STARTUP=1 for the pack's full load report instead of the one-line summary.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| latent | LATENT | MiniMax H3 audio-video latent, from Empty MiniMax H3 Latent AV or MiniMax H3 Image to Video. | |
| audio | AUDIO | Soundtrack to lock the clip to. Shorter audio is padded with silence, longer audio is trimmed. | |
| audio_vae | VAE | MiniMax H3 audio VAE (the same one the decode side uses). |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| latent | LATENT | The same latent with the soundtrack written in and its audio stream marked preserved. |