Extensions/ComfyUI-outputlists-combiner
ComfyUI Extension

ComfyUI-outputlists-combiner

ComfyUI custom nodes to create output lists with strings and numbers, make combinations of them, and help with XYZ-Gridplots

By geroldmeisinger·Created 10 months ago·Updated a day ago· 194
geroldmeisinger/ComfyUI-outputlists-combiner
Nodes11
On cloudLocal install
CategoryUtility, _for_testing
Stars194
Updateda day ago
Readme
<!--- Auto-generated from readme/! DON'T EDIT THIS FILE! --> <div align="center"> <img src="/media/promo.png" alt="OutputLists Combiner Promo" width="600" /> </div> <h2 align="center">Supercharge multi-asset generation!</h2> <h3 align="center"> <a href="#installation" target="_blank">Installation </a> · <a href="#changelog" target="_blank">Changelog </a> · <a href="#nodes" target="_blank">Nodes </a> · <a href="#deprecated-nodes" target="_blank">Deprecated nodes </a> </h3> <div align="center"> <a href="https://www.buymeacoffee.com/GeroldMeisinger" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" ></a> </div>

Overview

https://github.com/user-attachments/assets/766e5802-f382-48d1-b113-9a1ebd7398fd

https://github.com/user-attachments/assets/f6705477-ad88-4f23-9178-0ea24362948f

The original repo is located at https://github.com/geroldmeisinger/ComfyUI-outputlists-combiner

If you find this custom node useful:

  • Star the repo to make others discover the project and motivate the developer!
  • 💰 Donate for further development and greatly appreciate my efforts!

Table of Content

Installation

ComfyUI-Manager (recommended)

Search for OutputLists Combiner

Search OutputLists Combiner in ComfyUI-Manager

Comfy-CLI

comfy-cli node install ComfyUI-outputlists_combiner

Manual

cd custom_nodes # in ComfyUI/
git clone https://github.com/geroldmeisinger/ComfyUI-outputlists-combiner
cd ComfyUI-outputlists-combiner
uv pip install -r requirements.txt

Troubleshooting

ImportError: libEGL.so.1: cannot open shared object file: No such file or directory

Newer Skia versions requires libEGL.so to be present on Linux hosts, see official Skia-Python. apt-get install libfontconfig1 libgl1-mesa-glx libgl1-mesa-egl libegl1 libglvnd0 libgl1-mesa-dri

Changelog

  • 0.0.20 release for OpenManager
  • 0.0.19 fixed file glob limit in Load Any File, cleanup node promotion
  • 0.0.15 added Bake String node
  • 0.0.14 restructed Spreadsheet OutputList, deprecated Formatted String in favor of Comfy Core Format Text
  • 0.0.13 fixed nested Iterate loop nodes
  • 0.0.12 added Iterate loop nodes, added separator field in SpreadsheetOutputList
  • 0.0.11 fixed understaffed XYZGridPlot, fixed node documentation language codes
  • 0.0.10 fixed font_size in XYZGridPlot, fixed Load Any File, translated node documentation
  • 0.0.8 more flexible XYZGridPlot, better label rendering in XYZGridPlot, WorkflowDiscriminator node, node documentation
  • 0.0.6 SpreadsheetOutputList, XYZGridPlot
  • 0.0.4 restructured outputs, JsonOutputList,
  • 0.0.3 ConvertAnyToIntFloatString, KSamplerImmediateSave
  • 0.0.2 restructured outputs
  • 0.0.1 StringOutputList, NumberOutputList, CombineOutputLists, FormattedString
<!--- <details> <summary><b>0.0.0</b></summary> - feature 1 - feature 2 - feature 3 </details> -->

Background

Did you know that ComfyUI supports so called output lists which tell nodes downstream to execute multiple times within the same run? Notice how this output list emits four strings and causes the KSampler to run four times:

https://github.com/user-attachments/assets/303115d3-7c28-42e8-bb52-d02e7cc1022b

Wait, what?

Yeah, I didn't know about it either. Apparently everytime you see the symbol 𝌠 it's an output list. This feature is very underutilized but it allows you to be process sequentially without weird workarounds (like for-loops, increment counters or external python scripts) and makes it perfect for prompt combinations and XYZ-gridplots. I always found grids a hazzle in ComfyUI whereas they were straightforward in Automatic1111. Most custom nodes either require a lot of manual work or you have to use some extra-special nodes (like custom KSamplers). This project tries to make good use of output lists, integrate well with the ComfyUI's paradigm and finally make XYZ-gridplots easy to use again.

Make sure you understand what's happening in this example as it's crucial to work with the following nodes!

  • batch: All images are guaranteed to be of the same size. Used to generate multiple image at once in the KSampler more effectively. Also useful for videos because of the same-size guarantee.
  • data list: A special type in ComfyUI which causes downstream nodes to process multiple times.
  • LIST: A custom type in most third-party nodes which are passed around as one item which contains a python list.

Nodes

String OutputList

String OutputList

(ComfyUI workflow included)

Creates an OutputList by splitting the string in the textfield with a separator. value and index use(s) is_output_list=True (indicated by the symbol 𝌠) and will be processed sequentially by corresponding nodes.

Inputs

| Name | Type | Description | | --- | --- | --- | | separator | STRING | The string used to split the textfield values by. Supports character escaping like \n (newline), \n\n (two new lines), ¸\t (tabcharacter) etc. | | values | STRING | The text you want to split into a list. Note that the string is trimmed of trailing newlines before splitting, and each item is again trimmed of whitespace. |

Outputs

| Name | Type | Description | | --- | --- | --- | | value | * 𝌠 | The values from the list. | | index | INT 𝌠 | Range of 0..count. You can use this as an index. | | count | INT | The number of items in the list. | | inspect_combo | COMBO | A dummy-output you can use to link to a COMBO and pre-fill with it's values. The connection will then be automatically re-linked to value output. |

Number OutputList

Number OutputList

(ComfyUI workflow included)

Creates an OutputList with a range of numeric values. Uses numpy.linspace internally, because it works more reliably with floating-point values. If you want to define number lists with arbitrary steps instead check out the JSON OutputList and define an array, e.g. [1, 42, 123]. int, float, string and index use(s) is_output_list=True (indicated by the symbol 𝌠) and will be processed sequentially by corresponding nodes.

Inputs

