Nodes/NanoBanana FaceSwap/Load Image (Paint Mask)
ComfyUI Node

Load Image (Paint Mask)

Paint the mask right on the node, no popup editor needed

By IxMxAMAR·Created 4 months ago·Updated about a month ago· 3
Load Image (Paint Mask)
    • image
    • mask
    image
    mask_data
    brush_size20

    ComfyUI's stock way to paint a mask is a popup editor that opens over your canvas and feels like a detour every single time. LoadImagePaint does what its name promises: a LoadImage that lets you paint the mask directly on the image embedded in the node body. Pick the file, smear the brush where you want the edit, and both the image and the mask come out the other side ready to wire into an inpainting node.

    It exists because this pack's painted-edit and mask-inpaint nodes are the whole point of the workflow, and you don't want a popup in the way when you're iterating on a region. For the same reason it's a genuinely useful node beyond the pack - any mask-driven workflow (inpaint, ControlNet, regional conditioning) can take its MASK output.

    How it works

    The frontend side is a small companion JS file (web/load_image_paint.js) that ComfyUI serves automatically from the pack's WEB_DIRECTORY. It embeds a canvas in the node body, paints strokes at the image's native resolution (the canvas is just a scaled view), and serializes the painted strokes into a mask_data string. The backend decodes that, rebuilds the mask, and returns proper IMAGE and MASK tensors.

    Because the painted mask is stored in the mask_data widget, it travels with the workflow JSON. Save the workflow and the mask comes back - no separate mask file to keep in sync. That's a real quality-of-life win over stock LoadImage, whose masks live as files in input/.

    The inputs

    • image - a dropdown of images in your input/ folder, same as the stock LoadImage.
    • mask_data - the base64-encoded paint strokes. You usually never touch this; it's the wire format the canvas writes. But it's a STRING widget, which is why saved workflows stay portable.
    • brush_size (default 20) - brush radius in image pixels, not screen pixels. At high resolutions a 20px brush feels tiny; the tooltip saves you the guesswork.

    The outputs

    Two: image and mask. Wire the image into any node that takes an IMAGE and the mask into anything that takes a MASK - the pack's NanoBananaInpaintSwap and NanoBananaPaintedEdit are the obvious partners, but it works with anything.

    Install

    Part of the NanoBanana FaceSwap pack:

    cd ComfyUI/custom_nodes
    git clone https://github.com/IxMxAMAR/ComfyUI-NanoBanana-FaceSwap
    cd ComfyUI-NanoBanana-FaceSwap
    pip install -r requirements.txt
    

    Restart ComfyUI (or install via ComfyUI Manager under "NanoBanana FaceSwap"). The JS ships in the repo, so it works out of the box - no separate extension install.

    Troubleshooting

    Two things can bite you, and the pack has already defended against both in the source. On very old ComfyUI builds, the fallback filename resolution refuses ../ traversal, so a workflow pointing at a path outside input/ will error - update ComfyUI rather than fighting it. And mask_data decode is capped at 50MB of string and 25MP of decoded pixels, so a pathological workflow can't OOM your worker - but it also means an enormous hand-painted mask at 4K will silently fall back to an empty mask if it blows past the cap. Keep brush_size sane and your masks reasonable and you'll never hit either.

    One more: the embedded canvas is a view, not the full image. If the node body looks cramped, the painting still stores at native resolution - trust the output, not the preview.

    CategoryNanoBanana FaceSwap

    Inputs (3)

    NameTypeDefaultDescription
    imageCOMBO1 options: example.png
    mask_dataSTRING
    brush_sizeINT201–200Brush radius in image pixels.

    Outputs (2)

    NameTypeDescription
    imageIMAGE
    maskMASK