Nodes/ComfyUI-Practical-Tools/Batchloop Accumulate
ComfyUI Node

Batchloop Accumulate

Turn 20 loop iterations into one batch

By wenchengxiang·Created 2 months ago·Updated 9 days ago· 3
Batchloop Accumulate
  • accumulated
  • item
  • accumulated

What it is

A loop gives you one result per round. What you usually want at the end is all of them, in order, as a single thing - one image batch, one latent stack, one long audio clip - so it can go through one decode, one Save, one preview grid.

Batchloop Accumulate is the node that does that. Two wildcard inputs, one wildcard output, and a single promise: take this round's item, add it to what you've already got, hand the total back. It's designed specifically to sit in a loop body, with its output wired into the loop's End so the accumulated pile survives to the next round.

That's the whole reason it exists. The KB's map of ComfyUI's plumbing layer is all about wildcards, switches and context buses - the "carry one object down one wire" pattern - and this is that same idea applied to a loop's state. In core ComfyUI, there's no node that concatenates whatever you feed it round after round.

How it works

item is required, accumulated is optional, and the node dispatches on what it's holding:

  • accumulated not connected → returns item untouched. That's how you get a clean first round (and why you should leave the accumulator socket empty on round one instead of feeding it a dummy).
  • item is nothing → returns accumulated. Empty rounds don't corrupt the pile.
  • Two latents (dicts with samples) → concatenated along the batch dimension, mismatched spatial sizes upscaled to match the first, batch_index merged.
  • Two audio dicts (waveform + sample_rate) → concatenated along the time axis, channels padded to match, sample rates equalised by resampling. If the rates differ and torchaudio isn't installed, it raises a clear error saying so rather than producing a pitch-shifted mess.
  • Two tensors (images) → concatenated along the batch dimension, sizes aligned.
  • A list and anything → appends. Note the deliberate "appends, does not flatten": one item per round, exactly as handed in.
  • Anything else → wrapped as [accumulated, item], accumulated first, so a heterogeneous loop still gives you a predictable ordering.

In every branch the earlier rounds come first, which is what makes the result usable as a batch: iteration 0 is frame 0, iteration 19 is frame 19.

The wiring that matters

  • item - this round's result. The thing you just made: a decoded image, a latent, a waveform.
  • accumulated - feed this from your loop's carried value, i.e. the socket coming from While Loop Start valueN (or For Loop Start's valueN).
  • accumulated (output) - into the matching initial_valueN on While Loop End (or For Loop End). This is the wire that makes it accumulate; without it the node just returns the current round each time and you'd get only the last one.

The canonical build: While Loop Start → sampler/img2img → (optional save) → Batchloop Accumulate → While Loop End's initial_value, and after the loop, End's valueN → one VAE Decode → one batch of images. That last part matters - decode after the loop, once.

Install

ComfyUI Manager, search ComfyUI-Practical-Tools, or:

cd ComfyUI/custom_nodes
git clone https://github.com/wenchengxiang/ComfyUI-Practical-Tools

Restart. It needs torch and ComfyUI's own utilities, nothing else; no models. The pack's requirements.txt (onnxruntime, nvidia-vfx, openai) is for other nodes in the same pack, and the loader keeps going if those fail.

Gotchas

It accumulates in memory, all of it. Twenty 1-megapixel images in fp16 is around 80 MB of tensor - fine. Twenty 1080p frames is a lot, and a hundred rounds of latents decoded as one batch will try to allocate the whole batch at once on the VAE. If your loop finishes the work and then OOMs on decode, that's your batch, not your loop. Decode in chunks, or keep the accumulate step to latents and let a batch-aware decode node handle it.

Keep the type stable across rounds. The branch is chosen per call. Round one a tensor, round two a string, and you land in the catch-all branch with a [tensor, "some string"] list you'll have to unpick downstream. Mixing types is allowed; it's just never what you meant.

Round one is where the None confusion lives. Leave accumulated unconnected so the first round passes the item through unchanged. Wire something into it that's empty and you're relying on the node's null branch instead - same result, one more thing to debug.

It's a state holder, not a joiner. If you want to combine two image batches that already exist, use a batch node from anywhere; this one is for the loop case where the whole point is that round three only sees what rounds one and two left behind.

Order is arrival order, not sorted. It appends what it's given, when it's given it. If your loop's rounds don't run in the order you expect - nested loops, odd condition wiring - the output batch faithfully reproduces that confusion.

CategoryPractical-Tools/Logic

Inputs (2)

NameTypeDefaultDescription
accumulated*—
item*—

Outputs (1)

NameTypeDescription
accumulated*—