Nodes/Link Comfy Nodes/WAN Loop Frames
ComfyUI Node

WAN Loop Frames

Inpaint the seam instead of generating the loop twice

By Mister-Link·Created 11 months ago·Updated 4 days ago· 1
WAN Loop Frames
  • frames
  • frames
  • mask
  • frame_count
  • metadata
  • context_frames
  • raw_frames
◄frames_to_cut0►
◄frames_to_add0►
◄context_frames4►
◄preferenceadd frames►

Wan has no native loop mode, and for a long time the answer was "generate a whole second clip that ends where the first one starts." Expensive, and the join usually morphs rather than cycles, because you're asking the model to invent a transition into a frame it never saw as a destination.

WAN Loop Frames takes the cheap route: it doesn't generate a loop. It carves out a few frames of your animation around the seam, blanks the middle of them, and hands the sampler a tiny masked segment to fill - the handful of frames between "end of clip" and "start of clip." Then you play the result on repeat.

The mechanism, because it's clever and non-obvious

You feed it the full source animation. It cuts frames off - alternately from the beginning and the end (frames_to_cut), per the tooltip. Then it does a fixed 50% scoot: split the trimmed animation at its midpoint and reorder it, second half first. Now the tail of your clip and the head of your clip sit adjacent.

That adjacency is the seam. Into the gap it inserts frames_to_add blank frames, keeping context_frames real frames on either side - the last of the tail and the first of the head. Out comes a small segment: before-context, blanks, after-context. Naming quirk worth knowing: the "before" context is the end of your animation, and the "after" context is the beginning. That's the wrap, not a bug.

The mask output is black over the context frames and white over the blanks to fill. White means "change this," black means "leave it alone" - the standard masked-inpainting convention, so it plugs into the same masked-sampler path as any image inpaint.

Then the arithmetic. Wan's 3D causal VAE compresses time roughly 4×, so valid lengths are 1 + 4n, full stop - the same trap that turns a 111-frame request into a 109-frame clip. This node resolves it in two places: the final full-animation count and the context segment itself are both rounded to 1 + 4n. preference picks the direction - add frames rounds up and keeps every source frame, cut frames rounds down, eating into your context to get there and raising if it can't reach a valid count at all.

Inputs and outputs

Four required inputs. frames is the source animation; frames_to_cut is how many frames you'll sacrifice so the seam lands sensibly; frames_to_add is how many blanks the sampler gets to invent (that's your loop bridge's length); context_frames (default 4) is the real footage the model sees while inventing - more of it, better-informed bridge, slower sample.

Six outputs, and you need all of them:

  • frames - only the context segment; this goes into encode → sampler.
  • mask - the white-to-inpaint mask for that segment.
  • frame_count - final full-animation length after adjustment; your save/encode length.
  • metadata - a JSON string that must reach WAN Unloop Frames untouched.
  • context_frames - the segment's actual frame count; wire it into whatever sets your sampler length.
  • raw_frames - the untouched original batch, for the merge step.

Wire segment and mask into your masked sampling path, decode, then hand the decoded segment plus raw_frames plus metadata to WAN Unloop Frames, which puts the generated bridge where it belongs. The two nodes are a matched pair.

Installing it

Manager, search Link Comfy Nodes, restart. Manually:

cd ComfyUI/custom_nodes
git clone https://github.com/Mister-Link/link-comfy-nodes

Heads up: the README is one sentence - "AI slop nodes for my own purposes, use at your own risk (I change how nodes behave without warning)." And because the pack imports every node on load, a failure anywhere in requirements.txt - which includes a git-sourced clip @ git+https://github.com/openai/CLIP.git line plus pytorch_lightning and mambapy, neither of which these WAN nodes need - takes the whole pack down.

Where people get burned

context_frames has to fit. The error tells you exactly how many frames are available "before" and "after" the midpoint, which is unusually helpful - read it instead of over-cutting. And context_frames of 0 with frames_to_add of 0 produces an empty segment, which also raises.

The bridge is only as good as the seam. If your clip's first and last frames are wildly different compositions, no amount of context will hide the pop - this node makes the transition cheap, not plausible. And its behavior has already changed once in a noticeable way (the newest commit is about loop context beyond the cap frames), so re-check your outputs after updating.

Categoryconditioning/video_models

Inputs (5)

NameTypeDefaultDescription
framesIMAGEFull source animation. Only a midpoint context segment is output for sampling.
frames_to_cutINT00–9998Source frames removed alternately from the beginning and end; at least one frame is always preserved.
frames_to_addINT00–9998Requested number of blank masked frames inserted at the midpoint seam.
context_framesINT40–9998Number of source context frames included before and after the blank inpaint frames.
preferenceCOMBOadd framesIf the requested full-animation result is not 1 + 4n, round the blank-frame count up or down.

Outputs (6)

NameTypeDescription
framesIMAGEOnly the requested context segment: before-context, blank frames, after-context.
maskMASKBlack for context frames and white for the blank frames to inpaint.
frame_countINTFinal full-animation frame count after WAN 1 + 4n adjustment.
metadataSTRINGMetadata for WAN Unloop Frames to merge the inpainted segment back into the full animation.
context_framesINTActual number of frames in the context segment for the sampler length.
raw_framesIMAGEUntouched original frame batch for WAN Unloop Frames.