WAN Loop Frames
Inpaint the seam instead of generating the loop twice
- frames
- frames
- mask
- frame_count
- metadata
- context_frames
- raw_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.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| frames | IMAGE | Full source animation. Only a midpoint context segment is output for sampling. | |
| frames_to_cut | INT | 00–9998 | Source frames removed alternately from the beginning and end; at least one frame is always preserved. |
| frames_to_add | INT | 00–9998 | Requested number of blank masked frames inserted at the midpoint seam. |
| context_frames | INT | 40–9998 | Number of source context frames included before and after the blank inpaint frames. |
| preference | COMBO | add frames | If the requested full-animation result is not 1 + 4n, round the blank-frame count up or down. |
Outputs (6)
| Name | Type | Description |
|---|---|---|
| frames | IMAGE | Only the requested context segment: before-context, blank frames, after-context. |
| mask | MASK | Black for context frames and white for the blank frames to inpaint. |
| frame_count | INT | Final full-animation frame count after WAN 1 + 4n adjustment. |
| metadata | STRING | Metadata for WAN Unloop Frames to merge the inpainted segment back into the full animation. |
| context_frames | INT | Actual number of frames in the context segment for the sampler length. |
| raw_frames | IMAGE | Untouched original frame batch for WAN Unloop Frames. |