Save Image (Swwan)
The saver that tells you the absolute path it wrote
- image
- alpha
- background_color
- paths
ComfyUI's stock Save Image does exactly one thing and refuses to discuss it: PNG, into output/, with the graph embedded. It won't tell you the full path of what it wrote, it won't write a JPEG, and it has a famously awkward relationship with transparency. This is the pack's replacement, and it's the node I'd actually reach for if I were scripting around ComfyUI rather than clicking around it.
The reason is the output. paths comes back as the absolute file path for every image it wrote - a list, one entry per image in the batch. If you're driving ComfyUI from a script, a queue manager, or a pipeline that hands files to something else, that single detail is the difference between a clean handoff and scraping the output folder for the newest file.
How it works
It resolves the destination folder first: a relative output_path hangs under ComfyUI's output directory, an absolute one is used as given, and you can even feed a list of folders - one per image - as long as the count matches the batch. Then it hands the folder and filename_prefix to ComfyUI's own folder_paths.get_save_image_path, which is what does prefix templating (%date:…%, %Empty Latent Image.width% and friends) and subfolder handling, so behaviour there matches the rest of the ecosystem.
Then the alpha maths, which is where this node is genuinely more careful than the stock one. Your image may be RGB or RGBA; alpha (MASK) can be supplied separately. If the format can carry alpha - PNG, WebP, TIFF - and has_alpha is on and alpha_mode isn't flatten, the file keeps a real alpha channel. With input_mode = premultiplied it will un-premultiply the RGB before writing (that's what epsilon is for), or flatten it against background_color instead. With input_mode = straight and alpha dropped, it composites onto background_color. JPEG and BMP always flatten, because they have nowhere to put alpha.
Two more nice touches: it looks for an existing file and any same-name .txt/.json before choosing a name, then reserves the filename with O_CREAT|O_EXCL, so two parallel batches can't silently overwrite each other; and relative paths inside output come back as UI previews so you still get the thumbnail in the node.
The inputs worth setting
image, file_format (png, jpg, webp, tif, bmp), output_path, filename_prefix are the required ones - that's the whole "beginner" set. Then, as you outgrow the defaults: alpha + input_mode + alpha_mode if transparency is involved, quality (JPEG/WebP, default 88), png_compress_level (default 1 - fast and large; raise it if you're archiving a lot), webp_lossless, webp_method, number_prefix + number_digits to put the counter before the name instead of after, embed_metadata to control the workflow/prompt chunks, save_workflow_as_json to drop the graph next to the image, and caption to write a same-basename .txt.
Install
ComfyUI Manager → search Swwan, or:
cd /path/to/ComfyUI/custom_nodes
git clone https://github.com/aining2022/ComfyUI_Swwan.git
cd ComfyUI_Swwan
python -m pip install -r requirements.txt
numpy, Pillow, opencv-python, scipy, scikit-image - no model downloads, nothing compiled against CUDA. Windows portable users install into the embedded interpreter:
.\python_embeded\python.exe -m pip install -r .\ComfyUI\custom_nodes\ComfyUI_Swwan\requirements.txt
Where people get burned
paths is a list, not a string. It's declared as a list output, so it will not plug straight into a node that wants one STRING. Grab the first element or join it - this is the single most common surprise with the node, and it's why SwwanSaveImage is a plumbing choice as much as a saving one.
Format choice costs you the workflow. PNG keeps the workflow and prompt text chunks; JPEG doesn't have anywhere to put them and WebP only keeps a flat parameter block. Save PNGs as your master, export JPEG/WebP as derivatives. And if you turn off embed_metadata, nothing you share will load back as a graph - which is the point sometimes, and a nasty little accident other times.
Premultiplied without alpha is an error, not a guess. Set input_mode = premultiplied with no alpha and no RGBA image and the node raises rather than inventing one. That's correct behaviour; it just looks like a crash if you didn't know.
Empty caption, no file. The .txt is only written when caption is non-empty, so don't diagnose a missing sidecar as a permissions problem.
One pack, many ported nodes. The pack-level reports worth knowing: installing Swwan has been blamed for missing-node warnings and odd widget behaviour alongside other packs (Kija/KJNodes on one thread, a Z-Image workflow's style list on another). If something unrelated breaks at the same moment, suspect the install before the workflow.
Inputs (20)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| file_format | COMBO | png | 5 options: png, jpg, webp, tif, bmp |
| output_path | STRING | — | |
| filename_prefix | STRING | Swwan | — |
| alphaopt | MASK | — | |
| input_modeopt | COMBO | straight | 2 options: straight, premultiplied |
| alpha_modeopt | COMBO | auto | 3 options: auto, keep, flatten |
| has_alphaopt | BOOLEAN | true | — |
| background_coloropt | COLORCODE | #ffffff | — |
| qualityopt | INT | 881–100 | — |
| png_compress_levelopt | INT | 10–9 | — |
| optimizeopt | BOOLEAN | true | — |
| webp_losslessopt | BOOLEAN | false | — |
| webp_methodopt | INT | 20–6 | — |
| number_prefixopt | BOOLEAN | false | — |
| number_digitsopt | INT | 51–10 | — |
| embed_metadataopt | BOOLEAN | true | — |
| save_workflow_as_jsonopt | BOOLEAN | false | — |
| captionopt | STRING | — | |
| epsilonopt | FLOAT | 0.000.000001–0.1 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| paths | STRING | — |