| Name | Type | Description | | --- | --- | --- | | start | FLOAT | Start value to generate the range from. | | stop | FLOAT | End value. If endpoint=include then this number is included in the list. | | num | INT | The number of items in the list (don't confuse it with a step). | | endpoint | BOOLEAN | Decides if the stop value should be included or excluded in the items. |

Outputs

| Name | Type | Description | | --- | --- | --- | | int | INT 𝌠 | The value converted to int (rounded down/floored). | | float | FLOAT 𝌠 | The value as a float. | | string | STRING 𝌠 | The value as a float converted to string. | | index | INT 𝌠 | Range of 0..count which can be used as an index. | | count | INT | Same as num. |

JSON OutputList

JSON OutputList

(ComfyUI workflow included)

Creates an OutputList by extracting arrays or dictionaries from JSON objects. Uses JSONPath syntax to extract the values, see JSONPath on Wikipedia . All matched values are flatten into one long list. You can also use this node to create objects from literal strings like [1, 2, 3]. key, value, int and float use(s) is_output_list=True (indicated by the symbol 𝌠) and will be processed sequentially by corresponding nodes.

Inputs

| Name | Type | Description | | --- | --- | --- | | jsonpath | STRING | JSONPath used to extract the values. | | json | STRING | A JSON string which is translated to an object. | | obj | * | (optional) object of any type which will replace the JSON string |

Outputs

| Name | Type | Description | | --- | --- | --- | | key | STRING 𝌠 | The key for dictionaries or index for arrays (as string). Technically it's a global index of the flattened list for all non-keys. | | value | STRING 𝌠 | The value as a string. | | int | INT 𝌠 | The value as a int (if it cannot parse the number, defaults to 0). | | float | FLOAT 𝌠 | The value as a float (if it cannot parse the number, defaults to 0). | | count | INT | Total number of items in the flattened list | | debug | STRING | Debug output of all matched objects as a formatted JSON string |

Spreadsheet OutputList

Spreadsheet OutputList

(ComfyUI workflow included)

Creates multiple OutputLists from a spreadsheet (.csv .tsv .md .ods .xlsx .xls). You can use the Load any File node to load a file in base64-encoding. Internally uses pandas read_excel and read_csv to load spreadsheet files. All lists use(s) is_output_list=True (indicated by the symbol 𝌠) and will be processed sequentially by corresponding nodes.

Comments that start with # character in textfiles are ignored.

Inputs

| Name | Type | Description | | --- | --- | --- | | selectors | STRING | A list of selectors separated by separator or empty list. The selectors can be names in the headers or column names (A, B, C...ZZZZ) or row indices (1...65536). Note that in spreadsheets rows start at 1, columns start at A, whereas OutputLists are 0-based (in select-nth). | | separator | STRING | Separator character used for selectors and data in text files (.csv .tsv .md). Supports escaping, e.g. becomes tab character, \ becomes backslash. | | direction | BOOLEAN | Direction of iteration is either row-based (top-down) or column-based (left-to-right) | | num_headers | INT | Treat the first x rows (or columns) in the spreadsheet as headers and skip them in the list. Uses the header as reference for row (or column) names. If direction=top-down searches the headers in bottom header row first (left-to-right, then iterating up). If direction=left-to-right searches the headers from rightmost header column first (top-down, then iterating left). | | select_nth | INT | Only select the nth entry (0-based) or ignore if -1. Useful in combination with the PrimitiveInt+control_after_generate=increment pattern. | | string_or_base64 | STRING | CSV/TSV string or spreadsheet file in base64 (for .ods .xlsx .xls). Use Load Any File node to load a file as base64. |

Outputs

| Name | Type | Description | | --- | --- | --- | | count | INT | Number of items in the longest list row (or column). | | values_dict | DICT 𝌠 | A dictionary using the selectors as keys and the values of the current row (or column). Useful in combination with Format Text node. Always includes both the selector and column name (or row index) as alias, if there is a header. | | values_list | ARRAY 𝌠 | A list of values of the current row (or column) based on the selectors. Useful in combination with Format Text node. | | item_a | STRING 𝌠 | | | item_b | STRING 𝌠 | | | item_c | STRING 𝌠 | | | item_d | STRING 𝌠 | |

OutputLists Combinations

OutputLists Combinations

(ComfyUI workflow included)

Takes up to 4 OutputLists and generates every combination of them.

Example: [1, 2, 3] x ["A", "B"] = [(1, "A"), (1, "B"), (2, "A"), (2, "B"), (3, "A"), (3, "B")]

unzip_a .. unzip_d use(s) is_output_list=True (indicated by the symbol 𝌠) and will be processed sequentially by corresponding nodes.

All lists are optional and empty lists will be ignored.

Technically it computes the Cartesian product and outputs each combination splitted up into their elements (unzip), whereas empty lists will be replaced with units of None and they will emit None on the respective output.

Example: [1, 2] x [] x ["A", "B"] x [] = [(1, None, "A", None), (1, None, "B", None), (2, None, "A", None), (2, None, "B", None)]

Alternative usage: If you connect one list to a unit value it essentially works as a on-signal node (a.k.a execution order enforcer).

Inputs

| Name | Type | Description | | --- | --- | --- | | list_a | * | (optional) | | list_b | * | (optional) | | list_c | * | (optional) | | list_d | * | (optional) |

Outputs

| Name | Type | Description | | --- | --- | --- | | unzip_a | * 𝌠 | Value of the combinations corresponding to list_a. | | unzip_b | * 𝌠 | Value of the combinations corresponding to list_b. | | unzip_c | * 𝌠 | Value of the combinations corresponding to list_c. | | unzip_d | * 𝌠 | Value of the combinations corresponding to list_d. | | index | INT 𝌠 | Range of 0..count which can be used as an index. | | count | INT | Total number of combinations. |

Convert To Int Float Str

Convert To Int Float Str

(ComfyUI workflow included)

Converts anything number-like to INT FLOAT STRING. Uses nums_from_string.get_nums internally which is very permissive in the numbers it accepts. Anything from actual ints, actual floats, ints or floats as strings, strings that contains multiple numbers with thousand-separators. Use a string 123;234;345 to quickly generate a list of numbers. Don't use commas as separators as they may be interpreted as thousand-separators. int, float and string use(s) is_output_list=True (indicated by the symbol 𝌠) and will be processed sequentially by corresponding nodes.

Inputs

| Name | Type | Description | | --- | --- | --- | | any | * | Anything that can be meaningfully converted to a string with parseable numbers inside |

Outputs

| Name | Type | Description | | --- | --- | --- | | int | INT 𝌠 | All the numbers found in the string with the decimals truncated. | | float | FLOAT 𝌠 | All the numbers found in the string as floats. | | string | STRING 𝌠 | All the numbers found in the string as floats converted to string. | | count | INT | Amount of numbers found in the value. |

XYZ-GridPlot

XYZ-GridPlot

(ComfyUI workflow included)

Generates a XYZ-Gridplot from a list of images. It takes a list of images (including batches) and flattens them into a long list first (thus batch_size=1).

Grid shape

Determines the shape of the grid by:

  1. the number of row labels
  2. the number of column labels
  3. the remaining sub-images. You can use order=inside_out to reverse the image selection (useful if batch_size>1 and you want to label the batches).

Alignment

  • If a label gets wrapped into the next line the whole axis is considered "multiline" and aligns them at top with justified-spacing.
  • If all the labels are numbers or all end in numbers (e.g. strength: 1.) the whole axis is considered "numeric" and aligns them right.
  • All other texts are considered "singleline" and aligns them centered.
  • Aligns singleline and numeric labels for columns at bottom, and for rows aligns them vertically in the middle.

Font-size

  • The height of the column label area is determined by font_size or half of largest sub-images packing height in any row (whichever is greater).
  • The width of the row label area is determined by the widest width of the sub-images packing (with a minimum of 256px).
  • The text is shrunk down until it fits (down to font_size_min=6) and uses the same font size for the whole axis (row labels or column labels). If the font size is already at the minimum, clips any remaining text.

Sub-images packing

Shapes the sub-images (usually from batches) into the most square area (the "sub-images packing"), unless output_is_list=True, in which case uses only one image for each cell and create a list of whole image grids instead. You can use this list of image grids to connect another XyzGridPlot node to create super-grids. If the sub-images consist of batches of different sizes, fills up the missing cells with empty images. The number of images per cells (including batched images) have to be a multiple of rows * columns.

Inputs

| Name | Type | Description | | --- | --- | --- | | images | IMAGE | A list of images (including batches) | | row_labels | * | Row label texts at the left side | | col_labels | * | Column label texts at the top | | gap | INT | Gap between the sub-image packings. Note that within the sub-images themselves uses no gap. If you want a gap between the sub-images connect another XyzGridPlot node. | | font_size | FLOAT | Target font size. The text will be shrunk down until it fits (down to font_size_min=6). | | row_label_orientation | COMBO | Text orientation of the row labels. Useful if you want to save space. | | order | BOOLEAN | Defines in which order the images should be processed. This is only relevant if you have sub-images. Useful if batch_size>1 and you want to plot the batches. | | output_is_list | BOOLEAN | This is only relevant if you have sub-images or you want to create super-grids. |

Outputs

| Name | Type | Description | | --- | --- | --- | | image | IMAGE 𝌠 | The XYZ-GridPlot image. If output_is_list=True creates a list of images which you can connect to another XYZ-GridPlot node to create super-grids. |

Load Any File

Load Any File

(ComfyUI workflow included)

Loads any text or binary file and provides the file content as string or base64 string. Additionally tries to load it as a IMAGE. And also tries to load any metadata.

filepath supports ComfyUI's annotated filepaths [input] [output] or [temp]. filepath also support glob-pattern expansions subdir/**/*.png. Internally uses python's glob.iglob.

metadata calls exiftool, if it's installed and available at PATH, otherwise uses PIL.Image.info as a fallback.

If you need more control over the paths use it together with Path OutputList.

For security reason only the following directories are supported: [input] [output] [temp]. For performance reasons the number of files are limited to: 1024.

Inputs

| Name | Type | Description | | --- | --- | --- | | filepath | STRING | Base directory defaults to [input] user-directory. Supports glob-pattern expansion subdir/**/*.png. Use suffix [input] [output] or [temp] (mind the leading whitespace!) to specify a different ComfyUI user-directory. | | _extra | STRING | (optional) try to load additional file from string (plaintext or base64) |

Outputs

| Name | Type | Description | | --- | --- | --- | | content | STRING 𝌠 | File content for text files, base64 for binary files. | | image | IMAGE 𝌠 | Image batch tensor. | | mask | MASK 𝌠 | Mask batch tensor. | | metadata | STRING 𝌠 | Exif data from ExifTool. Requires exiftool command to be available in PATH. |

Load Any Video

Load Any Video

(ComfyUI workflow included)

This node is a duplicate of nodes_video.py LoadVideo except with the fix included from issue#11017 It is required to load videos based on annotated filepaths which are restricted to user directories: [input] [output] [temp]..

Inputs

| Name | Type | Description | | --- | --- | --- | | file | STRING | |

Outputs

| Name | Type | Description | | --- | --- | --- | | None | VIDEO | |

Path OutputList

Path OutputList

(ComfyUI workflow included)

List directory content via glob patterns and split each filepath into it's parts.

filepath supports ComfyUI's annotated filepaths [input] [output] or [temp]. filepath also support glob-pattern expansions subdir/**/*.png. Internally uses python's glob.iglob.

bare_strings is intended for different styles of path recombinations, e.g. "{fulldir}/{basename}.{ext}" vs "{fulldir}{basename}{ext}"

As a design choice the ComfyUI user directory annotation is used in the glob pattern (to allow more flexible patterns) insted of providing a separate variable (in a combo box).

Inputs

| Name | Type | Description | | --- | --- | --- | | glob | STRING | Glob-pattern expansion subdir/**/*.png to list directory content. Base directory defaults to [input] user-directory. Use suffix [input] [output] or [temp] (mind the leading whitespace!) to specify a different ComfyUI user-directory. | | limit | INT | Limit maximum number of paths to collect (-1.. unlimited) | | bare_strings | BOOLEAN | Decides if path-parts only contain the bare strings versus safe OS compliant definitions, e.g. if True ext is png vs .png, full_dir is examples/animals vs examples/animals/, and parent_dir may be a empty string vs ./. Note that rel_dir always defaults to . |

Outputs

| Name | Type | Description | | --- | --- | --- | | filepath+ | * 𝌠 | Full filepath (relative to a ComfyUI directory) including annotations. Recommended if you want to be specific and adhere to ComfyUI's path notation.<br>e.g. examples/animals/myfile.png [input] | | filepath | STRING 𝌠 | Full filepath (relative to a ComfyUI directory) without annotations. Recommended if you only load files from input directory anways.<br>e.g. examples/animals/myfile.png | | filename | STRING 𝌠 | Full filename<br>e.g. myfile.png | | basename | STRING 𝌠 | Basename part of the file without extension<br>e.g. myfile | | ext | STRING 𝌠 | Extension. Note that hidden-files (e.g. .bashrc) are considered files without a extension.<br>png if bare_strings=True else .png | | full_dir | STRING 𝌠 | Full directory of the file (relative to a ComfyUI directory)<br>e.g. examples/animals if bare_strings=True else examples/animals/ (note the trailing slash) | | parent_dir | STRING 𝌠 | Immediate parent directory of the file<br>e.g. animals or empty for empty parent if bare_strings=True else ./ | | annotation | STRING 𝌠 | Annotation to reference the ComfyUI user directory<br>input if bare_strings=True else [input] (note the leading whitespace) | | index | INT 𝌠 | Range of 0..count. You can use this as an index. | | count | INT | Total number of files. |

Iterate Begin

Iterate Begin

(ComfyUI workflow included)

Iterate a sub-workflow by executing it from a data list sequentially in item-major order (as opposed to node-major)." You need to connect the flow_control from a IterateBegin to a IterateEnd node. Only use this if a sub-workflow takes a long time to process without any visible progress (see execution stalling problem). Make sure to use the passthrough output slots on output nodes (Preview Image, Save Image etc.) so the intermediate results are visible. Internally uses the node expansion mechanism which duplicates the sub-workflow multiple times for each list item.

lists use(s) is_output_list=True (indicated by the symbol 𝌠) and will be processed sequentially by corresponding nodes.

Inputs

| Name | Type | Description | | --- | --- | --- | | datalist | * | (optional) | | _ | * | Ignore! Only used internally |

Outputs

| Name | Type | Description | | --- | --- | --- | | flow_control | FLOW_CONTROL | You need to connect the flow_control from a IterateBegin to a IterateEnd node. | | item | * | | | index | INT | |

Bake String

Bake String

(ComfyUI workflow included)

Works as a simple string passthrough first but 'bakes' the string into the override field of the workflow JSON and then uses this value instead.

This node may seem strange but it allows to add additional infos on how a specific image was created in a multi-asset workflow.

  • Use-case 1 "per-image parameters": If multiple images are created from an output list, the same workflow is stored for ALL images. This node allows to bake the specific string into the workflow JSON for the very string that was used in a individual image.
  • Use-case 2 "include image": img2img and controlnet workflows require an input image. Used together with a base64 string the full image can be baked into the workflow JSON.

Inputs

| Name | Type | Description | | --- | --- | --- | | string | STRING | The string that will be passed through unless override is set (lazy=True which means upstream nodes won't be executed if override is set) | | override | STRING | If set, will always output this string instead. Used by Save Image (and other save nodes) to bake the value into the workflow JSON. | | limit | INT | Limit of characters which will be baked into the field. | | trim | BOOLEAN | Trims the override string of whitespace characters (like spaces and new lines) before doing the override-check. This prevents triggering the override when a new line was entered by accident. Only disable it if you actually need a whitespace string as an override. |

Outputs

| Name | Type | Description | | --- | --- | --- | | string | STRING | If override is set, will use override, otherwise it's a passtrough of string. | | is_override | BOOLEAN | A bool indicating if the override was used. Useful for If/Else Switches and Execution Blockers. |

Workflow Discriminator

Workflow Discriminator

(ComfyUI workflow included)

Compares workflows and discriminates them to extract the different values as individual OutputLists. You can use this node to restore how each individual image was created from a list of images with the same workflow. Note that ComfyUI's IMAGE doesn't contain the workflow metadata and you need to load the images with specialized image+metadata loaders and connect the metadata to this node. Custom nodes with metadata loaders include:

  • Load Any File.metadata -> JSON OutputList(jsonpath=$.["PNG:Prompt"]).value
  • Crystool 🪛 Load image with metadata.Metadata RAW -> 🪛 Metadata extractor.prompt
  • Simple_Readable_Metadata Simple Readable Metadata-SG.metadata_raw

Inputs

| Name | Type | Description | | --- | --- | --- | | objs_0 | * | (optional) A single object (or a list of objects), usually of a workflow. objs_0 and more_objs will be concateneted together and exist for convinience, if you only want to compare two objects. | | more_objs | * | (optional) Another object (or a list of objects), usually of a workflow. objs_0 and more_objs will be concateneted together and exist for convinience, if you only want to compare two objects. | | ignore_jsonpaths | STRING | (optional) A list of JSONPaths to ignore in case you want to chain multiple discriminators together. |

Outputs

| Name | Type | Description | | --- | --- | --- | | list_a | * 𝌠 | | | list_b | * 𝌠 | | | list_c | * 𝌠 | | | list_d | * 𝌠 | | | jsonpaths | STRING 𝌠 | |

Deprecated nodes

Formatted String

Formatted String

(ComfyUI workflow included)

Creates a string that contains placeholder variables and replaces them with their respective values. [DEPRECATED] Use Comfy Core's Format Text instead. You can still use this one if you want to use the S&R replacements.

Uses python str.format() internally, see Python - Format String Syntax .

  • You can use {a:.2f} to round off a float to 2 decimals.
  • You can use {a:05d} to pad up to 5 leading zeros to fit with comfys filename suffix ComfyUI_00001_.png.
  • If you want to write { } within your strings (e.g. for JSONs) you have to double them: {{ }}.

Also applies search & replace (S&R) syntax such as %date:yyyy-MM-dd hh:mm:ss% and %KSampler.seed%. Thus you can also use it as a GET-node. Note that "search & replace" takes place in Javascript context and runs before node execution.

Inputs

| Name | Type | Description | | --- | --- | --- | | fstring | STRING | Creates a string that contains placeholder variables and replaces them with their respective values.<br>[DEPRECATED] Use Comfy Core's Format Text instead. You can still use this one if you want to use the S&R replacements.<br><br>Uses python str.format() internally, see Python - Format String Syntax .<br>* You can use {a:.2f} to round off a float to 2 decimals.<br>* You can use {a:05d} to pad up to 5 leading zeros to fit with comfys filename suffix ComfyUI_00001_.png.<br>* If you want to write { } within your strings (e.g. for JSONs) you have to double them: {{ }}.<br><br>Also applies search & replace (S&R) syntax such as %date:yyyy-MM-dd hh:mm:ss% and %KSampler.seed%.<br>Thus you can also use it as a GET-node.<br>Note that "search & replace" takes place in Javascript context and runs before node execution. | | a | * | (optional) value that will be as a string at the {a} placeholder. | | b | * | (optional) value that will be as a string at the {b} placeholder. | | c | * | (optional) value that will be as a string at the {c} placeholder. | | d | * | (optional) value that will be as a string at the {d} placeholder. |

Outputs

| Name | Type | Description | | --- | --- | --- | | string | STRING | The formatted string with all placeholders replaced with their respective values. |

KSampler Immediate Save

KSampler Immediate Save

(ComfyUI workflow included)

Node expansion of default CheckpointLoader, KSampler, VAE Decode and Save Image to process as one. [DEPRECATED] Use Iterate Begin -> workflow -> Iterate End pattern instead.

This is useful if you want to save the intermediate images for grids immediately.

Inputs

| Name | Type | Description | | --- | --- | --- | | cpkt_name | COMBO | The name of the checkpoint (model) to load. | | positive | STRING | The conditioning describing the attributes you want to include in the image. | | negative | STRING | The conditioning describing the attributes you want to exclude from the image. | | latent_image | LATENT | The latent image to denoise. | | seed | INT | The random seed used for creating the noise. | | steps | INT | The number of steps used in the denoising process. | | cfg | FLOAT | The Classifier-Free Guidance scale balances creativity and adherence to the prompt. Higher values result in images more closely matching the prompt however too high values will negatively impact quality. | | sampler_name | COMBO | The algorithm used when sampling , this can affect the quality , speed , and style of the generated output. | | scheduler | COMBO | The scheduler controls how noise is gradually removed to form the image. | | denoise | FLOAT | The amount of denoising applied , lower values will maintain the structure of the initial image allowing for image to image sampling. | | filename_prefix | STRING | The prefix for the file to save. This may include formatting information such as %date :yyyy-MM-dd% or %Empty Latent Image.width% to include values from nodes. |

Outputs

| Name | Type | Description | | --- | --- | --- | | image | IMAGE | The decoded image. |

Examples

Simple OutputList

Simple OutputList example

(ComfyUI workflow included)

Just uses a String OutputList to separate a string and produce 4 images in one run.

Video Tutorials

Combine prompts

Combine prompts example

(ComfyUI workflow included)

Combines two String OutputList with a OutputList Combinations and merges them into the prompt with Format Text. It iterates over all combinations of [cat, dog, rat] x [red, green, blue] = 3 x 3 = 9)

To debug strings it's recommended to use comfyui-custom-scripts Show Text as it outputs a new line for each emitted item.

Combine numbers

Combine numbers example

(ComfyUI workflow included)

Makes use of Number OutputList to generate the number ranges [256, 512, 768] x [768, 512, 256] and connects them to the image width and height to produce image variants in portrait, square and landscape.

Notice that images within a batch always have to be same width and height, wheras here each image has a different image size. This is only possible because it is a list of images.

Combine samplers and schedulers

Combine samplers and schedulers example

(ComfyUI workflow included)

https://github.com/user-attachments/assets/d8da27b9-99d2-4ac5-a6ed-d368d2ae1a38

Makes use of inspect_combo to populate the String OutputList (unneeded entries were deleted) and connects to the COMBO inputs samplers and schedulers. It iterates over all combinations of [euler, dpmpp_2m, uni_pc_bh2] x [simple, karras, beta] = 3 x 3 = 9)

