Nodes/ComfyUI_zura_nodes/Zura · Save H3 Continuation
ComfyUI Node

Zura · Save H3 Continuation

The two tensors that make a seamless H3 join possible

By ZURAVFX·Created about a month ago·Updated a day ago· 2
Zura · Save H3 Continuation
  • sampled_latent
  • generated_native_frames
  • continuation_state
◄window_json{}►
◄generation_digest►
◄model_digest►

MiniMax H3 in ComfyUI samples in 124-frame windows - about five seconds at 24 fps, against the up-to-15-seconds the model advertises through the hosted API. Chain those windows naively and the second one invents a fresh performance: same person, different take, mouth in the wrong place. This node is where the handoff happens, and what it writes is smaller than you'd expect.

What it writes

Two things, into output/genj/h3_continuation/<generation_digest>/window-NNNNNN/:

  • A two-token video prefix, extracted from the sampled latent. That's extract_video_prefix on the 24-channel H3 latent - the first two video tokens of the window, which is the most compact representation of "the motion state at this boundary" H3 will accept.
  • Five real decoded frames - the last five of the native 124-frame decode, saved as a guide image so the next window's sampler starts from pixels rather than an abstraction.

Plus a manifest recording the raster, the previous window's bounds, the tensor hashes and shapes, the context and prefix lengths, and both digests. The files are state.safetensors and state.json, written to a temp directory and published with an atomic rename - so an interrupted run either leaves a complete window or nothing, never a half-written state that the loader will happily read.

The correctness argument is in the H3 token arithmetic: 2 + 5 * ((valid_frames - 5) / 17). Only windows whose valid frame count fits that progression can be saved, which is why the node insists the window be non-terminal and same-shot.

Why it's an output node

It's marked OUTPUT_NODE = True, so ComfyUI executes it every run even when nothing downstream needs it. That's correct for state-writing: the save is a side effect, not a wire, and a graph that skips it would produce a chain with a hole in it.

Inputs and outputs

  • sampled_latent - LATENT, this window's sampled output.
  • generated_native_frames - IMAGE. Read the code comment before wiring this: it must come from the visible native H3 VAEDecode, before any preview resize, crop, or export conversion. The node requires exactly 124 frames, 3 channels, finite values in 0–1, CPU. A resized preview pipe will be rejected, and rightly so.
  • window_json - STRING, the planner's record for the window being saved.
  • generation_digest, model_digest - STRING, lowercase SHA-256 digests. The same pair you'll hand to the loader next window; a mismatch there is the "belongs to a different generation" error.
  • continuation_state - H3_CONTINUATION, straight into Zura · Apply H3 Continuation (or via the load node after a graph split).

Install

cd ComfyUI/custom_nodes && git clone https://github.com/ZURAVFX/ComfyUI_zura_nodes
pip install -r ComfyUI_zura_nodes/requirements.txt

Restart. safetensors comes with ComfyUI. The H3 weights are the big separate downloads, and the H3 community licence is territorial - the weights are not licensed everywhere the API is available.

Common issues

"Save the full 124-frame native H3 decode as finite CPU IMAGE data." Your generated_native_frames isn't the raw native decode. Most often it's been through an upscale or a preview resize - the message means "wire the VAEDecode output directly," and it's worth checking before you resize anything in that chain, because the state can't be reconstructed from a rescaled guide.

"Only a nonterminal same-shot window can save continuation state." You're saving the last window of a shot, or saving at a cut. The final window of a shot has nothing to hand forward - continuation never reaches across a cut, which is deliberate: hard cuts should stay hard.

"The sampled H3 latent raster does not match its native decoded guide." The latent and the decode came from different rasters. Almost always a resolution change between sampling and decoding, or a leftover wire from an earlier experiment.

"This cache key already contains different video latent data." Not from this node - that one's the video latent cache - but the same family of confusion applies here: if the save finds an existing window directory whose manifest doesn't match, it refuses rather than overwriting. A completed chain is immutable, and the way to redo it is to change the generation.

Starting over. Clear the chain by starting from window 1 with a new generation digest. There's no "overwrite window 3" affordance, and honestly you don't want one - a half-replaced chain is exactly the silent-corruption case the validation exists to catch.

CategoryZura/Artist Studio/H3 Windows

Inputs (5)

NameTypeDefaultDescription
sampled_latentLATENT—
generated_native_framesIMAGE—
window_jsonSTRING{}—
generation_digestSTRING—
model_digestSTRING—

Outputs (1)

NameTypeDescription
continuation_stateH3_CONTINUATION—