Looped-DiT Generate
Your loops slider is not your step count
- model
- image
- report
Looped-DiT Generate is a whole text-to-image pipeline collapsed into one node. Prompt in, image out, with no CLIP loader, no VAE loader, no KSampler and no sampler/scheduler pair to configure. If that looks unusual, it is - you're looking at a pixel-space diffusion transformer, and the missing loaders are correct rather than broken. That absence trips people up constantly; it's the same confusion that made HiDream-O1 look "broken" to half the people who tried it.
This is an unofficial ComfyUI port of SenseNova's Looped-DiT, a small research model with one architectural gimmick: a shared group of transformer blocks runs several times inside every denoising step. The pack's pitch is fidelity - the author verified bit-identical output against the official Looped-DiT code across 27 images, down to the 8-bit pixels and the float32 tensor. That's an unusual amount of proving, and it tells you who this is for: someone who wants the model's actual output, not an approximation.
How it actually works
Pixel space means what it says. The model operates on a 3×512×512 image directly, no compression, no VAE round trip. Text goes through FLAN-T5-Large once per run, and the sampler is the official Euler loop - 100 steps by default, with a conditional and unconditional pass per step at the default CFG. So one image is 200 full model passes, not 100.
Loop depth is the thing to understand, because the name lies to you. The tooltip says it plainly: loops is how many times the shared middle block group runs inside one denoising step. For the B/16 checkpoint that's 6 + 5 × loops + 6 blocks per pass - 17 blocks at loops 1, 22 at loops 2, 32 at loops 4. The models were trained at loops 4, which is why 4 is the default. Deeper means more compute per step, not more steps.
The image output is also the official x * 127.5 + 128 conversion, clamp and truncate, so Save Image writes exactly the reference pixels.
The inputs that matter
- model - the
LOOPED_DIThandle from Looped-DiT Loader. Wire it in; there's no other way to get one. - loops - 1 to 16, default 4. Leave it at 4 unless you're deliberately testing depth. Anything above 4 is off-distribution.
- seed - a 64-bit int with the usual post-generate control attached, so the fixed/randomize dropdown applies here exactly like a KSampler.
- prompt, steps, cfg - multiline text (default
a red cube on top of a blue sphere), Euler steps 1–1000 (official 100), CFG 1–30 at 0.1 steps (official 6.0). The node will happily leave the official values behind; the images get worse when you do.
Two outputs: image (straight into Save Image or Preview Image) and report, a JSON string you read through a Show Text node. Keep the report - the token, noise and float hashes in it are how you tell whether two runs actually differed, and the timings tell you where the seconds went.
Install
ComfyUI Manager, search the pack title "Looped-DiT for ComfyUI", or by hand:
cd ComfyUI/custom_nodes
git clone https://github.com/hiroki-abe-58/ComfyUI-LoopedDiT
Then fetch the models with your ComfyUI's own Python - the pack ships no dependencies of its own (pyproject.toml lists an empty dependency set, and there's no requirements.txt), but it does not download weights at run time:
python custom_nodes/ComfyUI-LoopedDiT/tools/download_models.py --models-dir models
# add --b32 for the B/32 checkpoint too
You get models/looped_dit/looped-dit-b16.pt (1.03 GB, MIT) and models/looped_dit/flan-t5-large/ with config.json, tokenizer.json and model.safetensors (3.1 GB, Apache-2.0). Restart ComfyUI and open workflows/gui/looped_dit_generate.json - the repo ships a working graph, which is faster than wiring it yourself.
Where people get burned
Reading loops as steps. "I set loops to 20 for more detail" is the mistake this node invites. At loops 4 on an RTX 5090 a 512×512 image takes about 15 seconds of denoising; a single loops-4 image is roughly 21 seconds including text encoding and saving. Deeper loops cost you time for a result the model wasn't trained to produce.
Looking for a negative prompt. There isn't one. CFG's unconditional pass feeds the same text through with an all-zero mask, per the official code. Any negative text you have nowhere to put.
Resolution and batching. Batch size 1 and 512×512 are hard constraints of the released checkpoints, and prompts truncate at 256 tokens. A 1024×1024 latent or a batch of four is not a setting you're missing.
It's CUDA only. The loader raises immediately on a machine with no CUDA GPU; CPU and Apple Silicon aren't supported in this version. And since ComfyUI's memory manager doesn't know this model exists, the usual free-VRAM behaviour doesn't apply - add Looped-DiT Unload when you're done.
Chasing bit-identical output on your own card. Verification happened on one GPU, one driver, one torch build, default flags. --fast or --deterministic changes kernels, and different kernels change pixels.
And the author is upfront about the thing you'd assume otherwise: higher loop depth is not automatically better. In his own demo set, loops 2 draws two lanterns when the prompt asks for one. Depth is a variable to test, not a quality dial.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| model | LOOPED_DIT | — | |
| prompt | STRING | a red cube on top of a blue sphere | — |
| seed | INT | 00–18446744073709550000 | — |
| loops | INT | 41–16 | Loop depth = how many times the shared middle blocks run inside ONE denoising step (6 + 5 x loops + 6 blocks per model pass for B/16). It is not the number of sampling steps. The models were trained at 4. |
| steps | INT | 1001–1000 | Euler steps (official: 100) |
| cfg | FLOAT | 6.01–30 | Classifier-free guidance (official: 6.0). The unconditional pass uses the same text with a zero mask. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| report | STRING | — |