Nodes/ComfyUI-CitronNodes/Citron Image Saver
ComfyUI Node

Citron Image Saver

The save node that makes CivitAI actually read your PNG

By citronlegacy·Created about a year ago·Updated 22 days ago· 0
Citron Image Saver
  • images
  • hashes
  • a1111_params
◄filename%time_%basemodelname_%seed►
◄path►
◄extension▾►
◄steps20►
◄cfg7.00►
◄modelname►
◄sampler_name►
◄scheduler_namenormal►
◄positiveunknown►
◄negativeunknown►
◄seed_value0►
◄width512►
◄height512►
◄lossless_webptrue►
◄quality_jpeg_or_webp100►
◄optimize_pngfalse►
◄counter0►
◄denoise1.00►
◄clip_skip0►
◄time_format%Y-%m-%d-%H%M%S►
◄save_workflow_as_jsonfalse►
◄embed_workflowtrue►
◄additional_hashes►
◄show_previewtrue►
◄custom►

ComfyUI's stock SaveImage already embeds your whole graph in the PNG - drag the file back onto the canvas and the workflow rebuilds. That part works. What it doesn't do is write the flat, A1111-flavoured settings block that CivitAI's uploader parses, which is why people upload a PNG and watch the "resources used" panel come up empty. Citron Image Saver exists to close that gap: one save node that writes both dialects - the ComfyUI workflow chunks and an A1111 parameters string with resource hashes.

It's a fork: the pack's own attribution points at giriss/comfy-image-saver, the node the "stop using the default Save Image node if you post to Civitai" crowd has recommended for years. Citron adds a workflow .json export, a custom field, chained hash passing between savers and an explicit show_preview. It also has zero Reddit footprint - not one thread mentions the pack - so treat it as a competent fork of a known-good node, not as something with a community behind it.

How it works

Under the hood it builds a metadata record, then serialises it two ways.

The record comes from whatever you wire into the node's optional inputs: model name, prompts, sampler and scheduler names, steps, CFG, seed, size, denoise, CLIP skip. From the prompt strings it regex-scrapes <lora:name:weight> and embedding:name, resolves each to a file under your loras/embeddings folders, and SHA-256s it down to the first 10 characters. Same for the checkpoint. Those short hashes are what CivitAI matches against its database, so this is the step that decides whether your upload gets auto-linked.

Two consequences worth knowing before you run it. First, the hash is cached in a .sha256 sidecar file written next to the model, so the very first save with a new checkpoint prints a progress bar and sits there hashing 6–12 GB. That's not a hang. Second, LoRAs are found from prompt text only - if your LoRA is applied by a loader node and the tag never appears in the positive string, it won't be hashed and won't show on the site.

Then it writes: PNG gets a parameters text chunk plus the workflow/prompt chunks; JPEG and WebP get the string stuffed into EXIF UserComment via piexif, the pack's one dependency.

The inputs that matter

