Comparison Grid
The one node that brings A1111's XY Plot to ComfyUI
- model
- clip
- vae
- positive
- negative
- latent_image
- lora_name
- lora_1
- lora_2
- lora_3
- lora_4
- lora_5
- lora_6
- lora_7
- lora_8
- lora_9
- lora_10
- lora_11
- cell_images
- grid_image
- num_rows
- num_cols
Every A1111 refugee knows the feeling: you're in ComfyUI, you want to see how your LoRA looks at 0.5 vs 0.75 vs 1.0, and you realize the XY Plot tab you took for granted just… isn't there. The stock answer is "wire up a hundred nodes yourself," which nobody wants to do at midnight. Comparison Grid is the fix: one node that takes any two of eight parameters, sweeps them across rows and columns, runs a full generation per cell, and hands you a labeled grid. It's the missing tab, rebuilt as a ComfyUI node.
What it actually does
Under the hood this node is the pipeline, not a wrapper. Give it a model, CLIP, VAE, your positive/negative conditioning, and a starting latent - the same things you'd feed a KSampler - and it:
- Parses your row and column values (one per line).
- Pre-loads every LoRA it'll need from your
loras/folder once. - Iterates each row×col combination, applying the LoRA at that strength and sampling with those exact steps, CFG, sampler, scheduler, denoise, and seed.
- Decodes through the VAE, stamps "row_label | col_label" on the cell, and assembles the finished grid with headers.
The axis choices are the ones you actually tune: lora, strength, seed, steps, cfg, sampler, scheduler, denoise. Two of these become row_axis/row_values and col_axis/col_values. The default_* inputs (default_seed, default_steps, default_cfg, and so on) set the values for everything not on an axis - so a CFG sweep only touches CFG, and your seed stays locked. That last part matters more than people think: "change one variable at a time on a fixed seed" is the universal debugging discipline in this hobby, and this node bakes it in.
The outputs
You get four. cell_images is a batch of all R×C individually-labeled cells (row-major) - useful if you want to feed cells into a SaveImage separately or post-process a single winner. grid_image is the assembled grid with row/column headers, ready for SaveImage or PreviewImage. Then num_rows and num_cols, mostly handy if you're wiring something clever downstream.
Cell labels are styled with font_size, font_color, and bg_color (hex strings, defaults of white-on-black), and there's an optional font_path if you want a custom .ttf instead of the bundled DejaVu fallback. None of it affects the generation, so set it once and forget it.
LoRA specifics
LoRAs load from ComfyUI's standard loras/ folder via folder_paths. Two ways in: pick one from the default_lora dropdown (used when lora isn't an axis), or wire lora_name / lora_1…lora_11 from an upstream download node - the README points at comfyui-model-manager, same author, whose download nodes output filenames. When lora is an axis, those linked inputs override the multiline text, and the node is smart about labels: it pulls "Epoch N" straight out of the filename when it's there.
Installing it
The normal two routes. In ComfyUI Manager, search "comfyui-comparison-grid" and install, or:
cd ComfyUI/custom_nodes
git clone https://github.com/evandcoleman/comfyui-comparison-grid.git
Restart ComfyUI and it appears under image/comparison as Comparison Grid. The nice surprise for a custom node: there's no requirements.txt. It only uses ComfyUI's bundled torch, Pillow, and numpy - no surprise deps to fight, which is rare enough to be worth celebrating in the dependency-hell era.
Where people get burned
- Every cell is a full generation. A 4×4 grid is 16 complete samples at your step count. Treat it as a batch job, not something you leave in a live workflow - and start with a 3×3.
- It never caches. The node's
IS_CHANGEDreturns NaN, so every queue run re-renders the whole grid. Don't connect it to a reroute node you're constantly poking. - Row and column axes must differ. Pick the same parameter for both and it raises a ValueError telling you exactly that.
- It can't fix wrong-architecture LoRAs. An SDXL LoRA on Flux will fail identically in every cell; the grid shows you tuning, not compatibility. Check your base model first.
The 100+ node monster this replaces is the real cost - a 4×4 LoRA-by-strength matrix would be a genuinely ridiculous graph. This collapses it to one box. If you've been putting off systematic testing because the setup felt like a project, this is the point where you stop putting it off.
Inputs (34)
| Name | Type | Default | Description |
|---|---|---|---|
| model | MODEL | — | |
| clip | CLIP | — | |
| vae | VAE | — | |
| positive | CONDITIONING | — | |
| negative | CONDITIONING | — | |
| latent_image | LATENT | — | |
| row_axis | COMBO | 8 options: lora, strength, seed, steps, cfg, sampler, +2 | |
| row_values | STRING | — | |
| col_axis | COMBO | 8 options: lora, strength, seed, steps, cfg, sampler, +2 | |
| col_values | STRING | — | |
| default_lora | COMBO | 1 options: none | |
| default_strength | FLOAT | 1.00-20–20 | — |
| default_seed | INT | 00–18446744073709550000 | — |
| default_steps | INT | 201–10000 | — |
| default_cfg | FLOAT | 7.00–100 | — |
| default_sampler | COMBO | 44 options: euler, euler_cfg_pp, euler_ancestral, euler_ancestral_cfg_pp, heun, heunpp2, +38 | |
| default_scheduler | COMBO | 9 options: simple, sgm_uniform, karras, exponential, ddim_uniform, beta, +3 | |
| default_denoise | FLOAT | 1.000–1 | — |
| font_size | INT | 368–256 | — |
| font_color | STRING | #FFFFFF | — |
| bg_color | STRING | #000000 | — |
| font_pathopt | STRING | — | |
| lora_nameopt | * | — | |
| lora_1opt | * | — | |
| lora_2opt | * | — | |
| lora_3opt | * | — | |
| lora_4opt | * | — | |
| lora_5opt | * | — | |
| lora_6opt | * | — | |
| lora_7opt | * | — | |
| lora_8opt | * | — | |
| lora_9opt | * | — | |
| lora_10opt | * | — | |
| lora_11opt | * | — |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| cell_images | IMAGE | — |
| grid_image | IMAGE | — |
| num_rows | INT | — |
| num_cols | INT | — |