Save Image
Save Image — Filenames That Actually Tell You What Made the Picture
- images
The naming is the feature
Six months in, your output/ folder is 4,000 files called ComfyUI_00001_.webp and the only way to tell them apart is to open them. The metadata is in there, but you can't see metadata in a file browser.
This node builds folder and file names out of values pulled from your own graph - sampler_name, cfg, steps, the checkpoint, the resolution, the timestamp - so the filename is a summary of how the image was made. Wire it where your SaveImage goes and nothing else in the workflow changes.
How the naming works
filename_prefix is the fixed start. filename_keys is a comma-separated list appended in order, and each key is one of:
- Any widget name from any node -
sampler_name,cfg,steps,scheduler,denoise,ckpt_name,vae_name. When several nodes have the same widget, the highest-numbered node wins. 13.cfg- that widget on node 13 specifically, when "highest-numbered wins" picks the wrong one.resolution- the first image'sWxH.%F %H-%M-%S- anystrftimeformat; the default keys end on a timestamp.'text'- a fixed literal, quotes kept./sub,./sub,../sub- step into a subfolder before the next key. A bare/is a separator.
foldername_prefix and foldername_keys do the same thing for the subfolder under output/, and delimiter is the one character (default -) between parts. named_keys changes the format from 0001-seed-123456-cfg-5.0 to seed=123456-cfg=5.0, which reads better and parses worse. Model names lose their .safetensors extension, floats are trimmed, and Windows-hostile characters get dropped.
The counter behaves the way you'd hope: it continues from the highest number already in that folder, counter_digits sets its width, and counter_position puts it before or after the name.
The inputs that matter, and what comes out
Beyond images (the only required input), there are two optional text sockets: positive_text_opt and negative_text_opt. They don't affect the image or the filename - they're saved into the job data and nothing else.
There are no outputs at all. It's an output node, like the stock SaveImage, so it always runs and the cache works backward from it.
output_ext defaults to .webp and covers .png, .jpg, .jpeg, .tiff, .bmp, .gif, .jp2/.j2k, plus .avif and .jxl when their Pillow plugins are installed. quality is that encoder's quality, and it has a wrinkle: 100 is lossless for WebP/AVIF/JXL, but for PNG it maps to compression level 0–9 - so on PNG it's a file-size dial, not a fidelity dial.
save_metadata (on by default) embeds the prompt and workflow - PNG text chunks for PNG, EXIF Make and ImageDescription for everything else except BMP, which has neither.
save_job_data appends an entry per run to a jobs.json in the folder, or one <image>.json per image with job_data_per_image on. The five levels add prompt texts, basic data, sampler settings and loaded models; job_custom_text is free text you can shove in.
image_preview only decides whether the files appear in the queue/history gallery. Nothing is ever drawn under the node, so the node keeps whatever size you gave it - a small mercy when you have four of them stacked.
Install
Manager: search ComfyUI-BCNodes, install, restart. Manual:
cd ComfyUI/custom_nodes
git clone https://github.com/0xBeycan/ComfyUI-BCNodes
The pack's requirements.txt (postfx, caption-audit) is only for the PostFx and Caption Audit nodes; this node needs nothing extra. Find it by typing BCNodes in the node library.
Where people get burned
The default extension is WebP, and WebP is not a master. WebP is smaller and it can carry a flat parameter string, but it is not the only format that round-trips back into ComfyUI - the README says ComfyUI loads PNG and WebP back into the editor, which is better than JPEG manages. Still, if this folder is your archive, set output_ext to .png. Keep the lossless file, publish the small one; the same rule the whole ecosystem runs on.
If you want CivitAI to auto-link your models on upload, this node doesn't do that job either. It embeds ComfyUI's prompt and workflow; it does not write the flat A1111-style parameters block that CivitAI's auto-detection keys on. Same caveat the stock node carries. Nothing wrong with the node - just don't expect the swap to fix model linking.
counter_digits: 0 overwrites. That setting writes a fixed name with no counter, so the next run silently replaces the previous file. Convenient for a "latest" file, a data-loss machine everywhere else.
one_counter_per_folder does nothing. The tooltip says "Unused" and the README agrees - it survives only to keep the widget order stable across versions. Leave it alone.
One honest limitation: filename_keys resolves against widget values, so a value that only exists at runtime (something computed mid-graph) isn't available to it. Widgets are.
Inputs (19)
| Name | Type | Default | Description |
|---|---|---|---|
| images | IMAGE | — | |
| filename_prefix | STRING | ComfyUI | Fixed string the file name starts with |
| filename_keys | STRING | sampler_name, cfg, steps, %F %H-%M-%S | Comma separated keys appended to the file name, in this order. Any widget name of any node works (`sampler_name, scheduler, cfg, denoise`, `ckpt_name`, `vae_name`, `model_name`); `13.cfg` picks node 13; `resolution` is WxH; `%F %H-%M-%S` is a strftime format; `'text'` is a fixed string; `/sub` starts a subfolder. |
| foldername_prefix | STRING | Fixed string the subfolder name starts with | |
| foldername_keys | STRING | ckpt_name | Same rules as filename_keys; `/` or `../` make nested subfolders |
| delimiter | STRING | - | One character placed between the parts; `/` makes subfolders |
| save_job_data | COMBO | disabled | Append an entry per job to jobs.json in the subfolder: prompt texts, basic data, sampler settings, loaded models |
| job_data_per_image | BOOLEAN | false | One <image>.json per image instead of jobs.json |
| job_custom_text | STRING | Free text saved with the job data | |
| save_metadata | BOOLEAN | true | Embed prompt and workflow in the image (PNG text chunks, EXIF for the other formats) |
| counter_digits | INT | 40–8 | Digits of the image counter: 3 gives image_001.png. Continues from the highest counter in the subfolder; 0 disables it |
| counter_position | COMBO | last | image_001.png or 001_image.png |
| one_counter_per_folder | BOOLEAN | true | Unused |
| image_preview | BOOLEAN | true | List the saved images in the queue / history gallery. They are never drawn under the node |
| output_ext | COMBO | .webp | File format; AVIF and JXL appear when their Pillow plugins are installed |
| quality | INT | 900–100 | Encoder quality for JPEG / JXL / WebP / AVIF / JPEG 2000 (100 = lossless for WebP / AVIF / JXL); PNG maps it to compression level 0-9 |
| named_keys | BOOLEAN | false | Prefix each value with its key: prefix-seed=123456-cfg=5.0-0001.webp |
| positive_text_optopt | STRING | Saved as positive_prompt in the job data | |
| negative_text_optopt | STRING | Saved as negative_prompt in the job data |
Outputs (0)
No outputs