Required: images (what you're saving), filename, path, extension (png, jpeg, jpg, webp).

filename is a template, and it's the most under-used part. The tooltip lists the variables: %date, %time, %model, %width, %height, %seed, %counter, %sampler_name, %steps, %cfg, %scheduler_name, %basemodelname, %denoise, %clip_skip. The default %time_%basemodelname_%seed gives you 2026-10-24-141530_myCheckpoint_123456789.png. path is a subdirectory under Comfy's output folder and takes the same variables; it gets created if missing.

Of the long optional list, the ones you actually wire: positive, negative, modelname, sampler_name, scheduler_name, steps, cfg, seed_value, width, height. modelname accepts a comma-separated list - first one becomes the primary model, the rest slide into the hash list. additional_hashes takes Name:HASH pairs (16-char max, 30 entries) for anything the scraper can't see. custom inserts a free-text fragment into the parameters string. save_workflow_as_json drops a sibling .json; embed_workflow and show_preview toggle the embedded graph and the UI thumbnail.

Outputs are hashes and a1111_params. hashes is a comma-separated Name:HASH string - feed it into another saver's additional_hashes when you save two variants in one graph. a1111_params is literally the text that got written, handy for eyeballing what CivitAI will see.

Install

cd ComfyUI/custom_nodes
git clone https://github.com/citronlegacy/ComfyUI-CitronNodes
# then restart ComfyUI

The README lists ComfyUI Manager support as "coming soon" and the repo still carries placeholder publisher metadata, so Manager search may come up empty - the clone is the reliable path. piexif is the only pip dependency:

# from your ComfyUI venv
pip install piexif

No model downloads. This is pure plumbing. Nodes land under Citron_ImageSaver.

Common issues

  • Every node in the pack vanished after install. piexif is imported at module load in the saver code, so a missing dependency takes the whole pack down with an import error, not just the saver. Check the console for the traceback and install the package.
  • JPEG metadata silently missing. EXIF is capped at 65535 bytes. The node prints an error and retries without the workflow; if it's still too big it prints a second error and skips metadata entirely. PNG is the format that always works.
  • The metadata is wrong, not missing. Nothing here reads your sampler. If you leave steps at its default 20 while the sampler ran 34, the file says 20. That's the whole reason the pack also ships selector and literal nodes plus a checkpoint loader that emits its own filename - wire them and the numbers stay honest.
  • "Model hash" is blank. The name didn't resolve to a file on disk - usually a typo, or a name that only exists in diffusion_models and doesn't match exactly.
CategoryCitron_ImageSaver

Inputs (26)

NameTypeDefaultDescription
imagesIMAGEimage(s) to save
filenameSTRING%time_%basemodelname_%seedfilename (available variables: %date, %time, %model, %width, %height, %seed, %counter, %sampler_name, %steps, %cfg, %scheduler_name, %basemodelname, %denoise, %clip_skip)
pathSTRINGpath to save the images (under Comfy's save directory)
extensionCOMBOfile extension/type to save image as
stepsoptINT201–10000number of steps
cfgoptFLOAT7.000–100CFG value
modelnameoptSTRINGmodel name (can be multiple, separated by commas)
sampler_nameoptSTRINGsampler name (as string)
scheduler_nameoptSTRINGnormalscheduler name (as string)
positiveoptSTRINGunknownpositive prompt
negativeoptSTRINGunknownnegative prompt
seed_valueoptINT00–18446744073709550000seed
widthoptINT5120–16384image width
heightoptINT5120–16384image height
lossless_webpoptBOOLEANtrueif True, saved WEBP files will be lossless
quality_jpeg_or_webpoptINT1001–100quality setting of JPEG/WEBP
optimize_pngoptBOOLEANfalseif True, saved PNG files will be optimized (can reduce file size but is slower)
counteroptINT00–18446744073709550000counter
denoiseoptFLOAT1.000–1denoise value
clip_skipoptINT0-24–24skip last CLIP layers (positive or negative value, 0 for no skip)
time_formatoptSTRING%Y-%m-%d-%H%M%Stimestamp format
save_workflow_as_jsonoptBOOLEANfalseif True, also saves the workflow as a separate JSON file
embed_workflowoptBOOLEANtrueif True, embeds the workflow in the saved image files. Stable for PNG, experimental for WEBP. JPEG experimental and only if metadata size is below 65535 bytes
additional_hashesoptSTRINGhashes separated by commas, optionally with names. 'Name:HASH' (e.g., 'MyLoRA:FF735FF83F98')
show_previewoptBOOLEANtrueif True, displays saved images in the UI preview
customoptSTRINGcustom string to add to the metadata, inserted into the a111 string between clip skip and model hash

Outputs (2)

NameTypeDescription
hashesSTRINGComma-separated list of the hashes to chain with other Image Saver additional_hashes
a1111_paramsSTRINGWritten parameters to the image metadata