Combine row/column for filename

Combine row/column for filename example

(ComfyUI workflow included)

Makes use of the index combined the same way as the prompts, which gives as the rows and columns. Format Text produces the filename prefix img_{c:02d}_row_{ad}_col_{b}.

Compare LoRA-model and LoRA-strength

Combine LoRA-model and LoRA-strength example

(ComfyUI workflow included)

https://github.com/user-attachments/assets/64e118c1-15f3-463b-b439-37e1a1f5b62b

Custom LoRAs:

Makes use of inspect_combo to populate the String OutputList with the model names (unneeded entries were deleted), and a corresponding String OutputList with the trigger words. Both OutputLists are combined with a Number OutputList each to iterate over all combinations of [modelA, modelB, modelC] x [0.4, 0.7, 1.0] = 3 x 3 = 9 and [triggerA, triggerB, triggerC] x [0.4, 0.7, 1.0] = 3 x 3 = 9, so they are in-sync. The LoRA filename and LoRA strength are connected with the LoRA Model Loader, and the trigger word is used to construct a prompt in Format Text.

If you don't need separate trigger words, just delete the second combination altogether, it's much simpler this way!

It might be a little confusing why we need two combinations here, but it is important that the lists are synchronized. Ideally we would only construct a single combination with pairs of [(modelA, triggerA), (modelB, triggerB), (modelC, triggerC)] x lora-strengths but then we would need to deconstruct the (modelX, triggerX) pairs later.

