Nodes/Kinburg-Nodes/Image Compare (HTML)
ComfyUI Node

Image Compare (HTML)

The A/B gallery you used to build by hand — as one node

By Kinburg·Created 3 months ago·Updated 6 days ago· 1
Image Compare (HTML)
  • images
  • settings_data
  • reference
  • images_captioned
  • html_path
  • url
titleImage comparison
columns3
overlay_captionstrue
caption_positionbottom
font_size0
filename_prefixcompare
save_captioned_imagesfalse
auto_collecttrue
captions
prompts
times
output_dir
embed_imagesfalse
save_prompts_txtfalse
report_db
reference_index-1
judge_data

Every serious workflow eventually hits the same wall: you generated twenty variations of a LoRA, a checkpoint, a cfg sweep, and you need to actually look at them side by side before picking a winner. In core ComfyUI that means Save Image nodes, a file explorer, and a lot of alt-tabbing. Image Compare (HTML) is the node that collapses that whole ritual into one run: it takes your batch and writes an interactive HTML gallery you open in the browser - grid, before/after slider, opacity overlay, synced zoom and pan, even a loupe. It's the best node in Kinburg-Nodes, and honestly the reason to install the pack at all.

It's also the node in the pack that leans hardest on the pack's wider ecosystem. It's designed to sit at the end of an accumulator-driven sweep - wire it the outputs of Get Accumulator (images), (captions), (prompts), (texts) - so a batch of runs becomes a browsable page with each image's caption, full generation prompt, and per-image generation time underneath. The settings_data input (from the pack's Generation Info Filter) renders each run's actual sampler parameters under the image, which is the thing you genuinely need when you're comparing a 12-step recipe against a 30-step one and you've forgotten which was which.

The headline trick is the reference input. Wire in a baseline - the source of an upscale, or the fp16 output when you're checking a quantized model - and the node computes SSIM + PSNR for every image against it and adds a "Similarity" sort to the page. That's a real objective tiebreaker on top of your eyes, and it turns the node from a gallery into an evaluation tool. (No reference? reference_index uses one image in the batch as the baseline instead; -1 turns metrics off.)

Only images is strictly required. The rest of the inputs are sockets, not widgets - wire captions (one per line), prompts (one block per image, separated by a --- line), times, judge_data (a Vision LLM Judge's results, if you want AI verdicts rendered per image). For output: images_captioned is a list of images with captions drawn on (so mixed-size inputs stay separate rather than being forced into one batch), html_path is the file, and url is a clickable served link - open the page straight from the node.

How it's saved

By default it writes a portable folder - <prefix>_<datetime>/ with a light index.html and an images/ subfolder using relative links. Zip it, share it, open it offline, it all works. Flip embed_images on and you get one self-contained .html with every image inlined as base64 - single file, much larger. Both open from the url output. Your review state - hide, star rating, tags, comments - persists in browser localStorage, so you can come back to a sweep the next day and still see which one you rejected.

Installing

It's part of Kinburg-Nodes (ComfyUI Manager → search "Kinburg-Nodes", or git clone https://github.com/Kinburg/Kinburg-Nodes into custom_nodes and restart). Per the README, Image Compare needs nothing extra beyond ComfyUI's bundled deps. The full sweep setup - accumulators, Generation Info - comes with the same pack, so it all slots together.

Where people get tripped up

The auto_collect toggle defaults to on and quietly re-wires every accumulator in your graph right before the workflow queues, so a Set node you just added or muted is picked up without a click. If you ever see images appearing on the page that you didn't think you'd collected, that's the culprit - flip it off and use the manual 🔌 Collect All button. And don't forget the page's visibility toggles: captions, prompts, settings, metrics and judge sections each have their own on-page switch, so a "cluttered" comparison is usually just one toggle away from clean.

CategoryKinburg-Nodes/image/compare

Inputs (20)

NameTypeDefaultDescription
imagesIMAGEImages to compare — a batch OR an image list (e.g. from Get Accumulator (images list)); different sizes are fine.
titleSTRINGImage comparison
columnsINT31–12Default number of columns in Grid mode
overlay_captionsBOOLEANtrueDraw captions onto the 'images_captioned' output
caption_positionCOMBObottom2 options: bottom, top
font_sizeINT00–200Caption font size in px; 0 = auto from image width
filename_prefixSTRINGcompare
save_captioned_imagesBOOLEANfalseAlso save the captioned images as PNG files in the output folder
auto_collectBOOLEANtrueRe-wire every Get Accumulator in the graph automatically, right before the workflow is queued — so a Set you just added, removed, muted or bypassed is picked up without a click. Off: only the '🔌 Collect All' button collects. (Purely an editor convenience; the backend ignores this value.)
captionsoptSTRINGOne caption per line, aligned with the image batch (e.g. from Get Accumulator (captions)). Missing lines become empty.
promptsoptSTRINGFull generation prompts, one block per image separated by a '---' line (e.g. from Get Accumulator (prompts)). Shown on the page (toggleable). Multi-line prompts are fine.
timesoptSTRINGPer-image generation time — one entry per line, aligned with the image batch (e.g. Stop Timer's 'elapsed' collected via Get Accumulator (texts) with a newline separator). Shown under each image and used for the grid's 'Time' sort. Strings like '12.34 s', '1m 30s', '890 ms' or '00:01:30' are parsed for sorting.
output_diroptSTRINGCustom save folder (absolute path). Empty = ComfyUI output. A served copy is also written to output so 'Open comparison' keeps working.
embed_imagesoptBOOLEANfalseHow the comparison is saved. Off (default): a portable FOLDER '<prefix>_<datetime>/' with a light index.html + an images/ subfolder (relative links) — open it offline, zip/share it, or open it in-app. On: a single self-contained .html with every image inlined as base64 (one file, much larger). Both open from the node's URL output.
save_prompts_txtoptBOOLEANfalseSave each prompt to a .txt file named like its image
settings_dataoptGEN_SETTINGSStructured per-image settings from Generation Info Filter's 'settings_data' output. Rendered under each image (one '[Class] param: value' line per field; toggleable) and stored by field (EAV) when you 'Save run to report'.
report_dboptSTRINGSQLite file the page's 'Save run to report' button writes to. Empty = <output>/kinburg/reports.db. The value is shown (and editable) on the page.
referenceoptIMAGEOptional baseline to measure similarity against (e.g. the source of an upscale/img2img, or the fp16 output when checking a quantized model). When connected, SSIM + PSNR are computed for every image vs this reference and shown on the page (with a 'Similarity' sort).
reference_indexoptINT-1-1–4096Used only when no 'reference' image is connected: 0-based index of the image IN THIS BATCH to use as the baseline (every image is measured against it). -1 = off (no metrics).
judge_dataoptSTRINGOptional AI verdicts — wire the Vision LLM Judge's 'results_json' here. Each image gets a read-only judge section (stars / tags / comment), separate from your own review, with its own page toggle.

Outputs (3)

NameTypeDescription
images_captionedIMAGE
html_pathSTRING
urlSTRING