Save Image with Custom Metadata
Label your seed, prompt, and CFG right into the PNG
- images
- meta_value_0
- meta_value_1
- meta_value_2
- meta_value_3
- meta_value_4
- meta_value_5
- meta_value_6
- meta_value_7
- meta_value_8
- meta_value_9
Every image ComfyUI saves already carries its own black box: drag it back onto the canvas and the whole workflow graph reconstructs, nodes and links and all. That's genuinely great - it's why "workflow included" became a community norm instead of an afterthought. But it's overkill when all you want is a quick, human-readable record of this one seed or that CFG value without reloading an entire graph to check. MetaSaver is built for that gap: you hand it up to ten name/value pairs, and it writes them into the PNG as their own labeled record, sitting alongside - not instead of - the normal workflow data ComfyUI already embeds.
How it actually works
Under the hood it's refreshingly simple. The node exposes ten numbered pairs, meta_name_0/meta_value_0 through meta_name_9/meta_value_9. meta_name_N is a plain text box where you type the label - "seed", "positive_prompt", "cfg_scale", whatever means something to you. meta_value_N accepts literally anything, because its type is a wildcard: wire in a KSampler's seed output, a CLIPTextEncode's prompt string, a float widget, even a raw string you type by hand. Leave a pair blank and it's silently skipped - no validation to fight, no half-written junk. Everything you do fill in gets written twice: once as a single structured JSON blob (so a script can pull the whole set back in one read) and once as individual flat fields (so a human with exiftool can eyeball just the one they care about).
The inputs and outputs that matter
Two required, the rest optional:
images(IMAGE) - what actually gets saved. Same batch behavior as ComfyUI's own Save Image: connect your VAE Decode output here.filename_prefix(STRING, defaultComfyUI) - same as the stock node, just the filename stem.meta_name_0…meta_name_9/meta_value_0…meta_value_9- your ten label/value pairs, all optional, all skipped if left empty.
There's no output socket - this is a terminal node (is_output_node), same as any Save Image. Nothing downstream to wire it into.
Installing it
ComfyUI Manager: search "MetaSaver", click Install, restart. Or by hand:
cd ComfyUI/custom_nodes
git clone https://github.com/SorenWeile/ComfyUI_MetaSaver
Restart ComfyUI. Nothing else to install - the image side of this pack just leans on Pillow, which ComfyUI already needs for everything else. No model downloads, no extra pip packages, no GPU cost beyond the save itself.
Reading it back, and where people get stuck
Drag the saved PNG back into ComfyUI and you get the full workflow like normal - MetaSaver doesn't touch that path. For your custom fields specifically: open it in Python with Image.open(...).info.get("custom_metadata") for the JSON blob, or run exiftool on the file and look for the individual fields.
The failure mode is almost always the same one: a field with a name typed in but no value actually connected (or the reverse) gets silently dropped rather than erroring, so if a field you swore you filled in isn't showing up, check that both the name box and the value input are populated - not just one. If the node itself is missing from the search menu after install, that's a restart-didn't-fully-restart problem nine times out of ten; check the console log for import errors on the way up.
One judgment call worth making up front: ten fields covers almost everyone. If you're the type running big parameter sweeps and want to log more than ten things per image - batch id, a LoRA stack, multiple CFG values, freeform notes - that's what MetaSaverDynamic is for, the same node with twenty pairs instead of ten. Don't reach for it by default, though; more empty fields on the canvas is just more scrolling for no benefit if you only ever fill in three or four.
Inputs (22)
| Name | Type | Default | Description |
|---|---|---|---|
| images | IMAGE | — | |
| filename_prefix | STRING | ComfyUI | — |
| meta_name_0opt | STRING | — | |
| meta_value_0opt | * | — | |
| meta_name_1opt | STRING | — | |
| meta_value_1opt | * | — | |
| meta_name_2opt | STRING | — | |
| meta_value_2opt | * | — | |
| meta_name_3opt | STRING | — | |
| meta_value_3opt | * | — | |
| meta_name_4opt | STRING | — | |
| meta_value_4opt | * | — | |
| meta_name_5opt | STRING | — | |
| meta_value_5opt | * | — | |
| meta_name_6opt | STRING | — | |
| meta_value_6opt | * | — | |
| meta_name_7opt | STRING | — | |
| meta_value_7opt | * | — | |
| meta_name_8opt | STRING | — | |
| meta_value_8opt | * | — | |
| meta_name_9opt | STRING | — | |
| meta_value_9opt | * | — |
Outputs (0)
No outputs