Nodes/lf-nodes/Periodic image batch sampler
ComfyUI Node

Periodic image batch sampler

Turn a 24fps batch into a smooth looping animation

By lucafoscili·Created 2 years ago·Updated a day ago· 35
Periodic image batch sampler
  • image
  • ui_widget
  • image
  • receipt
  • image_list
target_count24
loop_endpoint_policyexclude_final_endpoint
source_fps24.00
intended_fps12.00

Looping animations have a nasty habit: you generate a batch of frames, string them together, and the seam where the last frame meets the first jumps like the video got hiccuped. LF_PeriodicImageBatchSampler is the tool that fixes the seams by being very precise about which frames it keeps - it resamples an ordered image batch down to an exact frame count using pure tensor indexing, no interpolation, no pixel math, no quality loss.

The name undersells what it's actually for. "Periodic" here means it selects frames at regular intervals to hit an exact target count: you've got a 48-frame batch and want a 24-frame loop, or a 24fps source you want to re-time to 12fps. Because selection is done by nearest-index math on the tensor (index_select), every frame that survives is the original frame, byte-identical - there's no blending of in-between states, which is exactly what you want when you'd rather keep a crisp loop than smear frames together.

The two loop policies, and why one is a trap

  • exclude_final_endpoint (default) - samples only the frames before the source's final endpoint. The thinking: for a closed loop, you reuse your opening frame as the ending endpoint, so including the last frame would double it and break the seam. This mode requires at least two source frames and target_count can't exceed source_count - 1.
  • include_final_endpoint - keeps the final frame as the endpoint. Use this when the loop legitimately ends on a distinct frame; target_count can't exceed source_count.

That first mode is the one people trip over: set target_count equal to the full source count and the node will error out, because under exclude_final_endpoint you physically can't keep all the frames. It's not a bug - it's the tool telling you your math doesn't close the loop.

Inputs and outputs

  • image - the ordered batch. Sampling is lossless; dimensions and values survive untouched.
  • target_count - the exact number of output frames (default 24).
  • loop_endpoint_policy - as above.
  • source_fps / intended_fps - defaults 24 and 12. Honest truth: these don't drive the sampling; they're recorded in the receipt as timing metadata for whatever encodes the result. If you want the frames to play at a different rate, that decision happens downstream.

Outputs: image (exactly target_count frames), image_list (each frame individually, in output order), and receipt - a lf.periodic_image_batch_sampler.receipt.v1 record listing the selected indices, source/target frame counts, policy, and both FPS values. That receipt is genuinely useful for reproducibility: you can prove exactly which source frames made it into the loop.

Installing and gotchas

It ships in LF Nodes (Manager → search "LF Nodes", or):

cd ComfyUI/custom_nodes
git clone https://github.com/lucafoscili/lf-nodes.git

restart, and it's under ✨ LF Nodes → Image. The pack pins Pillow 12.2.0 and was rewritten/relocated in 2025 - use lucafoscili/lf-nodes, not an archived clone URL.

The failure modes are all the frame-count rules above, plus one easy miss: feeding it an already-combined animation where you wanted the last frame kept while leaving the default policy on. If your result feels one frame short, switch to include_final_endpoint or bump the source to include a duplicate opening frame. It's a small node with sharp edges, but for loop prep it's the right tool - and it never degrades your pixels doing it.

Category✨ LF Nodes/Image

Inputs (6)

NameTypeDefaultDescription
imageIMAGEOrdered IMAGE batch to sample without interpolation or pixel conversion.
target_countINT241–4096Exact number of output frames.
loop_endpoint_policyCOMBOexclude_final_endpointOmit the source's final endpoint from the sampled batch, or include it. For a closed loop, reuse the opening frame as the ending endpoint before choosing omit.
source_fpsFLOAT24.000.01–1000Source timing recorded in the receipt; sampling is governed by exact frame counts.
intended_fpsFLOAT12.000.01–1000Intended playback rate recorded in the receipt.
ui_widgetoptLF_MASONRY[object Object]

Outputs (3)

NameTypeDescription
imageIMAGEExactly target_count source frames selected by lossless tensor indexing.
receiptJSONSampling indices and source/intended timing receipt.
image_listIMAGEIndividual sampled frames in output order.