Loop Strip — Find Best Cycle
Let the math find your seamless walk loop
- frames
- loop_frames
- grid_1
- grid_2
- grid_3
- grid_4
This is the node that makes the Loop Strip pack worth installing. Point it at a video of a character walking and it tells you which frames form a seamless loop - the thing you'd otherwise burn an afternoon hunting by eye, scrubbing back and forth until a footstep stops jarring.
You've seen the result a thousand times in 2D games: a sprite sheet where frame 1 and frame N are close enough that the walk reads as infinite. Finding that boundary by hand on a 300-frame clip is miserable. Find Best Cycle does it from the silhouette.
How it works
It's smarter than a naive "first frame ≈ last frame" check. The pipeline, from the source:
- Every frame gets downscaled to a 160px grayscale thumbnail - fast to compare, all the leg information survives.
- Background detection plus Otsu thresholding builds a mask of the character, and the bottom 40% of that silhouette gives a leg-spread signal: peaks when the legs are apart mid-stride, valleys when they pass each other.
scipy.signal.find_peaksfinds the stride peaks, and each peak-to-next-peak+2 pair is a candidate walk cycle.- Candidates are scored on boundary match between start and end frames (weighted 5×), half-cycle symmetry (2×), how evenly
num_framesslots into the cycle (1.5×), and motion stalls (1×). - The winner gets its end refined ±5 frames, then it picks the in-cycle offset that makes your last extracted frame line up with the first - that's the seamless part.
If peak detection fails (busy background, no clean legs), it quietly falls back to a sliding-window scan over your whole min-to-max range. Either way it prints [LoopStrip] lines to the console so you can see what it chose and why.
The inputs that matter
Only a few are worth touching:
- num_frames (default 8, up to 64) - how many frames you want in the finished strip. Lower = chunkier animation, higher = smoother.
- min_cycle_pct / max_cycle_pct (defaults 0.15 / 0.8) - the search range as a fraction of the clip. If it keeps grabbing a half-cycle, raise the minimum.
- grid_cols / grid_rows - set both above 1 to split a multi-character grid video (like a 2×2) and apply the same cycle to every cell, synced. That's what the
grid_1–grid_4outputs are for.
Everything else, leave alone.
Wiring it up
Feed it the frames output of Loop Strip - Load Video. The main loop_frames output goes straight into Loop Strip - Assemble Sprite Strip, usually with a centering node in between if your subject drifts. If you're using the grid feature, take the cell you care about.
Install
Easiest via ComfyUI Manager - search "Loop Strip" and hit install. Or manually:
cd ComfyUI/custom_nodes
git clone https://github.com/serhiiyashyn-sf/comfyui-loopstrip
cd comfyui-loopstrip
pip install -r requirements.txt
Then restart ComfyUI. Dependencies are opencv-python, numpy, torch, scipy, and requests - you almost certainly already have all of them in a working ComfyUI install, so there's nothing heavy to fetch. No model files are required.
Where people get burned
This node assumes a character it can separate from the background. A clean or plain background makes the mask trivial; a cluttered scene confuses the Otsu step and you get a nonsense "best" cycle. If the console shows a fallback or a weird cycle, simplify the backdrop or trim the clip to one clean pass before you run it. And remember: all frames live in memory, so don't feed it a ten-minute video expecting miracles.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| frames | IMAGE | — | |
| num_frames | INT | 82–64 | Number of frames to extract from the loop cycle |
| min_cycle_pct | FLOAT | 0.150.05–0.5 | Minimum cycle length as fraction of total frames |
| max_cycle_pct | FLOAT | 0.800.3–1 | Maximum cycle length as fraction of total frames |
| grid_cols | INT | 11–4 | Grid columns (1 = no grid split) |
| grid_rows | INT | 11–4 | Grid rows (1 = no grid split) |
Outputs (5)
| Name | Type | Description |
|---|---|---|
| loop_frames | IMAGE | — |
| grid_1 | IMAGE | — |
| grid_2 | IMAGE | — |
| grid_3 | IMAGE | — |
| grid_4 | IMAGE | — |