Nodes/Loop Strip/Loop Strip — Find Best Cycle
ComfyUI Node

Loop Strip — Find Best Cycle

Let the math find your seamless walk loop

By serhiiyashyn-sf·Created 5 months ago·Updated 4 months ago· 0
Loop Strip — Find Best Cycle
  • frames
  • loop_frames
  • grid_1
  • grid_2
  • grid_3
  • grid_4
num_frames8
min_cycle_pct0.15
max_cycle_pct0.80
grid_cols1
grid_rows1

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:

  1. Every frame gets downscaled to a 160px grayscale thumbnail - fast to compare, all the leg information survives.
  2. 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.
  3. scipy.signal.find_peaks finds the stride peaks, and each peak-to-next-peak+2 pair is a candidate walk cycle.
  4. Candidates are scored on boundary match between start and end frames (weighted 5×), half-cycle symmetry (2×), how evenly num_frames slots into the cycle (1.5×), and motion stalls (1×).
  5. 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_1grid_4 outputs 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.

CategoryLoopStrip

Inputs (6)

NameTypeDefaultDescription
framesIMAGE
num_framesINT82–64Number of frames to extract from the loop cycle
min_cycle_pctFLOAT0.150.05–0.5Minimum cycle length as fraction of total frames
max_cycle_pctFLOAT0.800.3–1Maximum cycle length as fraction of total frames
grid_colsINT11–4Grid columns (1 = no grid split)
grid_rowsINT11–4Grid rows (1 = no grid split)

Outputs (5)

NameTypeDescription
loop_framesIMAGE
grid_1IMAGE
grid_2IMAGE
grid_3IMAGE
grid_4IMAGE