H3 Relay Windowed Edit · Character Swap
H3 Relay Windowed Edit is how you swap a character into footage you already shot
- model
- clip
- video_vae
- audio_vae
- source
- reference_image
- source_audio
- edited_frames
- window_plan
The character-consistency problem got rerouted. Instead of trying to generate the same person twice, everyone now generates once and edits - feed a reference and source footage to a video model and let it restage the shot. MiniMax H3 does this well enough that "replace the person in the reference video with the person from the reference image" became a launch-week party trick.
The catch is the one you'd predict: H3's honest range is 4–15 seconds. H3 Relay Windowed Edit · Character Swap is the wrapper that takes a longer source clip, splits it into H3-sized windows, carries generated history between them so the character doesn't change identity halfway through, and stitches the result back to exactly your source length.
What you wire in
The model side is native H3: model, clip, video_vae, audio_vae. Both VAEs are used - H3 generates picture and audio together, and the reference conditioning needs the audio VAE too. The pack's Pack LTX Model node isn't involved here; that's for the LTX finishing path.
Then the content: source (your prepared frames), reference_image (the character - only the first image is used, matching the single-character contract), and source_audio optionally, if you want the original soundtrack available.
Settings worth understanding:
prompt- defaults toReplace the person in <Video 1> with the character in <Picture 1>. Preserve motion, framing and scene.Those<Video 1>/<Picture 1>tokens are H3's native reference addressing, not decoration.window_frames- maximum target size per window, default"124"(5.17s). Short clips and final windows shrink automatically to the smallest valid size that fits.history_frames-0for independent windows,1for a generated boundary frame only, or18/35/52… (17k+1) for real sliding history. Default 18. It must be smaller than the window.seed- the master. Window one uses it, window two uses master + 1, and so on. Keep it fixed and reroll individual windows; changing it starts everything over.steps8,cfg1,sampler_nameer_sde,schedulersimple,denoise1 - the native H3 defaults.source_countexists so you can pass the preparation node'ssource_framesand exclude the padding that prep adds to reach the H3 grid.
Two outputs, and one of them is not a video
edited_frames is the assembled IMAGE batch - feed it to a video-saving node. window_plan is a JSON string describing every actual source range, target size, effective history, padded tail and discarded boundary, plus the policy tag adaptive-target-v1. It looks like a debugging leftover; keep it. When a seam looks wrong, the plan tells you which window is responsible, and an unchanged plan is part of what makes a checkpoint reusable.
Watching it work
Each completed window publishes a small MP4 preview card on the node before the next window starts - hover to play, tap on a touch screen. Every card has a seed field and a Reroll button: rerolling window four keeps one through three and rebuilds four and everything after it, which is the behaviour you want, because later windows inherit the changed history.
Ordinary Run with unchanged inputs reuses completed windows, including across a restart. There are no lock toggles to manage. Change the prompt, the reference, the source pixels, the window or history size, a sampler setting or a model file and the whole set resets with the old checkpoints left on disk. Rationale: reuse checks real input bytes and a hash of the model graph, not filenames.
Install
cd /path/to/ComfyUI/custom_nodes
git clone https://github.com/akatz-ai/h3-relay.git
Manager → H3 Relay → install → restart. Needs ComfyUI 0.32.0 or newer, FFmpeg on PATH, the H3 FL2VA/Ref2VA diffusion models, Qwen3VL text encoder and both H3 VAEs from the pack's MODELS.md. No pip dependencies - dependencies = [], which for a video pack is a small mercy.
Where people get burned
- Not 24 fps, or dimensions not divisible by 32. The node rejects both up front. Use H3 Prepare Video · Auto Size & Length first; it normalizes frame rate and lands you on the grid.
- A 30-second clip on a 24GB card. Windows accumulate CPU RAM and disk. This is an experimentation workflow, not a streaming implementation - the pack's disk-staged long-form runner is a different chain (Sequence Start → Generate Shot → Assemble). Start with ten seconds.
- Expecting pixel-identical preservation outside the person. An edit model re-emits the whole frame. Background text, reflections and occlusions will move. No prompt fixes that; it's structural.
- Identity drifting anyway. Check history first -
history_frames=0means each window is independent and the character will wander. Then check that you're not changing settings between runs, since a configuration reset silently regenerates everything with fresh derived seeds. - The character reference is only the first image. Batch in a grid of poses, not a batch of frames, if you want more than one view.
- License. H3's weights are territory-restricted (the US, EU, UK and South Korea are outside the license's Applicable Territory). The pack's GPL-3.0 says nothing about your model rights.
Inputs (19)
| Name | Type | Default | Description |
|---|---|---|---|
| model | MODEL | — | |
| clip | CLIP | — | |
| video_vae | VAE | — | |
| audio_vae | VAE | — | |
| source | IMAGE | — | |
| reference_image | IMAGE | — | |
| fps | FLOAT | 24.00 | — |
| prompt | STRING | Replace the person in <Video 1> with the character in <Picture 1>. Preserve motion, framing and scene. | — |
| window_frames | COMBO | 124 | Maximum generated frames per window; short clips and tails shrink automatically, excluding past history: 107 = 4.46s, 124 = 5.17s. Valid H3 sizes are 17n+5. |
| history_frames | COMBO | 18 | 0 = independent windows; 1 = generated boundary only; 18/35/52... = H3 sliding history. Includes one boundary frame. Must be smaller than window_frames. |
| seed | INT | 9042340–18446744073709550000 | Master seed. Window 1 uses this value; later windows use master + window index. Changing it starts fresh windows and clears per-window overrides. Keep fixed to reroll individual windows. |
| steps | INT | 81–100 | — |
| cfg | FLOAT | 1.000–100 | — |
| sampler_name | STRING | er_sde | — |
| scheduler | STRING | simple | — |
| denoise | FLOAT | 1.000–1 | — |
| source_audioopt | AUDIO | — | |
| source_countopt | INT | 0 | 0 uses all input frames. Connect preparation's source_frames to exclude its model-padding tail. |
| window_controlsopt | STRING | {} | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| edited_frames | IMAGE | — |
| window_plan | STRING | — |