AP Loop Open (Latent)
A feedback loop that never leaves latent space — history for iterative latent workflows
- source_latents
- source_masks
- custom_latents
- additional_data
- loop_token
- original_current_latent
- original_current_mask
- original_first_latent
- original_previous_latent
- original_previous_mask
- processed_previous_latent_1
- processed_previous_latent_2
- processed_previous_latent_3
- processed_previous_latent_4
- processed_previous_latent_5
- processed_previous_mask_1
- processed_previous_mask_2
- processed_previous_mask_3
- processed_previous_mask_4
- processed_previous_mask_5
- custom_current_latent
- additional_data_current
- iteration_index
The image loop nodes give you a per-frame feedback loop in pixel space. This is the same machinery running in latent space, and the reason that matters is the same one that motivates APApplyRAFTOpticalFlowLatent: if your loop body works on latents, going back to pixels every iteration means a VAE decode and encode per frame. The latent loop keeps the whole recursion in latent space, which is faster, uses less VRAM, and avoids the drift that repeated VAE round-trips introduce.
Same shape as APImageLoopOpen - it takes a batch of source_latents, and on each iteration hands you the current latent, the first latent, the previous source latent, and up to five processed_previous_latent_1..5 entries (plus matching masks) that came back from your loop body. The loop_token output carries the iteration state, exactly like the image version.
The mechanism
Identical hidden-iteration recursion, latent-flavored. APLatentLoopOpen reads the internal iteration index and accumulated processed-latent history, slices the source batch at the current index, and exposes it alongside the processed history. The token it emits carries iteration index, total count, and history_count; its counterpart APLatentLoopClose consumes it. The two nodes are a matched pair - you don't wire iteration counters or history depth manually between them, which is the pack's "clean API" selling point.
The latent dict's metadata (shape, noise masks, anything the sampler needs) is carried through, so the latents you get back are valid inputs to a KSampler. If you're combining this with latent-space flow warping (APApplyRAFTOpticalFlowLatent or the masked variant), the whole loop body - warp, blend, sample - can stay in latent space until the very end.
Inputs to actually set
source_latents- the batch to iterate (e.g. the VAE-encoded frames of your sequence, or an empty-latent sequence).history_count- processed-history depth exposed (0–5, default 3). This is the "up to 5 previous latents" the author advertises - the temporal context for your loop body.source_masks(optional) - per-frame masks in lockstep.custom_latents+custom_frame_index_map+apply_custom_replacement- replace specific iterations' latent with one from a custom source, same feature as the image loop.additional_data(optional,*) - a parallel payload iterated at the same index (e.g. an IMAGE payload alongside a LATENT loop).
return_first_when_no_previous_available=true makes missing history slots fall back to the first latent rather than zeros, which keeps early iterations clean.
Outputs
loop_token, original_current_latent, original_current_mask, original_first_latent, original_previous_latent, original_previous_mask, processed_previous_latent_1..5, processed_previous_mask_1..5, custom_current_latent, additional_data_current, and iteration_index.
Installing it
Pack-wide install: ComfyUI Manager (search "AP_OpticalFlow"), or
cd ComfyUI/custom_nodes
git clone https://github.com/adampolczynski/ComfyUI_AP_OpticalFlow
python -m pip install -r custom_nodes/ComfyUI_AP_OpticalFlow/requirements.txt
Restart. Dependency: torchvision>=0.15.
When to use it over the image loop
If your per-frame processing is fundamentally pixel work - compositing against the original, pixel-space inpaint, saving intermediate frames - the image loop is the natural fit and this adds a pointless encode. If your per-frame work is warp-in-latent + sample, this node removes the two VAE hops per iteration that the image loop would force you to pay. The README frames it as the way to "avoid repeated VAE encode/decode in iterative latent workflows." It pairs cleanly with the latent apply nodes and APTemporalBlendLatents - the whole temporal-consistency toolbox exists in both spaces, and this is the loop for the latent side.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| source_latents | LATENT | — | |
| history_count | INT | 30–5 | — |
| return_first_when_no_previous_available | BOOLEAN | false | — |
| apply_custom_replacement | BOOLEAN | false | — |
| custom_frame_index_map | STRING | — | |
| source_masksopt | MASK | — | |
| custom_latentsopt | LATENT | — | |
| additional_dataopt | * | — |
Outputs (19)
| Name | Type | Description |
|---|---|---|
| loop_token | FLOW_CONTROL | — |
| original_current_latent | LATENT | — |
| original_current_mask | MASK | — |
| original_first_latent | LATENT | — |
| original_previous_latent | LATENT | — |
| original_previous_mask | MASK | — |
| processed_previous_latent_1 | LATENT | — |
| processed_previous_latent_2 | LATENT | — |
| processed_previous_latent_3 | LATENT | — |
| processed_previous_latent_4 | LATENT | — |
| processed_previous_latent_5 | LATENT | — |
| processed_previous_mask_1 | MASK | — |
| processed_previous_mask_2 | MASK | — |
| processed_previous_mask_3 | MASK | — |
| processed_previous_mask_4 | MASK | — |
| processed_previous_mask_5 | MASK | — |
| custom_current_latent | LATENT | — |
| additional_data_current | * | — |
| iteration_index | INT | — |