Zura · Blend Selected Word
Paste a mouth back into the shot without retiming a single frame
- generated_frames
- generated_audio
- edit
- VIDEO
The last node, and the one that decides whether it looks like a cut or a glitch
The video side of a dialogue edit runs the other way from most lip-sync workflows. LTX Dub-It doesn't re-animate your whole shot - it regenerates a crop of the face, speaking the new words. Zura · Blend Selected Word then composites only the mouth region of that regenerated crop back into the original frames, over the window where the replaced word lives, and swaps in the matching slice of the new audio.
Done right, it looks like the actor said the new word. Done wrong you get the classic smear - a mouth that doesn't belong to the head, sliding across half a second of footage. The Zura docs are refreshingly explicit that this came from a real bug: the generated range sat at 0–0.5 s while the selected words were later and longer, so a different head pose got interpolated across the wrong interval. The fix, in one line: nothing is retimed, ever.
How it works
edit (from ZuraDialogueSource) carries the source geometry, the frame range, the face box, the mouth ellipse and a per-frame tracking path. Your generated_frames come back from the Dub-It stage at the refined resolution.
The node then walks every source frame in order. Inside the word window it pulls the temporally matching generated frame, strips the guide padding, area-downsamples the supersampled crop into the native crop size, warps it by the offset between the source mouth centre and the generated one, and blends it through an elliptical mask centred on the lips. mouth_feather softens that mask edge (0.24 default), and a temporal ramp fades the replacement in and out over roughly 0.75 s at each end of the word so the swap doesn't pop.
Audio follows the same discipline: blend_audio is called with retiming explicitly disabled, so if the generated and source word durations disagree by more than a sample you get an error, not a stretch. The gain is matched by RMS and the joins get an audio_fade_ms ramp.
Then it encodes: H.264 CRF 16, yuv420p, AAC 256k, faststart - and afterwards verifies the export against the source's frame count, dimensions and frame rate. If they don't match, the whole render is rejected rather than written.
Inputs you'll set
generated_frames, generated_audio and edit all come off wires. The rest:
generated_word_start/generated_word_end- only meaningful if you settiming_modeto manual. Leavetiming_modeat source and these are ignored entirely, which is what you want: the tooltip spells it out ("Source uses original timestamps; it does not align generated speech"). If the delivery came out early or late, move the replacement range in the upstream picker, don't fudge these.audio_fade_ms- 12 by default. Raise it if a join clicks.mouth_feather- 0.24. Higher = softer, blurrier mouth edge; lower = sharper and more likely to show a seam.filename_prefix- output subfolder plus basename. Must stay inside ComfyUI's output directory and can't contain colons.temporal_resampling- nearest keeps one mouth pose per output frame; blend interpolates between adjacent generated frames. Nearest is the default and the docs prefer it, because blending blurs two distinct mouth shapes together.timing_mode- source or manual. Manual requires your replacement selection to be the same duration as the original within one frame.
Output is a single VIDEO. The node also writes, next to the finished .mp4, the generated-only video and audio (handy for A/B), plus a .json receipt listing changed_frames, the gain in dB, both ranges, effective context, crop mode, retimed: false and the verified export geometry.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/ZURAVFX/ComfyUI_zura_nodes
cd ComfyUI_zura_nodes && pip install -r requirements.txt
or Zura Nodes in ComfyUI Manager. Beyond the pack you need ffmpeg/ffprobe on PATH, the native LTX nodes from ComfyUI-LTXVideo, and the LTX 2.5 distilled INT8 stack plus the Dub-It 0.9 LoRA. The pack's docs flag that the 2.3 Dub-It adapter on 2.5 is "an experimental local combination, not an officially validated pairing" - worth knowing before you blame your settings.
Where people get burned
"Source changed after preparation." The finish stage re-hashes the source video. Re-encode, trim or move it after preparing and it stops. Re-run from ZuraDialogueSource.
"Generated video has X, expected Y. Rerun both LTX stages." Your Dub-It graph returned the wrong frame count or size - usually a stale cached result or a changed render_size between passes.
A smeared mouth. Almost always a timing mismatch: the replacement got mapped to a different instant than the source word. Check the preview, then move the selection upstream. The node refuses mismatches over one frame, but a match that's within a frame can still be the wrong frame.
Inputs (10)
| Name | Type | Default | Description |
|---|---|---|---|
| generated_frames | IMAGE | — | |
| generated_audio | AUDIO | — | |
| edit | ZURA_DIALOGUE_EDIT | — | |
| generated_word_start | FLOAT | 1.165 | — |
| generated_word_end | FLOAT | 1.600 | — |
| audio_fade_ms | FLOAT | 120–100 | — |
| mouth_feather | FLOAT | 0.240.01–1 | — |
| filename_prefix | STRING | dialogue/euros | — |
| temporal_resamplingopt | COMBO | nearest | Nearest keeps a single mouth pose per output frame. Blend interpolates adjacent generated frames. |
| timing_modeopt | COMBO | source | Source uses original timestamps; it does not align generated speech. Preview the replacement and move its range if the delivery differs. Neither mode retimes video or audio. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| VIDEO | VIDEO | — |