XYZ-GridPlot Simple

XYZ-GridPlot Simple example

(ComfyUI workflow included)

Uses String OutputLists + OutputLists Combinations + Format Text to generate multiple prompts for an image grid. The values of the String OutputLists are directly used as labels for the XYZ-GridPlot and they also define how the grid should be shaped.

Note that batch_size=1 and output_is_list=False. If you set batch_size=4 you get a image grid with the batch as sub-grids. If you also set output_is_list=True the sub-images will not be arranged together but you will get 4 separate images instead.

https://github.com/user-attachments/assets/a649b701-58a5-47a8-b697-e2a34a39c999

Also see video tutorials for an introduction into XYZ-GridPlots.

Load multiple files with different formats

Load multiple files example

(ComfyUI workflow included)

Uses String OutputList to emit multiple glob patterns that expand, 1. on the directory tests, 2. on any sub-directory ** (in this case: imgs), 3. on all files with a certain file ending (*.png), 4. starting at ComfyUI's [output] directory as the base. This calls Load Any File 3 times, each time with a different format, which again emits multiple files each time, resulting in a list of many files.

Repeat OutputLists

Repeat OutputLists example

Cycle OutputLists

Cycle OuputLists example

The execution stalling problem

One thing you may have noticed when you make a large image grid is that you have to wait for ALL intermediate images to be processed before anything is saved and the grid created. Thus you could loose a lot of processed images when something happens or you cancel the job (though ComfyUI keeps them in cache and should pick up immediately). Another problem that occurs with loader nodes is that the load ALL resources at once before passing on execution which will eventually lead to OOM.

