Nodes/ComfyUI-SaveMem/SaveImagesMemory
ComfyUI Node

SaveImagesMemory

No, SaveMem doesn't save your VRAM — it hands your PNG to another process

By hnmr293·Created about a year ago·Updated about a year ago· 1
SaveImagesMemory
  • images
    name
    dummy_input0

    Let's get the naming out of the way, because it's the first thing you'll trip on: SaveMem has nothing to do with saving your VRAM. It's "save to shared memory." The "Mem" is a block of RAM that two Python processes on the same machine can both touch, and this node's entire job is to take your rendered image and drop a lossless PNG of it into that block so another program can grab it without ever touching disk.

    That sentence probably tells you whether this node is for you. It's an interop tool for people driving ComfyUI through its API - headless, workflow-as-JSON, output in RAM. The README is blunt about it: "This is intended for use via API." If you're clicking Generate in the UI and saving to the output folder, this does nothing for you. Keep scrolling.

    What it actually does

    The usual way to hand an image to code outside ComfyUI is to let it save a PNG to output/ and have your script watch the folder, or poll the /history endpoint. Both work; both are clunky. This node skips the filesystem: it attaches to a named multiprocessing.shared_memory.SharedMemory segment that your other process already created, serializes each image frame to a PNG, and writes it straight into that segment. Your consumer reads the bytes and is done - no file locking, no disk churn, no waiting for the next poll.

    The memory layout is small and documented:

    • 4 bytes: how many images you wrote (native endian)
    • 4n bytes: the byte length of each image
    • everything after: the concatenated PNG blobs

    Multiple images in one batch each become their own PNG - handy for a video frame pass or a batch of gens. The node also embeds the standard ComfyUI prompt and workflow text chunks into the PNG it writes, so a tool that already reads ComfyUI metadata will find the graph right there in the image.

    The inputs that matter

    Only three, and only two are real:

    • name (STRING) - the tag of the shared memory to write into. This has to match, character for character, what the receiving process created.
    • images (IMAGE) - wire this from any sampler or preview node.
    • dummy_input (INT, optional) - a rerun trigger. The node has no output, so ComfyUI may decide "already ran this, nothing changed" and skip re-writing. Nudge this number and it re-executes.

    There are no outputs. SaveImagesMemory is a terminal node - it's the end of a branch, not the middle.

    Installing it

    It's on the Comfy Registry, so the easy path is ComfyUI Manager → Install Custom Nodes → search ComfyUI-SaveMem, then restart. Or the manual way:

    cd ComfyUI/custom_nodes
    git clone https://github.com/hnmr293/comfyui-savemem
    

    Restart ComfyUI. That's it - no model downloads, no heavy deps. The pack uses only torch, numpy, and PIL, which ComfyUI already ships. Worth knowing who wrote it: hnmr293 is the developer behind the well-loved sd-webui-cutoff extension and ComfyUI-nodes-hnmr, not some random pack farm.

    Common issues

    • FileNotFoundError from the node itself. The shared memory block doesn't exist. The README says it plainly: create the destination segment on the receiver side before you run the workflow.
    • "Buffer size X is smaller than required Y". Your segment is too small. Size it for your biggest output - a 1024² PNG is a few MB, but a batch of them adds up fast.
    • Stale data on re-run. ComfyUI cached the node; bump dummy_input.
    • It's per-machine. Shared memory doesn't cross the network. If your reader lives on another box, this is the wrong tool - watch the output folder or use the API instead.

    A matching receiver looks like this:

    import struct
    from multiprocessing import shared_memory as sm
    
    shim = sm.SharedMemory(name="my_image")  # you created this earlier, with create=True
    n, = struct.unpack("=I", shim.buf[:4])
    lens = struct.unpack(f"={n}I", shim.buf[4:4 + 4*n])
    offset = 4 + 4*n
    for length in lens:
        png = bytes(shim.buf[offset:offset+length])  # raw PNG - save it, decode it, whatever
        offset += length
    shim.close()
    

    Useful? Only if your pipeline genuinely needs zero-disk handoff. If that's you, it's one of the few clean ways to get it.

    Categoryhnmr/image

    Inputs (3)

    NameTypeDefaultDescription
    nameSTRINGname of the shared memory
    imagesIMAGE
    dummy_inputoptINT0dummy input for rerunning

    Outputs (0)

    No outputs