Nodes/H3 Relay/H3 · Append Window
ComfyUI Node

H3 · Append Window

Where two rendered windows stop being two windows

By akatz-ai·Created 3 months ago·Updated 17 days ago· 49
H3 · Append Window
  • frames
  • window
  • previous
  • audio
  • initial_history
  • initial_history_audio
  • H3_RELAY_WINDOW_STATE

Every windowed video pipeline has one dumb, load-bearing junction: the place where the last rendered window meets the new one. Get it right and nobody notices. Get it wrong and you get a duplicated frame every few seconds, or a one-frame hop, or audio that's a hundred milliseconds out by the end of the clip.

H3 · Append Window is that junction, and it is deliberately paranoid about it.

What it does

Inside the loop, it receives the decoded frames of the current window plus that window's record, checks that they line up with what's already been accepted, drops the overlap, and returns an updated state bundle containing the assembled frames so far, the assembled audio so far, and the list of windows it has processed.

The check is the interesting part. Each window record says where it starts and how much overlap gets discarded - one frame, when history is in play, because H3 regenerates the boundary frame from context. Append Window refuses to proceed unless retained_frames == source_start + discarded_overlap. That single assertion is what prevents both gap and double-frame: it means the state you passed in ends precisely where this window begins its delivery, no more and no less. Skip it and you get a timeline that's fine for two windows and visibly broken by the sixth.

It also tolerates the one failure you can't avoid: if H3's decoder hands back one frame fewer than the window size (which happens at the tail of a run), the node logs a warning and holds the last frame to keep the clock intact. Any other count mismatch is an error, because silently stretching a render is worse than stopping.

Inputs and outputs

Required: frames (decoded IMAGE from your native VAE Decode) and window (from Window Inputs - this is a single H3_RELAY_WINDOW, not the whole plan). Optional: previous, the carry socket from Start Loop, which is where accumulated state comes from; audio (the decoded audio for this window, and if you omit it you get a silent assembly); initial_history and initial_history_audio for the segment case where you start from saved frames instead of from nothing.

One output: H3_RELAY_WINDOW_STATE. Wire it to End Loop's output_value and next_iteration_value - it's both the accumulator and the carry. Everything downstream (Finish Windows, or Review Windows' durable checkpoint path) consumes that state.

There's a hidden behaviour worth knowing about: if the window record carries a checkpoint key - which is what Review Windows adds to pending windows - Append Window also writes a per-window record into the durable cache in ComfyUI/output/__h3_removal_cache/ and publishes a preview. That's why the checkpointed path demands decoded audio: a record without audio can't reproduce the continuation.

Install

Manager → H3 Relay → restart, or:

cd /path/to/ComfyUI/custom_nodes
git clone https://github.com/akatz-ai/h3-relay.git

Nothing extra for this node - no ffmpeg, no weights, no pip packages. It runs as soon as ComfyUI can load the pack, which needs 0.37.0+ for the native loop schema. The models are only required elsewhere in the graph, and they carry the MiniMax H3 Community Licence (no US, EU, UK or Korea) - worth checking before you invest in a 40-window run.

Where people get burned

The classic is audio without history: you wire audio but forget that a continuation window's assembled audio needs previous's audio at a matching sample rate. The node will tell you Missing matching generated audio history, which sounds cryptic until you realise it's saying "you disconnected the carry socket."

Second: decoded audio must sit on the source clock - the node slices by 24 fps frame indices and refuses if the resulting length doesn't match end of the window. If you re-encoded your audio through something that padded the tail, this is where you'll find out.

Third, and this one is a design constraint rather than a bug: the state holds every frame of the assembled clip in RAM. That's fine for a 20-second shot and grows linearly forever. For long sequences the pack's staged, disk-backed path exists precisely because this node's accumulator doesn't scale - use it for anything past a few windows.

CategoryH3 Relay/timeline

Inputs (6)

NameTypeDefaultDescription
framesIMAGE—
windowH3_RELAY_WINDOW—
previousoptH3_RELAY_WINDOW_STATE—
audiooptAUDIO—
initial_historyoptIMAGE—
initial_history_audiooptAUDIO—

Outputs (1)

NameTypeDescription
H3_RELAY_WINDOW_STATEH3_RELAY_WINDOW_STATE—