Cyclist :bicyclist:
ComfyUI already has an option to infinitely repeat a workflow. This extension adds an ability to reuse generated results to cycle over them again and again. With this tool, you can automate whatever iterative loop action you have in mind: building grids, animating frame-by-frame, changing conditions every step, etc.
General Usage
- Put a loading node where you want to provide a result from previous run.
- Optionally, connect a fallback input in case nothing is there to load yet (usually, at first iteration).
- Put a saving node in the end of your workflow.
- Put an Interrupt node if you want to stop generation when certain conditions are met.
- Done! Press "Queue Prompt". And again. And again, maybe.
- Optionally, check "Extra options" and "Auto Queue" checkboxes to let ComfyUI infinitely repeat a workflow by itself.[^1]
- If you want to start a loop from scratch, press the "New Cycle" button introduced in this workflow. It will increment all filenames and loop IDs, if it can.[^2]. Alternatively, use Loop Manager to do this automatically.
[!WARNING] Check Known Issues at the bottom of this page to learn about non-obvious behaviour.
Installation
Option 1: Use ComfyUI Manager.
Option 2:
- Install git
- Navigate to your
/ComfyUI/custom_nodes/
folder - Run
git clone https://github.com/Pos13/comfyui-cyclist/
console command in it
Nodes and example workflows
<ins>Loop Manager</ins>: Simply provides a string. This string — loop_id — can be used as a name of a variable to put into memory, or as a filename. If the "increment" value is set on "by_interrupt_node", loop_id will automatically change when Interrupt node procs, to prevent overriding end result. If the "increment" value is set on "on_any_interrupt", loop_id will change in the same situation, plus when you manually cancel queue. Useful to skip failures.
<ins>Memorize Int/Float/String</ins>: These simple nodes save something in memory. Information is lost if you restart ComfyUI. Variables are exclusive to provided loop ID. You can store exactly one Int, one Float, one String and one Conditioning for every loop ID. Change ID to save a new variable of this type.
<ins>Recall Int/Float/String</ins>: These nodes are for loading information from memory. If nothing is there, fallback input is used instead. Fallback is optional. Example:
<details> <summary>${\color{blue}Workflow\ to\ generate\ an\ image\ until\ it\ gets\ a\ high\ score}$</summary>Every time an image is generated, it gets a score made by Image Reward Score node. Score is saved by Memorize Float node. Next gen, it's loaded and compared to user-provided target score. If greater, process is stopped.
</details><br/><ins>Convert To</ins>: Takes any input and tries to output an int, float, boolean and string representation of it. Boolean and string can always be cast into, but incorrect int or float will raise an error.
<ins>Compare Anything</ins>: Takes two inputs of any kinds and a compare operation. Outputs True or False boolean value.
- Integers are compared as usual.
- Floats are compared with 1<sup>-09</sup> is precision.
- Strings are compared alphabetically.
- Images and latents are compared by total pixel amount across whole batch.
- Everything else is casted to string before comparison.
- If types are different, it tries to cast inputs into the same type in this order: to boolean, to float, to string.
<ins>Int/Float Math</ins>: Just a handfull of arithmetic operations betwen two numbers.
<details> <summary>${\color{blue}Workflow\ to\ generate\ an\ image\ until\ it\ gets\ a\ high\ score,\ but\ always\ save\ the\ best\ attempt}$</summary>This is slightly modified version of the workflow above. Not a score is saved, but an image. Every new run it is compared with a new image to let WAS Node Suite to choose the best.
- Convert To node is used to cast boolean -> float -> number, as WAS only work with number representation of boolean.
- Compare Anything node compares float scores.
- Float Math node is used to provide float constant, as Primitive node can't be connected to unspecified input. "Adding zero" just outputs upper value.
<ins>Recall/Memorize Conditioning</ins>: Works the same way other Recall/Memorize nodes work. It stores conditioning to memory, not to disc.
<details> <summary>${\color{blue}Workflow\ to\ generate\ ugly\ animal\ crossbreeds}$</summary>Impact Pack is used to generate random animal words. These words are used to add a new conditioning for an image.
Don't use Auto Queue here! You'd probably want to click "Queue Prompt" manually, and press "New Cycle" whenever result is already good enough.
</details><br/><ins>Interrupt</ins>: Put this onto any link, output is unchanged "any_in" input.[^3] When this node is activated by workflow, it stops it if "stop" input is true. You can convert "stop" from widget to input with right-clicking the node.
Be aware where you put Interrupt node! You want it to prevent some heavy computing, so place it in the way of any thing required for such computing. Also, you can only place it after the nodes used to provide "any_in" or "stop" inputs, because ComfyUI will not execute a workflow with a loop.
The best place for Interrupt node is right after important "Reload/Recall" node.
Multiple Interrupt nodes are allowed, and sometimes wanted.[^4]
Triggered Interrupt will show this message:
<ins>Save Image (Override)</ins>: This node works similarly to default Save Image node, but filename remains the same, without counter. It saves image to your output folder![^5]
<ins>Reload Image</ins>: Loads image by filename, from "\ComfyUI\output" folder. If file does not exists, fallback input is used instead. Fallback is optional. Image is loaded in RGBA, with transparency channel.[^6]
<details> <summary>${\color{blue}Workflow\ to\ generate\ an\ image\ until\ right\ things\ are\ recognised}$</summary>Before generating a new image, "BLIP Interrogate" node from WAS Node Suite tries to analyze previous result. If answers are right, generation stops.
Workflow page at civitai: https://civitai.com/models/342128
</details><br/><ins>Save Model (Override)</ins>: This node works similarly to default Save Model node, but filename remains the same, without counter. It saves model to your default models/checkpoints
folder!
This workflow is for testing model's LoRA compatibility. Apply more and more random LoRAS. Applied LoRA's names are saved in images' filenames. WAS Node Suite is used.
Don't use Auto Queue here! You'd probably want to click "Queue Prompt" manually, and press "New Cycle" whenever result is already ~~good~~ bad enough.
</details><br/><ins>Save Latent (Override)</ins>: This node works similarly to default Save Latent node, but filename remains the same, without counter. It saves latent file to your output/latent folder!
<details> <summary>${\color{blue}Workflow\ to\ gradually\ upscale\ image\ until\ megapixel\ count\ is\ met}$</summary>This workflow uses latent upscale by x1.375 times over and over, until image becomes big enough. You'd want to set megapixel count according to your VRAM and patience amount. WAS Node Suite is used to calculate latent size.
Notice disabled nodes! Enable them only after the whole cycle is done to save time and not calculate intermediate results. Enabling them will not disrupt normal cycle flow in any way. No early interrupts, no extra iterations.
</details><br/><ins>Generation Timer</ins>: This node measures time spent on generation. Outputs floats.
- Timer starts right before every generation, when workflow is checked.
- Timer stops when the last "Save/Memorize" node in the workflow procs.
Mutiple Generation Timers can be used, but you better assign them to different loops.[^7][^8]
<ins>Force Timer Stop</ins>: This node tells the timer to stop whenever any input is provided, no matter what. You can use it to measure time spent by certain blocks, not the whole workflow. But the start is always at generation start.
Force Timer Stop node is not necessary to use Generation Timer, as long as you have any "Save/Memorize" node.
<details> <summary>${\color{blue}Workflow\ that\ works\ for\ certain\ amount\ of\ time}$</summary>This is almost a default ComfyUI workflow! Just set amount of time you want your PC to work generating images, check "Extra options" and "Auto Queue" checkboxes, and press "Queue Prompt" button.
Workflow page at civitai: https://civitai.com/models/342065
</details><br/>Other workflows
<details> <summary>${\color{blue}Workflow\ to\ evenly\ upscale\ to\ exact\ resolution}$</summary>Set a width and height, and image will upscale to it. But not in one go: it calculates how many iterations should be made to not add too many pixels to width or height, and performs exactly that many iterations. You can gradually change denoise, CFG scale and steps count from first to last iterations. Uses pythongosssss' Custom Scripts for math and display.
It's a little wild :sweat_smile:.
</details><br/> <details> <summary>${\color{blue}Workflow\ to\ stack\ cats}$</summary>SDXL Turbo is used to make the amount of very noisy cats fast. They pile on noisy background one-by-one, top to bottom. After the cycle is done, unmute the top group of node to generate final result.
Use Everywhere nodes hide links. Version without is exists, but it's messy.
</details><br/>[!NOTE] All example workflows (and more) can be found in the workflows folder. But for possible updates, comments and questions about workflows you better navigate to my Civitai page.
Known Issues
[^1]: For now, "Auto Queue" checkbox unchecks automatically on interrupt. This prevents accidental short-circuits. Yes, you have to check it back manually every time you start a new cycle. [^2]: Pressing "New Cycle" button will change a string in every "filename" and "loop_id" widget. It also will detect Primitives connected to them, and update them too. But nothing else. It will not update the string provided by any type of "String Const" or "String Op" or "Recall String" node. [^3]: Reroutes and Primitives don't work well with unspecified inputs. It is possible to juggle them to set different input and output types on Interrupt node. It's on you to not to. [^4]: Bypassing Interrupt node does not work. Just disconnect "stop" input instead. [^5]: Image batches are not supported yet. It's planned. [^6]: Some nodes don't support RGBA (Upscale Image (Using Model), for example). You can use Images to RGB node from WAS Node Suite to fix that. [^7]: Generation Timer does not output the same time intervals as ComfyUI does. It doesn't account for anything happening before Generation Timer node is checked and after the last "Save/Memorize" (or assigned Force Timer Stop node) is executed. [^8]: Generation Timer only works if "loop_id" is in widget form, not input. I can't get around this limitition.