Nodes/PortraitUtils/Simple Image Saver
ComfyUI Node

Simple Image Saver

Atomic PNGs, optional 16-bit, no half-written files

By heyburns·Created about a year ago·Updated 23 days ago· 3
Simple Image Saver
  • images
    ◄output_path►
    ◄filenameComfyUI►
    ◄suffix►
    ◄file_formatPNG►
    ◄jpeg_quality95►
    ◄include_metadatatrue►
    ◄unique_filenamestrue►
    ◄png_bit_depth8-bit►
    ◄jpeg_alpha_backgroundWhite►

    What it's for

    ComfyUI's stock SaveImage is fine until it isn't. It names files ComfyUI_00001_.png, dumps everything in output/, and has no concept of a 16-bit intermediate. This is the pack's replacement: a saver with a real filename, an explicit format, optional 16-bit PNG, and - the part I actually care about - atomic writes, so a crash or a cancel mid-encode doesn't leave a truncated PNG sitting in your output folder looking like a valid file.

    It's an output node. It has no graph output sockets at all, so nothing downstream can consume it; it exists to write files and supply ComfyUI's saved-image preview.

    How it works

    The core mechanics:

    • Atomic publication. The image is encoded to a temporary location and only moved into place once the file is complete. "Publish only complete files" is the engine's contract, and it's why you'll stop finding 0-byte or half-decoded PNGs after a failed run.
    • unique_filenames (on by default) appends a counter rather than overwriting an existing name. Turn it off only when you genuinely want a stable filename that gets replaced.
    • Batch handling. A batch of images gets an index in the filename; they don't all land on one name.
    • Missing directories are created. Point output_path somewhere that doesn't exist yet and it'll make it.

    Inputs worth setting

    Required: images, output_path, filename, suffix, file_format, jpeg_quality, include_metadata, unique_filenames. Plus two optional widgets.

    output_path takes an absolute path, or a relative one resolved under ComfyUI/output; blank keeps the default. filename is the base name without an extension, and suffix gets appended with a dash when you supply it - handy for keeping a finishing variant next to its source (photo- + final).

    file_format is PNG or JPG; jpeg_quality (default 95) is ignored for PNG. Two optional widgets matter for real photo work:

    • png_bit_depth - 8-bit (default) or 16-bit. The 16-bit option preserves more of the float image's precision, which is the correct choice for an intermediate you're going to keep editing, and a questionable one for a file you're about to upload anywhere. JPEG stays 8-bit.
    • jpeg_alpha_background - White or Black, used to composite RGBA before JPEG encoding. PNG keeps its alpha channel instead.

    Metadata, and the one thing to check

    include_metadata is on by default and writes the workflow prompt and extras into the file, so a saved PNG still drags back into ComfyUI as a loadable graph. Two caveats from the docs, both real. A JPEG's metadata goes in as a comment rather than EXIF, and overly large comments are skipped - so on a big workflow, a JPEG may quietly lose its metadata where a PNG wouldn't. And the server-side --disable-metadata flag overrides the widget entirely, which is why "this saver doesn't write metadata" is sometimes a global setting, not a node bug.

    If you upload to CivitAI and care about automatic model/LoRA linking, note that model auto-linking keys on an A1111-style flat parameters block, and the docs here describe workflow prompt/extras. PNG also isn't the format to publish if you're worried about leaking your prompt graph - that's the classic trade in this layer: the graph is the shareable project file, and it's also a full disclosure of your prompt, seed and checkpoints. Keep the PNG as your master, publish a derivative.

    Where people get burned

    Assuming this replaces SaveImage everywhere. It doesn't have to - it's an output node either way, so the cache still works backward from it and it runs every time. The difference is naming, format control and atomicity.

    Two practical gotchas: the node writes no filepath output, so don't try to wire a path to anything downstream; and if you set unique_filenames off, a re-run overwrites the previous file rather than erroring, which is exactly what you asked for and exactly as surprising as it sounds.

    Install

    ComfyUI Manager → search PortraitUtils (publisher heyburns, GPLv3), or:

    cd ComfyUI/custom_nodes
    git clone https://github.com/heyburns/PortraitUtils
    /path/to/ComfyUI/venv/bin/python -m pip install -r PortraitUtils/requirements.txt
    

    PNG16 I/O goes through OpenCV, so opencv-python-headless>=4.10.0.84 matters here - and install only one OpenCV wheel variant in ComfyUI's Python environment. Along with numpy, Pillow and tifffile, that's the whole dependency list. Restart the ComfyUI server after installing.

    CategoryPortraitUtils/IO

    Inputs (10)

    NameTypeDefaultDescription
    imagesIMAGE—
    output_pathSTRINGDirectory path (absolute or relative to ComfyUI/output).
    filenameSTRINGComfyUIBase filename without extension.
    suffixSTRINGOptional suffix appended with a dash when provided.
    file_formatCOMBOPNG2 options: PNG, JPG
    jpeg_qualityINT950–100JPEG quality (0-100).
    include_metadataBOOLEANtrueInclude workflow metadata (prompt + extras).
    unique_filenamesBOOLEANtrueAppend a counter to avoid overwriting existing files.
    png_bit_depthoptCOMBO8-bit16-bit PNG preserves more of the float image's precision. JPEG remains 8-bit.
    jpeg_alpha_backgroundoptCOMBOWhiteComposite RGBA on this background for JPEG; PNG retains alpha.

    Outputs (0)

    No outputs