Systematic A/B testing and parameter sweep tooling for ComfyUI workflows. Define ranges or lists of values for any node widget — CFG scale, sampler, steps, prompts, seeds — and the extension automatically queues every combination, then assembles a labeled comparison grid when the batch is complete. Stop tweaking sliders one at a time and start running controlled experiments.
- About The Project
- Getting Started
- Usage
- Node Reference
- Project Structure
- Running Tests
- Roadmap
- License
- Contact
ComfyUI's default workflow executes one prompt at a time. Finding optimal generation settings — the right CFG scale, the best sampler, the most effective prompt — requires manually adjusting widgets, re-queuing, and visually comparing results held only in memory. This process is slow, error-prone, and produces no structured record of what was tested.
Workflow Iterator treats parameter exploration as a first-class workflow operation. You declare what to vary, how many combinations to generate, and where to save results — then walk away while the batch runs.
- Five typed parameter nodes — dedicated nodes for
INT,FLOAT,STRING,COMBO, andSEEDtypes, each with its own parsing and validation logic. - Expressive value syntax — define ranges with step (
10-30, 5), comma-separated lists (euler, dpm_2, ddim), random seeds (random:5), or sequential seeds (increment:100:5). - Two combination strategies — matrix mode generates the full Cartesian product of all parameters; linear mode zips them index-by-index with cycling for curated, efficient runs.
- Automatic labeled comparison grids — the
WIGridCompositornode assembles a 2D grid (for two-parameter matrix sweeps) or a linear strip, with axis labels derived directly from the parameter values tested. - PNG metadata embedding — every saved image carries a structured JSON payload and an A1111-compatible parameter string describing the exact combination that produced it.
- Wildcard expansion — string parameters support
__wildcard_name__file references and{option1|option2}inline syntax, expanding before combination generation. - Live progress overlay — a JavaScript frontend extension polls the active batch status and renders a progress indicator directly on the WorkflowIterator node in the canvas.
- Batch cancellation API — cancel a running batch via HTTP without restarting ComfyUI.
| Component | Technology |
|---|---|
| Backend | Python 3.9+ |
| Image processing | Pillow ≥ 9.0.0, NumPy |
| Async HTTP routes | aiohttp (via ComfyUI's embedded PromptServer) |
| Frontend progress UI | Vanilla JavaScript (ComfyUI extension API) |
| Packaging | pyproject.toml (PEP 517) |
| Testing | pytest |
- ComfyUI — any recent version. (Installation guide)
- Python 3.9 or higher — bundled with most ComfyUI installations.
- Git — for cloning the repository.
Option A — ComfyUI Manager (recommended)
If you have ComfyUI Manager installed, search for "Workflow Iterator" in the Custom Nodes panel and click Install. Restart ComfyUI when prompted.
Option B — Manual install
- Clone the repository into your ComfyUI
custom_nodesdirectory:
cd /path/to/ComfyUI/custom_nodes
git clone https://github.com/YOUR_USERNAME/comfyui-workflow-iterator- Install the Python dependency:
# Using ComfyUI's embedded Python (recommended)
/path/to/ComfyUI/python_embeded/python -m pip install Pillow>=9.0.0
# Or, if using a system/venv Python
pip install Pillow>=9.0.0- Restart ComfyUI. The Workflow Iterator nodes will appear in the node browser under the
Workflow Iteratorcategory.
A complete iteration workflow requires four elements wired together on the canvas:
[WIParameter nodes] ──param_stack──► [WorkflowIterator] ──iteration_meta──► [WIGridCompositor]
│
targets any node's
widget at runtime
(e.g., KSampler.cfg)
Step-by-step:
-
Add a
WI Workflow Iteratornode from the node browser (Workflow Iteratorcategory). Setmodetomatrixorlinearand give the batch a name. -
Add one or more
WI Parameternodes (Int, Float, String, Combo, or Seed). Chain them by connecting each node'sparam_stackoutput to the next node'sparam_stack_ininput. Connect the final node'sparam_stackoutput to theWorkflowIteratornode'sparam_stackinput. -
Configure each parameter node:
parameter_name— a display label (used in grid axis labels and filenames).values— the values to sweep (see syntax reference below).target_node_title— the title of the node whose widget you want to control (e.g.,KSampler).target_widget_name— the exact widget name (e.g.,cfg,steps,sampler_name).
-
Add a
WI Grid Compositornode. Connectiteration_metafrom the WorkflowIterator. This node collects each iteration's output and assembles the grid on completion. -
Queue the workflow. The extension intercepts the submission, generates all combinations, and queues them automatically.
| Type | Format | Example Input | Parsed Result |
|---|---|---|---|
INT |
Range with step | 10-30, 5 |
[10, 15, 20, 25, 30] |
INT |
Comma list | 10, 20, 30 |
[10, 20, 30] |
FLOAT |
Range with step | 0.5-1.5, 0.25 |
[0.5, 0.75, 1.0, 1.25, 1.5] |
FLOAT |
Comma list | 5.0, 7.5, 10.0 |
[5.0, 7.5, 10.0] |
STRING |
Newline-separated | a cat\na dog |
["a cat", "a dog"] |
COMBO |
Comma list | euler, dpm_2, ddim |
["euler", "dpm_2", "ddim"] |
SEED |
Random count | random:5 |
5 random integers |
SEED |
Sequential | increment:100:4 |
[100, 101, 102, 103] |
SEED |
Explicit list | 42, 1337, 9999 |
[42, 1337, 9999] |
Wildcard expansion (STRING type only):
__subjects__ # loads every line from wildcards/subjects.txt
{cat|dog|bird} # inline random selection
a photo of __subjects__ in {summer|winter}
Wildcards are expanded before combination generation, so each resolved string becomes a separate combination value.
| Mode | Behaviour | Best for |
|---|---|---|
matrix |
Full Cartesian product of all parameters | Exhaustive exploration — 3 params × 10 values = 1,000 renders |
linear |
Index-by-index pairing, shorter lists cycle | Curated comparisons — always equals the longest list length |
Example — matrix mode with 2 parameters:
euler |
dpm_2 |
|
|---|---|---|
cfg=5 |
✓ | ✓ |
cfg=7 |
✓ | ✓ |
cfg=9 |
✓ | ✓ |
6 renders, assembled into a 3×2 labeled grid automatically.
Example — linear mode with the same parameters:
| Iteration | sampler | cfg |
|---|---|---|
| 0 | euler |
5 |
| 1 | dpm_2 |
7 |
| 2 | euler (cycling) |
9 |
3 renders. Shorter list cycles to fill.
The extension registers REST endpoints on ComfyUI's built-in server:
# List all active batches
GET /wi/active_batches
# Get progress for a specific batch
GET /wi/batch_status/{batch_id}
# Cancel a running batch
POST /wi/cancel_batch/{batch_id}Example response from /wi/active_batches:
{
"sampler_test_a3f9c12b": {
"total": 6,
"completed": 2,
"name": "sampler_test",
"mode": "matrix",
"cancelled": false,
"age_seconds": 14
}
}The main control node. Detects WIParameter nodes connected via param_stack, generates combinations, and outputs iteration metadata for downstream nodes.
| Input | Type | Description |
|---|---|---|
mode |
COMBO | matrix (cartesian) or linear (zip with cycling) |
batch_name |
STRING | Prefix for output filenames and batch ID |
enabled |
BOOLEAN | Disable to run the workflow once without iterating |
param_stack |
WI_PARAM_STACK | Connect from the last WIParameter node in the chain |
| Output | Type | Description |
|---|---|---|
current_index |
INT | Index of this execution within the batch (0-based) |
total_count |
INT | Total number of iterations in this batch |
batch_name |
STRING | Pass-through of the batch name |
iteration_meta |
WI_ITERATION_META | Structured metadata dict for downstream nodes |
Defines one parameter to sweep. Chain multiple nodes together via param_stack_in → param_stack.
| Input | Type | Description |
|---|---|---|
parameter_name |
STRING | Display label — appears in grid axis labels |
values |
STRING | Value list or range (see syntax table above) |
target_node_title |
STRING | Canvas title of the node to control |
target_widget_name |
STRING | Widget name on the target node |
enabled |
BOOLEAN | Disable to exclude this parameter without disconnecting it |
param_stack_in |
WI_PARAM_STACK | (Optional) Connect from the previous parameter node |
Collects each iteration's output image, then assembles and saves a labeled comparison grid on the final iteration.
| Input | Type | Description |
|---|---|---|
images |
IMAGE | Connect to the image output of your generation pipeline |
cell_width / cell_height |
INT | Size of each image cell in the grid (px) |
padding |
INT | Pixel gap between cells |
label_font_size |
INT | Font size for axis labels |
x_param_name / y_param_name |
STRING | Override which parameter appears on each axis |
save_grid |
BOOLEAN | Save the assembled grid to disk |
output_dir |
STRING | Subdirectory under ComfyUI's output folder |
iteration_meta |
WI_ITERATION_META | Connect from the WorkflowIterator node |
A drop-in replacement for ComfyUI's built-in Save Image node that embeds iteration metadata into the PNG and uses parameterized filenames.
comfyui-workflow-iterator/
├── __init__.py # Extension entry point — node registration,
│ # onprompt hook, HTTP route registration
├── pyproject.toml # Package metadata and dependencies
│
├── nodes/ # ComfyUI node class definitions
│ ├── workflow_iterator.py # WorkflowIteratorNode
│ ├── parameter_input.py # WIParameterInt/Float/String/Combo/Seed
│ ├── save_image.py # WISaveImage
│ └── grid_compositor.py # WIGridCompositor
│
├── core/ # Business logic (no ComfyUI dependencies)
│ ├── parameter_parser.py # Type-aware value string parser
│ ├── combination_engine.py # Cartesian and linear_zip generators
│ ├── iteration_state.py # Thread-safe singleton batch manager
│ ├── grid_builder.py # PIL-based grid assembly
│ ├── metadata.py # PNG metadata construction and embedding
│ └── wildcard.py # Wildcard file and inline expansion
│
├── web/
│ └── workflow_iterator.js # Frontend: progress overlay, node browser
│ # widget picker, value preview counter
│
└── tests/
├── test_parameter_parser.py
├── test_combination_engine.py
├── test_grid_builder.py
├── test_metadata.py
└── test_wildcard.py
The core/ directory has no dependencies on ComfyUI internals and can be imported and tested with a plain Python interpreter.
The test suite uses pytest and requires no running ComfyUI instance.
# Install dev dependencies
pip install pytest Pillow
# Run all tests from the repository root
pytest tests/ -v
# Run a specific module
pytest tests/test_parameter_parser.py -v
# Run with coverage (requires pytest-cov)
pip install pytest-cov
pytest tests/ --cov=core --cov-report=term-missingAll tests live in tests/ and are organised per core module. The core/ package is importable standalone because __init__.py wraps all PromptServer imports in a try/except ImportError block.
- Five typed parameter nodes (Int, Float, String, Combo, Seed)
- Matrix (Cartesian) and linear (zip) combination modes
- Automatic labeled 2D and linear comparison grids
- PNG metadata embedding (JSON + A1111-compatible string)
- Wildcard expansion (
__name__files and{a|b}inline) - Live progress overlay in the ComfyUI canvas
- Batch cancellation via HTTP API
- Full unit test coverage for all core modules
- Persistent experiment history — SQLite-backed log of batch configurations and result paths, queryable across sessions
- Human preference scoring — drag-to-rank or thumbs-up/down UI embedded in the canvas, feeding into a lightweight preference model
- GitHub Actions CI — automated test runs on push and pull request
- ComfyUI Manager official registry listing
Distributed under the MIT License. See LICENSE for full terms.
Jason Tran
- Website: jasontran.pages.dev
- Email: tran219jn@gmail.com
- GitHub Issues: use the issue tracker for bug reports and feature requests.
