Select loop segment
Find the one part of a shot that loops cleanly
- image
- ui_widget
- image
- receipt
- image_list
You generated a five-second clip of a character walking and you want a looping animation sprite for a game. There's a clean cycle in there somewhere - but it isn't the whole clip, and it isn't the bit you'd pick by eye, because the eye can't compare frame 3 to frame 27 across forty candidates.
LF_SelectLoopSegment does that comparison for you. It scans a shot for the segment whose end looks most like its own beginning and which actually moves, then resamples that cycle to exactly the frame count you need for an atlas.
How it works
Two scores, and the second one is the part that makes it useful.
The seam score compares each candidate start frame to the frame that would follow the end of its cycle - a small difference means the loop closes smoothly. Left alone, that metric has an obvious failure mode: a completely frozen stretch of video has a near-perfect seam and would win every time. So there's a motion flavour too, the mean inter-frame step inside the candidate, and it must be at least motion_floor times the shot's median step. A dead stretch is disqualified rather than crowned.
Candidates are ranked by the ratio of seam to motion, which is the right way round: a segment that closes well and moves confidently beats one that closes perfectly and does nothing. Ties go to the shortest cycle, so an exactly periodic shot doesn't hand you the longest possible interpretation of itself.
Then the chosen cycle gets resampled to target_count frames excluding the final endpoint - that's the deliberate bit that keeps the loop closed. If you sampled both endpoints, the first and last frames would be identical and your animation would visibly stall for one frame every cycle.
Analysis runs on greyscale, area-downsampled copies (analysis_max_edge, 96 by default), so it's cheap and doesn't pretend to be photometric. Every candidate scoring is deterministic: same frames in, same cycle out.
The inputs and outputs that matter
image- ordered frames of one shot, typically a decoded video.source_fps- the source's real frame rate. It only derives the reported cycle seconds, but get it right or the receipt lies to you.target_count- frames in the output loop, default 24. Your atlas's frame budget.min_period_frames/max_period_frames- the cycle lengths worth considering, default 12 to 96. Narrow this and the search is faster and more predictable.motion_floor- default 0.6, i.e. a candidate must move at least 60% as much as the shot's typical frame step. Raise it to reject sluggish cycles.analysis_max_edge(optional, 96) - resolution of the analysis copies. Higher notices smaller changes, costs more time.
Outputs: image (the sampled loop frames as one batch, playback order, pixels untouched), image_list (the same, individually), and receipt - chosen start and period, seam and motion scores, the top five candidates, cycle seconds and the sampled source indices. Read the receipt when a loop looks wrong; the top candidates tell you whether the winner was a near-miss or whether the whole shot had no usable cycle.
How to install it
ComfyUI Manager → search LF Nodes → install → restart, or:
cd ComfyUI/custom_nodes
git clone https://github.com/lucafoscili/lf-nodes
python -m pip install -r lf-nodes/requirements.txt
Pip with ComfyUI's interpreter. Two pack notes: requirements are broad (154 nodes - torch, opencv, rembg, av, transformers), and lucafoscili/comfyui-lf is the archived repo; lf-nodes is current.
The pack ships a SpriteLoopCut Workflow Runner card that runs this node on an uploaded shot, then background-removal, sprite normalization and an atlas grid. Opt-in - WORKFLOW_RUNNER_ENABLED=true before starting ComfyUI.
Common issues & troubleshooting
"No candidate loop found." The error message is unusually helpful and tells you the three levers: lower motion_floor, widen the period range, or shorten target_count. The most common cause is a period range narrower than the actual cycle in your clip.
It picked a boring stretch. motion_floor is too low. Raise it - the whole reason it exists is to stop a frozen shot from winning on an artificially tiny seam.
The loop still hitches. One frame of hitch usually means the source frames aren't evenly spaced in time (a video with variable frame rate decoded to a constant frame list), or source_fps is wrong. Check the receipt's sampledSourceIndices - evenly-spaced indices with a hitch means the pixels stutter, not the sampling.
Three frames of video, error thrown. Minimum three frames to find a loop at all. Decode more of the clip.
The output count isn't what I asked for. target_count is exactly what you get, up to the cycle's own resolution - if you ask for 64 frames from a 12-frame cycle, you're getting interpolated-by-repetition samples rather than new information. Keep target_count at or below the cycle length for anything that matters.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | Ordered frames of one shot (for example a decoded video). The best self-closing segment is chosen and resampled to target_count frames, excluding the final endpoint so the loop closes. | |
| source_fps | FLOAT | 24.000.01–1000 | Frame rate of the source frames; derives cycle seconds. |
| target_count | INT | 241–4096 | Frames in the output loop. Periods shorter than this are ignored. |
| min_period_frames | INT | 122–4096 | Shortest cycle to consider, in source frames. |
| max_period_frames | INT | 962–4096 | Longest cycle to consider, in source frames (clamped to the shot). |
| motion_floor | FLOAT | 0.600–4 | Minimum mean inter-frame motion inside a candidate, as a multiple of the shot's median step. Keeps frozen stretches from winning. |
| analysis_max_edgeopt | INT | 968–1024 | Longest edge of the grey analysis copies. |
| ui_widgetopt | LF_MASONRY | [object Object] | — |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | Sampled loop frames as one batch, in playback order; pixels are unchanged. |
| receipt | JSON | Loop-selection receipt: chosen start/period, seam and motion scores, sampled indices. |
| image_list | IMAGE | The same sampled frames as an authoritative list. |