The reason is that ComfyUI process the data list in node-major mode (one node processes all items before proceeding to the next node) instead of list-major mode (one item is processed by a group of nodes before proceeding to the next item). I have filled a RFC specifically for this problem and proposed changes to the execution scheme of ComfyUI. In the meantime there are multiple workarounds:

  • Ignore it and just wait it out (I recommend to start ComfyUI with --cache-ram though, so you can pick up were you left off anytime)
  • Use[Iterate loop nodes]](#iterate loop nodes) with passthrough output nodes (recommended!)
  • Use the PrimitiveInt control_after_generate=increment pattern but it requires some manual work.

Iterate loop nodes

Iterate loop nodes

(ComfyUI workflow included)

Makes use of Iterate Begin and Iterate End to mark the nodes between the flow_control as a "sequential group". This works similar to other loop nodes except this one here works with output lists. It's important to use a output node with passthrough to see the intermediate results, otherwise they will only act upon the first item. Newer ComfyUI versions already have them.

(if you want to understand how this works internally see the section for-loops)

The PrimitiveInt control_after_generate=increment pattern

You probably noticed the control_after_generate widget before in the KSampler for seed where it's often set to random. This feature can also be created manually with the Primitive Int node. If you set it to control_after_generate=increment you basically get a counter that increases everytime you run a prompt. When you hook it up as a index in a list selector node, it iterates over entries across multiple prompts. In the Run toolbox you can set the amount of prompts to the number of items in your list to iterate the whole list. This pattern essentially cancels out the effect of OutputLists and will only ever process one item at a time. That's especially useful if you want to test something out. Remember to reset the counter to 0 afterwards!

The PrimitiveInt control_after_generate=increment pattern

And because it is very tedious to add a selector for every single list, the Spreadsheet OutputList includes a select_nth widget which applies the index to all lists at once, and makes everything simpler for complex workflows that use multiple lists.

The PrimitiveInt control_after_generate=increment pattern and Spreadsheet OutputList

Node expansion in code

Another solution is node expansion in code but you literally have to rebuild a pattern in code, see Node expansion in code.

Deprecated: If you want to save the intermediate images after each step you can use the KSampler immediate Save Image beta-node. For this node to be visible in the node searchbox you need to activate Settings -> Comfy -> Show experimental nodes in search.

Advanced Examples

Animating LoRA strength

Animating LoRA strength example

(ComfyUI workflow included)

Makes use of a Number OutputList to iterate over the range 0.0..1.0. Note that num is +1 because we to split it into well-formed floatingpoint values and endpoint=True to include 1.00 in the values. Also uses Format Text with {0:0.2f} and KJNodes's Add Label to add the strength information as well-formatted label into the image itself. Note that the images are rebatched into batch_size=count because Create Video expects batches.

https://github.com/user-attachments/assets/da707caa-6342-40db-9f48-4b8384b55867

Also see

Old Stable Diffusion 1.5 example:

https://github.com/user-attachments/assets/59220dec-bafc-4abc-9294-ae76e3372da8

Iterate checkpoints

The Load Checkpoint node also suffers from the execution stalling problem in that it loads ALL checkpoints at once before emitting them which will likely cause OOM. You can workaround this limitation by using the Iterate Begin and Iterate End nodes.

Iterate checkpoints example

(ComfyUI workflow included)

Makes use of Iterate Begin and Iterate End to mark the nodes between the flow_control as a "sequential group". This works similar to other loop nodes except that they work with output lists. It's important to use a output node with passthrough to see the intermediate results, otherwise they will only act upon the first item. Newer ComfyUI versions already have them.

XYZ-GridPlots with Supergrids

I recommend to start ComfyUI with --cache-ram for this example if you want to experiment with the settings alot!

XYZ-GridPlots with Supergrids example

(ComfyUI workflow included)

Uses two XYZ-GridPlot in sequence to put one image grid inside the other. For more complex image grids the question always is: How should the axis be ordered and in which way the images be shuffled, e.g. do we want to show cat|dog|rat x red|blue|green and then the batch next to each other in a subgrid (RxCxB), or four separate images each with a grid of cat|dog|rat x red|blue|green (BxCxR). To achieve this you can play around with the options order=outside-in|inside-out and output_is_list=False|True, but make sure the row_labels and col_labels match what you want to achieve, as this info is also used how the grid is shaped.

Immediately save intermediate images of image grid

Generating a huge grid like this also suffer from the execution stalling problem. You can workaround this limitation by using the Iterate Begin and Iterate End nodes with an output node passthrough.

ImageGrids example

(ComfyUI workflow included)

Makes use of Iterate Begin and Iterate End to mark the nodes between the flow_control as a "sequential group". This works similar to other loop nodes except that they work with output lists. It's important to use a output node with passthrough to see the intermediate results, otherwise they will only act upon the first item. Newer ComfyUI versions already have them.

Load all images from grid

Let's say you generated a lot of images for your grid and (hopefully) stored them with some clever naming scheme, e.g. cell_{c:02d}-{a}-{b} like in the previous example. Now you need to load them from the output folder, without accidentally loading any other images. This uses the same prompt combination as before but uses the string to load the image filename. The following workflow makes use of Load Any File,

Load Image with Format Text

(ComfyUI workflow included)

External custom nodes which support image loading via path:

Bake values into workflows

If multiple images are created from an output list, the same workflow is stored for ALL images. This workflows allows to bake the specific string into the workflow JSON for the very string that was used in a individual image.

Bake String Iterate Loop Nodes before

(ComfyUI workflow included)

Makes use of Bake String which works as a string passthrough during the workflow phase but on Save Image stores the string in override field. It's important to use the Iterate Begin -> worklfow -> Iterate End pattern here and use the Bake String in a passthrough so it gets executed on every iteration, otherwise it will only be execute once (for cat) and all images use the same string. The Bake String output is hooked on a OutputLists Combinations which we exploit as a on-signal node, because it needs to be part of the execution. The actual value is used from Iterate Begin, otherwise - when you drag the output image into the workspace - will use the same string for all images. To fix this, see the next example.

When you drag an output image onto the workspace you get the following:

Bake String Iterate Loop Nodes after

(e.g. dog, which was on the second iteration)

Here you can see that the string dog is baked into the override field.

Bake values into flexible workflows

This workflow lets you use the same workflow to either re-generate the individual image or the original workflow for all images. For example when generating a XYZ GridPlot you want know which parameter was used for an individual image but also re-generate the whole grid again.

Bake String in XYZ GridPlot before

This workflow is an expansion of bake values into workflows and the XYZ GridPlot. Makes use of an Bake String node for the whole workflow (the outer) and one Bake String for the iterated workflow (the inner in Iterate Begin -> worklfow -> Iterate End). To check if this workflow is baked or not the outer Bake String.is_override is used together with a If/Else Switch to either use the original list (not baked) or use only one item (baked), which will be overriden by the sub-sequent inner Bake String. Hence, if the workflow is not baked, the list items will be used as is, otherwise the list collapses to one item which gets overriden by the inner string and only executes once. Because the downstream nodes for XYZ GridPlot don't make sense for a single item we block further execution with a Execution Blocker based on the outer Bake String.is_override.

When you drag an individual output image into the workspace you get the following:

Bake String in XYZ GridPlot after

Here you can see that the string a dog with a green hat is baked into the override field and when you execute the workflow again it generates the image again of which this individual image was a part of. If you clear the outer and inner override strings you can generate the full image grid again.

Bake images into workflows

This allows to ship input images with the workflow. Useful for img2img or Control-Net workflows.

Bake String in Load Any File before

(ComfyUI workflow included)

Makes use of Load Any File (1st) to load a image file as a base64 string and Bake String to insert this string as the override value into the workflow (once the image save in Save Image). Another Load Any File (2nd) loads the same image (again!) from the base64 string and passes it on to a img2img workflow. Hence, if the file exists, it will be loaded from disk, otherwise the base64 string will be used instead. When you drag the following output image into your workspace:

Bake String example_baked.png

(This file has ~600 KiB because the image diffusion introduced a lot of noise which PNG doesn't like. The workflow only increased by about 2x18 KiB due to the base64 input image.)

you should see the following worfklow (note the base64 string in Bake String):

Bake String in Load Any File after

If you are asking "Do we really insert a wasteful base64 cleartext version of a binary PNG file into the workflow JSON as a string?" the answer is: "Yes!". It's a image-in-a-JSON-in-a-image :) base64 uses up about +33% more space, so it's okay. Here is what it will look like to take the example.png resized to 16x16 (374bytes) and encoded as base64 (500bytes) in the workflow json:

"widgets_values": [
	"",
	"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAAXNSR0IB2cksfwAAAARnQU1BAACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAN9JREFUKM+tkjEKgzAUhn+1ToJDF88knqFVqIOi0BMU6R0i7aCgOPQE4pm6dCgoQpB2SKrRilTov+SF9/0vLy+R9uSFNdoAyAMAoHUIQNWIHY+IPACtQ1UjAOwYMlsYRBWraavECXs6ccKmrVjMGEls6XLgObUrWUAVC4Cfmj0ji6f7qal2Jae708Q8Yxgk0G5GlgxuRhjhFsYEZRrdIdndeeHyAYBaW16lMEZjHcoXBgIvvx35Vo8+meuXIfCWXkvIygt0f9T40su1ZwwrJf/SxvTziWKjdPUoeZ7/09Ib1L5LGKJX9wUAAAAASUVORK5CYII=",
	10240,
	true
],

You find the original example_bat.png in tests/imgs.

Discriminate multiple files

Iterate checkpoints example

(ComfyUI workflow included)

Similar to the basic Workflow Discriminator example, but uses a Load Any File with a glob pattern expansion to load multiple files, where all files are discriminated against.

Nested iterate loop nodes

Nested iterate loop nodes example

(ComfyUI workflow included)

Iterate prompts from PromptManager

Custom nodes:

PromptManager keeps track of all the prompt you generated in a database which you can annotate with tags and categories. The following workflow allows you to search by text, tags and categories to get selection of the prompts and iterate them.

Load prompts with GET HTTP and extract JSON with JSON OutputList

(ComfyUI workflow included)

Makes use of ComfyUI-HTTP's HTTP GET Request to call PromptManager's search API route at http://127.0.0.1:8188/prompt_manager/search and JSON OutputList to extract the text field using a JSONPath. The prompts are emitted as an OutputList and will be processed sequentially.

Examples for Video workflows

Iterate durations

Iterate durations video example

(ComfyUI workflow included)

Makes use of Number OutputList to generate a range of durations [1.0, 2.0, 3.0, ...] as a data list. The data list is connected with Iterate Begin -> workflow -> Iterate End to run the sub-workflow sequentially over the list.

Iterate resolutions

Iterate resolutions video example

(ComfyUI workflow included)

Makes use of Spreadsheet OutputList to convert the resolution table note from the official text2video template into a list of resolutions [(608,352),(736,416),(864,480), ...] as a data list. The data list is connected with Iterate Begin -> workflow -> Iterate End to run the sub-workflow sequentially over the list.

Iterate durations, measure time, write CSV

The following workflow is a extension of "Iterate durations", it generations multiple videos, measures the times and writes them to a CSV file.

Iterate durations, measure time, write CSV

(ComfyUI workflow included)

Accompanying reddit discussion

Custom nodes:

Make sure you understand the simpler examples above, how Iterate Begin/End works and also how execution order works in ComfyUI (see explanation from rgthree). In this example some Pipe from -> Timer -> Pipe to patterns were added to the MinimaxH3 default template before the KSampler and VAE Decode nodes to measure their execution time. It's important that all dependent nodes are finished before Timer=start (otherwise, if we only used the noise seed for example, the timer might start before all the models are loaded). It's also important that the output passes through the Timer=stop and that this is the only source for any downstreams node (otherwise, if we made KSampler.samples go to VAE Decode independently it might run the decoder before stopping the timer). The comma-separated lines for the CSV files are built with a Format Text and written using save STRING to file (in the subgraph append STRING to file). There are additional notes in the workflow to explain specific parts.

Example output for duration:

index,duration,sampler,decode_video,decode_audio,total,unit
0,2000,48571,11047,330,59948,ms
1,3000,61037,14469,370,75876,ms
2,4000,90756,20683,434,111873,ms
3,5000,99017,24955,530,124502,ms

plot duration

You can easily adopt this workflow for other values:

step size

index,stepsize,sampler,decode_video,decode_audio,total,unit
0,5,56212,26062,824,83098,steps
1,10,109488,24594,505,134587,steps
2,15,170691,21602,511,192804,steps
3,20,193693,24987,520,219200,steps

plot step size

step resolution

index,resolution,sampler,decode_video,decode_audio,total,MP
0,608 x 352,209384,20935,411,230730,0.21
1,736 x 416,325715,26882,414,353011,0.31
2,864 x 480,528057,62051,519,590627,0.41
3,960 x 544,705908,50888,559,757355,0.52
4,1056 x 608,959507,60587,519,1020613,0.64
5,1152 x 640,1278660,74005,571,1353236,0.74

plot step resolution

Generate multiple videos from spreadsheet

https://github.com/user-attachments/assets/f6705477-ad88-4f23-9178-0ea24362948f

Accompanying reddit discussion

Generate multiple videos from spreadsheet

(ComfyUI workflow included)

Makes use of Load Any File node to load a .csv spreadsheet file and feeds the text content into a Spreadsheet OutputList. The spreadsheet separates the data by separator=; and provides each line one-by-one as a data list. selectors are empty which means every column is selected. The workflow uses values_dict as the data list which contains the row as a dictionary of key-value pairs. The data list is forwarded to a Iterate Begin -> workflow -> Iterate End pattern which is required to make the intermediate results of slow workflows (t2v) available on each iteration. Each row is a dictionary and is provided in Format Text where we can access the column via {a[colname]} to construct the prompt. The prompt is then forwarded to a standard Text To Video MiniMax H3 template for generation. Another Format Text + {a[name]} is used to construct a readable filename for each video.

media/example_video.csv:

name;description;voice;weapon;killed;enemy;scene;style
Achilles;a muscular ancient Greek warrior in bronze scale armor and a crested helmet;fierce and booming ancient male voice;a long bronze spear with an ash wood shaft;friend;a tall Trojan prince in ornate silver armor and a plumed helmet holding a bloody sword;windy dusty plains outside the massive stone walls of Troy;epic ancient war blockbuster
Beowulf;a towering muscular Norse warrior with long blonde braids and chainmail;deep and boastful Scandinavian male voice;a massive iron broadsword with a golden hilt;king;a terrifying pale female swamp monster with glowing eyes and razor-sharp claws;dark misty cavern filled with glowing treasure and muddy water;dark fantasy epic
King Arthur;a regal middle-aged king in shining silver plate armor and a white tunic;noble and authoritative British male voice;a glowing straight sword with a jeweled crossguard;knight;a young treacherous knight in dark spiked armor with a tattered red cape;foggy muddy battlefield with broken banners and a blood-red sunset;gritty medieval historical drama
Red Riding Hood;a young girl in a bright red wool hooded cloak and a brown peasant dress;innocent but suddenly furious young female voice;a heavy steel woodsman axe with a long wooden handle;grandmother;a large terrifying wolf walking on two legs wearing a tattered nightgown and cap;dark creepy dense forest with twisted thorny trees and heavy fog;dark gothic fairy tale horror
Spartacus;a rugged muscular Thracian gladiator in leather straps and bronze arm guards;gritty and passionate Mediterranean male voice;a curved Thracian sica sword with a wide blade;brother;a wealthy arrogant Roman senator in a white toga with a purple border and a golden laurel;blood-stained sandy gladiator arena with towering stone seats and cheering crowds;epic historical sword-and-sandal
Joan of Arc;a determined teenage girl in custom-fitted silver plate armor and a short black bob haircut;fervent and commanding young French female voice;a steel broadsword with a fleur-de-lis engraved blade;squire;a cruel English bishop in dark flowing ecclesiastical robes and a tall mitre hat;smoky muddy 15th-century battlefield with siege towers and burning wagons;gritty medieval war epic
Snow White;a beautiful young woman in a yellow skirt blue bodice and a red ribbon with pale skin;soft but suddenly vengeful young female voice;a sharp iron dwarven pickaxe with a leather grip;dwarf;an old wicked queen in a black hooded cloak with a tall spiked collar holding a glowing red apple;snowy pine forest with a small rustic cottage and glowing woodland animals;dark fantasy fairy tale
Odysseus;a weathered middle-aged Greek king with a curly beard a tattered tunic and a tired expression;cunning and weary ancient male voice;a large wooden recurve bow with a thick animal gut string;dog;a massive one-eyed cyclops with dirty matted hair holding a giant wooden club;cavernous dark limestone cave filled with giant sheep and a massive boulder door;ancient mythological adventure
Ragnar Lothbrok;a charismatic Viking jarl with long braided blonde hair blue face paint and a fur mantle;intense and raspy Scandinavian male voice;a broad iron Danish axe with a long wooden haft;shieldmaiden;a cruel Northumbrian king in a golden tunic and a heavy iron crown holding a venomous snake;muddy snowy Viking village with longhouses and burning ships;gritty Viking historical drama
Robin Hood;a cheerful outlaw in Lincoln green tights a brown tunic and a feathered cap;witty and charismatic British male voice;a tall yew longbow with a linen string;peasant;a corrupt wealthy sheriff in a heavy velvet robe a fur collar and a gold chain;lush green Sherwood forest with massive ancient oak trees and dappled sunlight;classic swashbuckling adventure

Either copy to ComfyUI/input or copy-paste directly into the Spreadsheet OutputList.

Load multiple video files from disk

Load multiple video files

(ComfyUI workflow included)

Makes use of the Path OutputList to generate a data list of filepaths in the output directory. For each iteration the filepath is used in Load Any Video to load the video file and forwarded to the default Minimax H3 reference2video workflow to put the fennec fox girl in the reference video.

Notes:

  • The only reason the Load Any Video exists is because the official node doesn't support dynamic inputs.
  • If you want to iterate over ALL videos in a directory (instead of a glob) you can use the Comfy Core Load Video (from Folder) instead.
  • The Iterate Begin -> workflow -> Iterate End pattern is only required to make the intermediate results of slow workflows (ref2v) available on each iteration.

Iterate resolutions, iterate durations, measure time, write CSV

The following workflow is a extension of "Iterate durations, measure time, write CSV", it generations multiple videos for each combination of resolution x durations, measures the time and writes it to a CSV file.

Iterate durations, measure time, write CSV

(ComfyUI workflow included)

Accompanying reddit discussion

Custom nodes:

Make sure you understand the simpler examples above, how Iterate Begin/End works and also how execution order works in ComfyUI (see explanation from rgthree). In this example some Pipe from -> Timer -> Pipe to patterns were added to the MinimaxH3 default template to measure the total execution time (in contrast to the individual times of the sampler and decoder as in the previous example). Note that this also includes the model loading time and will produce wrong results on the first generation. To mitigate this a zero duration run was added. It's also important that the output passes through the Timer=stop and that this is the only source for any downstreams node. The comma-separated lines for the CSV files are built with a Format Text and written using save STRING to file (in the subgraph append STRING to file). There are additional notes in the workflow to explain specific parts.

Example output

resolution\video length,0.0,1.0,2.0,3.0,4.0,5.0,6.0,7.0
608 x 352,34694,46922,57616,78560,114835,134380,150945,183893
736 x 416,29459,49666,84211,109523,168462,202305,223172,261975
864 x 480,34779,70248,119980,161840,259740,314701,359692,445586
960 x 544,36226,79850,147996,203899,315231,383680,491456,603520
1056 x 608,35898,103637,172406,243467,417185,507748,753206,794601
1152 x 640,33885,113704,186834,262586,454986,601821,896350,1212095
1216 x 672,39751,178621,211445,304997,552472,728026,1240525,1390142
1280 x 736,41593,187896,253539,368939,669417,850895,1483785,1791947

heatmap resolution x duration

plot resolution x duration

XYZ-GridPlots with Videos

This workflow is based on Iterate resolutions, iterate durations and the video files generated from their and puts all video files into a XYZ GridPlot.

https://github.com/user-attachments/assets/f925e59e-c420-49aa-b75a-fd970e35f5c6

XYZ-GridPlots with Videos example

(ComfyUI workflow included)

Custom nodes:

Uses the same Number OutputList for durations and Spreadsheet OutputList for resolutions as before. They are connected such that the same file names are built again. Then uses Load Any Video to load a video file based on a dynamic string. For all used resolutions we do some calculation to fit the final video into 1920x1080 (plus some label area) and scale then scale all videoframes down accordingly. We also have to find the longest video length and pad missing frames in shorter videos with essentials' Image Expand Batch. Finally all the individual frames are fed into the XYZ GridPlot which has is_output_list=True, so all the image batches become the individual frames of the grid video.

For the file list we assume:

00_608 x 352_0.00_00001_.mp4
00_608 x 352_2.00_00001_.mp4
00_608 x 352_3.00_00001_.mp4
00_608 x 352_4.00_00001_.mp4
00_608 x 352_5.00_00001_.mp4
01_736 x 416_0.00_00001_.mp4
01_736 x 416_2.00_00001_.mp4
01_736 x 416_3.00_00001_.mp4
01_736 x 416_4.00_00001_.mp4
01_736 x 416_5.00_00001_.mp4
02_864 x 480_0.00_00001_.mp4
02_864 x 480_2.00_00001_.mp4
02_864 x 480_3.00_00001_.mp4
02_864 x 480_4.00_00001_.mp4
02_864 x 480_5.00_00001_.mp4
03_960 x 544_0.00_00001_.mp4
03_960 x 544_2.00_00001_.mp4
03_960 x 544_3.00_00001_.mp4
03_960 x 544_4.00_00001_.mp4
03_960 x 544_5.00_00001_.mp4
04_1056 x 608_0.00_00001_.mp4
04_1056 x 608_2.00_00001_.mp4
04_1056 x 608_3.00_00001_.mp4
04_1056 x 608_4.00_00001_.mp4
04_1056 x 608_5.00_00001_.mp4
05_1152 x 640_0.00_00001_.mp4
05_1152 x 640_2.00_00001_.mp4
05_1152 x 640_3.00_00001_.mp4
05_1152 x 640_4.00_00001_.mp4
05_1152 x 640_5.00_00001_.mp4
06_1216 x 672_0.00_00001_.mp4
06_1216 x 672_2.00_00001_.mp4
06_1216 x 672_3.00_00001_.mp4
06_1216 x 672_4.00_00001_.mp4
06_1216 x 672_5.00_00001_.mp4
07_1280 x 736_0.00_00001_.mp4
07_1280 x 736_2.00_00001_.mp4
07_1280 x 736_3.00_00001_.mp4
07_1280 x 736_4.00_00001_.mp4
07_1280 x 736_5.00_00001_.mp4

For-Loops

DISCLAIMER: The following example is in no way intended to glorify the use of for-loops in ComfyUI or any other forms of violence. In no event can the copyright holder be held liable to damages to your brain or mental functions. No one knows how for-loops actually work in ComfyUI and I do in no way claim to posses this wisdom either.

Only use this if you are effected by the execution stalling problem!

Custom nodes:

For-Loop over images

For-Loop images example

(ComfyUI workflow included)

This workflow makes use of Easy-Use's For Loop Start+For Loop End and Index Any and basic-data-handling's create LIST, append (LIST) and convert to Data List to iterate over an outputlist and map the results to a new outputlist, while executing all the sub-nodes within the for-loop for each item. Note that Easy-Use's For Loop is rather intended as a feedback cycle and as such more complicated then it needs to be for this simple value transformation and mapping. What happens here is that we use the outputlists count as the number of cycles, and start with an empty list as the accumlator. The for-loop index is used to access the item in the list, then generates the corresponding and appends it to the list. In the next cycle the list (with one image) is fed back to the start and then generates the next image and appends. In order for an output node (Preview Image, Save Image etc.) to be considered part of the node expansion it needs a "passthrough". You can either use Inspire-Pack's Preview Image Bridge or WAS's Image Save Passthrough.

For-Loop over checkpoints

For-Loop checkpoints example

(ComfyUI workflow included)

The same as above except we are iterate over SDXL checkpoints instead of strings. This workflow loads the checkpoints one-by-one and unloads them after usage.

Note: You have to start with --cache-none for this to work. I tried Unload Models and Purge VRAM V2 but they didn't work with default cache setting.

Background

In August 2024 ComfyUI introduced execution inversion which changed how nodes are processed. Read the Execution Model Announcement and the Execution Model Inversion Guide.

Confused? Good, because you are in good company. It's another example of sophisticated engineering wasted due to lack of any useful documentation. Anyway, one point of this feature is that it enables Node Expansion. You can think of it as a custom node which automatically copy-pastes and links other nodes in the background. If done the right way - by inspecting the node graph and the dependencies during runtime using dynprompt and copy-pasting the nodes in between multiple times - this gives rise to looping functionality (see code here). Many other custom node packs implement different variants of looping, but again - in a cycle of elitism - lack any useful documentation and examples, hence why they are not used anywhere. Which brings me to the conclusion that no one (NO ONE!) knows how for-loops in ComfyUI actually work and they are merely a cruel inside joke to mess with everyone.

Also note that most loop nodes want to support some form of feedback cycle and use the previous result as the input for the next cycle (e.g. a img2img loop). As such the nodes within the loop always need an input image, but because the first output image hasn't been generated yet, they need an initial value independent of the generation. In programming terms you could compare that to a reduce (or Arrow Loop) as opposed to a map (or Functor).

Alternative loop variants

Not in the registry:

If you are one of these developers and read this, thank you for your work, but please fix your documentation and examples!

Non-loops

The following packages have loop in there name but don't provide actual looping functionality in the sense described above:

Third-party custom nodes

I consider the following custom nodes essential for any ComfyUI installation and especially for this custom node pack:

Remember that the core feature of OutputLists Combiner revolves around data lists. One of my design goals is to introduce new custom nodes only if they are necessary in the most common use-cases and avoid cluttering with more duplicate functionality that is already available in other essential nodes. The following is a reference of useful third-party custom nodes which support the use of data lists, multi-prompting or XYZ-GridPlots. The list is opionated and incomplete on purpose. I left out: specializations (string lists, mask lists), niche-uses, endless list manipulation variants, and unpopular custom node packs.

Many custom nodes provide some support for data lists, multi-prompting and XYZ-GridPlots but always lack some essential features, resort to black magic or are not thought all the way through. I also want use this reference to discuss some shortcomings of other nodes and make some arguments why the OutputLists Combiner was necessary.

Data Lists

Any node which declares INPUT_IS_LIST = True or OUTPUT_IS_LIST = (..., True, ...) (in Scheme v1), is_output_list = True or is_input_list = True (in Scheme v3) makes use of data lists. You can search for these patterns in code if want to find which ones make use of it.

Core

Rebatch Images

Rebatch Images

ImageFromBatch

ImageFromBatch

Custom Scripts

Custom Scripts

Show Text

Show Text

The main advantage of this node is that it adds a new entry for every list item, whereas most other string output nodes only show the first or last entry.

Repeater

Repeater

Impact Pack

Impact-Pack

List Bridge

List Bridge

Uses INPUT_IS_LIST=True which means it collects and concatenates all input lists.

Make List (Any)

Make List (Any)

  • Useful if you want to manually create a data list.
  • Also see List of Any.

Make Image List

Make Image List

Same as Make List (Any) except it's type-safe on images.

Select Nth Item (Any list)

Select Nth Item (Any list)

Image List To Image Batch

Image List To Image Batch

Image Batch To Image List

Image Batch To Image List

Inspire Pack

Inspire Pack

Float Range

Float Range

An alternative to Number OutputList if you prefer steps instead.

Crystools

Pipe To and Pipe From

Pipe To and Pipe From

List of Any

List of Any

  • Useful if you want to manually create a data list.
  • Also see Make List (Any).

KJNodes

KJNodes

Get Images From Batch Indexed

Get Images From Batch Indexed

ComfyUI Essentials

ComfyUI Essentials

Batch Count and Get Image Size

Batch Count Get Image Size

Image List To Batch

Image List To Batch

An alternative to Image List To Image Batch although I recommend the Impact-Pack version because it also provides the inverse.

Easy-Use

Easy-Use

Length Any

Length Any

Index Any

Index Any

An alternative to Select Nth Item (Any list)

Image List To Image Batch

Image List To Image Batch

An alternative to the Impact-Pack variant.

Image Batch To Image List

Image Batch To Image List

An alternative to the Impact-Pack variant.

ComfyRoll

ComfyRoll

See List Nodes

Basic Data Handling

Basic Data Handling

See Data List

Bjornulf

Bjornulf Custom nodes

Job Iterator

Job-Iterator

Multi-Prompts

Core

Load Image Dataset from Folder

Load Image Dataset from Folder

  • Core node, but I recommend Load Any File instead.
  • Note: this is a BETA node and you need to activate experimental nodes in settings.

Regex

Impact-Pack

Impact-Pack

Inspire-Pack

Inspire-Pack

Load Image List From Dir (Inspire) and Load Image Batch From Dir (Inspire))

Load Image List From Dir Load Image Batch From Dir

  • Allows to load from arbitrary directory (warning: this has security implications!).
  • I recommend Load Any File instead, but this one provides more settings.

Load Prompts From Dir

Load Prompts From Dir

Load Prompts From File

Load Prompts From File

Easy-Use

See PromptList

See Wildcards

List-like types

When you open the node searchbox and filter by types you often stumble upon list-like types, which are not actual data lists, and contribute to some confusion:

  • FLOATS from Core and only used in Create Hook Keyframes From Floats
  • LIST from WAS Node Suite (old): a wrapper for python list of any type
  • ListString from Crystools: a wrapper for python list of strings
  • RangeFloat and RangeInt from Easy-Use
  • INT,FLOAT from ComfyUI Essentials: a variable type which supports INT and FLOAT, sometimes declared as NUMBER

XYZ-GridPlots

OutputLists Combiner

Recommended!

OutputLists Combiner

  • very versatile dispite the minimal amount of nodes
  • native integration of LoRAs, checkpoints and other COMBOs, no custom KSampler required
  • lots of examples
  • lots of documentation including multi-lingual node documentation

Images Grid

Images Grid

  • Very simple and intuitive but restricted feature set
  • Minor issue: manual list population with separators instead of native lists
  • Minor issue: duplicate nodes (ImagesGridByColumns, ImagesGridByRows)

Core

Image Grid

  • Simple image grid but no support for labels
  • Note: this is a BETA node and you need to activate experimental nodes in settings

Pixaroma

Pixaroma

  • YouTube - ComfyUI XY Plot Ep21 in-depth video tutorial
  • provides a full configuration suite in a XY Plot output node which makes it simple for trivial cases
  • technically works kinda backwards: the output node requests multiple images
  • fundamentally it fights against the ComfyUI paradigm and tries to do everything in one node

WAS Node Suite

WAS Node Suite (old)

Create Grid Image and Create Grid Image from Batch

  • Simple image grid but no support for labels

Soze

Soze

  • versatile due to data lists
  • requires a lot of manual entries due to extra textfields for every input
  • no documentation, no examples, only a node overview image

Easy-Use

Easy-Use

xyAny

ComfyRoll

ComfyRoll

TinyTerra

TinyTerra

xyPlot (and advanced xyPlot)

  • requires custom KSampler and doesn't integrate natively with ComfyUI
  • requires to learn custom syntax to enter images and labels manually, or use custom context-menu black magic to fill entries, or use extra nodes to generate them

Efficiency Nodes

Efficiency Nodes

  • requires custom KSampler
  • lots of specialized nodes for singular use-cases (for Sampler/Scheduler, LoRAs etc.)

D2 Nodes

D2 Nodes

  • hidden examples
  • requires custom KSampler
  • lots of specialized nodes for singular use-cases (for Sampler/Scheduler, LoRAs etc.)

qq-nodes

qq-nodes

  • requires multiple round-trip black magic to populate the grid

Cross-integration

FranckyB - FBnodes

  • "LoRA List+: Utility node for testing LoRAs, allows adding LoRAs to a list from anywhere on disk. Used with Outputlists-Combiner"

Development

This node pack

  • Node interface design: the core feature of OutputLists Combiner revolves around data lists. One of my design goals is to introduce new custom nodes only if they are necessary in the most common use-cases and avoid cluttering with more duplicate functionality that is already available in other essential nodes, see third-party nodes.
  • Research: I try to research the ecosystem and honor existing solutions first before implementing something new.
  • Tutorials: I try to provide documentation and examples for every node and use-case, because I hate that in other nodes packs. If you find anything to be unclear, please let me know!
  • Documentation: is generated from /readme via a pytest test_generate_docs.py (it's akward, I know, but I get the ComfyUI API in code this way).
  • Debugging: launch ComfyUI via vscode launch and then just set breakpoints in code.
  • Filestructure: I put this repo as a symlink in ComfyUI/custom_nodes. This lets me keep the files separate while still allowing me to start it with Comfy. Also I can comfy-cli node publish within my Comfy installation.
  • Code style: I use Elastic Tabstops Redux for vscode. There are git filter clean which can be added with git config --local filter.removeAlignmentSpaces.clean '"elastic-tabstops -r"'.

Tools

Custom node development

Credits

In 2026-09-02 I stumbled upon M1kep - KepListStuff which is a genuine spiritual predecessor to this node suite. It had everything from list generators to native XY grids already. Unfortunately the terms xy, grid or plot don't appear anywhere on the frontpage and that's probably why it's so hard to find.

<a href="https://www.star-history.com/?repos=geroldmeisinger%2FComfyUI-outputlists-combiner&type=date&legend=top-left"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=geroldmeisinger/ComfyUI-outputlists-combiner&type=date&theme=dark&legend=top-left&sealed_token=O_fzwwpfYfiDJD0lGW8DWEo0HzVwS7H1PWWSOMjKP0EGtrR7B_ERdL4w5Jb-97wz1i0kR5mDToKVTPNlvQQR3UGu4tamtdMLAvC-1yesfB84l1pJLPmRvk5QCDH0-4hI0ioByj3bdN8HRyvT6KkK_kD3ZRoP88hukJYn2BadCgvIb6bmQ2XHTpfyoaMq" /> <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=geroldmeisinger/ComfyUI-outputlists-combiner&type=date&legend=top-left&sealed_token=O_fzwwpfYfiDJD0lGW8DWEo0HzVwS7H1PWWSOMjKP0EGtrR7B_ERdL4w5Jb-97wz1i0kR5mDToKVTPNlvQQR3UGu4tamtdMLAvC-1yesfB84l1pJLPmRvk5QCDH0-4hI0ioByj3bdN8HRyvT6KkK_kD3ZRoP88hukJYn2BadCgvIb6bmQ2XHTpfyoaMq" /> <img alt="Star History Chart" src="https://api.star-history.com/chart?repos=geroldmeisinger/ComfyUI-outputlists-combiner&type=date&legend=top-left&sealed_token=O_fzwwpfYfiDJD0lGW8DWEo0HzVwS7H1PWWSOMjKP0EGtrR7B_ERdL4w5Jb-97wz1i0kR5mDToKVTPNlvQQR3UGu4tamtdMLAvC-1yesfB84l1pJLPmRvk5QCDH0-4hI0ioByj3bdN8HRyvT6KkK_kD3ZRoP88hukJYn2BadCgvIb6bmQ2XHTpfyoaMq" /> </picture> </a>

The original repo is located at https://github.com/geroldmeisinger/ComfyUI-outputlists-combiner

<a href="https://www.buymeacoffee.com/GeroldMeisinger" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" ></a>