AP Loop Close
The return wire — feed your processed frame back into the loop and get the whole sequence out
- loop_token
- processed_image
- processed_mask
- additional_data
- processed_images
- processed_masks
- processed_additional_data
AP Loop Open opens a per-frame recursion; AP Loop Close is the closing bracket. You put your actual processing - warp, inpaint, blend, sample, whatever makes the frame better - between the two, and Loop Close takes the processed result, stashes it as this iteration's output, and feeds it back into the loop so the next iteration of Loop Open sees it in processed_previous_image_1. When the last frame is reached, it returns the entire accumulated sequence.
The key idea, and the reason this node exists: without a close node, there's no way for a ComfyUI graph to carry processed state forward, because each node is a pure function with no memory. Loop Close is the memory. And the author's design goal - a "clean API, no unnecessary inputs" - is visible in how little you have to wire: in the simplified API it takes just loop_token and your processed_image, plus optional processed_mask and additional_data.
The mechanism
It reads the loop state from the loop_token (iteration index, total count, history depth - no need to wire those separately, which is a real ergonomic win over older loop nodes). It appends your processed frame to an accumulator, prepends it to the running history buffer, and then:
- If this is the last frame, it returns the accumulated
processed_imagesbatch (all frames, in order). - Otherwise it re-triggers the loop with an incremented iteration index, carrying the accumulator and history forward as hidden state.
So the output you get at the end is the full processed sequence - the thing you'd feed a video encoder or save node.
Inputs and outputs
loop_token(required,FLOW_CONTROL) - fromAPLoopOpen. This is what links the pair.processed_image(required) - the result of your per-frame processing. This is what accumulates into the output sequence.processed_mask(optional) - the per-frame processed mask, if your loop body produces one.additional_data(optional,*) - a per-iteration payload you want collected across the loop.
Outputs: processed_images (the accumulated sequence - connect to a save/video node), processed_masks (accumulated masks), and processed_additional_data (the aggregated payload from all iterations).
Installing it
Same pack-wide install as always: 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 is torchvision>=0.15.
The classic shape
Frame-by-frame temporal inpainting is the canonical use: Loop Open gives you current frame + processed history, you flow-warp and inpaint the moving region, Loop Close collects the result. On the next iteration, Loop Open's processed_previous_image_1 is the frame you just fixed - so your blend and warp decisions get to see actual processed history, not just source frames. That feedback loop is what kills flicker across long sequences, and it's the pattern the README's temporal-blend setup is built around (Loop Open → blend history → APTemporalBlendImages → Loop Close).
The one thing to get right
The loop_token connection is load-bearing. If you disconnect it or feed Loop Close a token from a different Open node, the pair desyncs and the error messages tell you exactly that (Loop Open will complain that expected processed history is missing). Keep the token wired, keep processed_image connected to actual processed output rather than the raw input frame, and the recursion does the rest.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| loop_token | FLOW_CONTROL | — | |
| processed_image | IMAGE | — | |
| processed_maskopt | MASK | — | |
| additional_dataopt | * | — |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| processed_images | IMAGE | — |
| processed_masks | MASK | — |
| processed_additional_data | * | — |