ComfyUI Node

Inline Mask

Paint the mask on the node instead of in a popup you'll lose

By adachi0130·Created 4 days ago·Updated 2 days ago· 3
Inline Mask
    • image
    • mask
    • full_image
    ◄image►
    ◄mask_data►
    ◄source_data►
    ◄edit_modemask►
    ◄crop_state{}►
    ◄blend_data►
    ◄layer_image►

    The annoyance this fixes

    ComfyUI's built-in way to make an inpaint mask is: right-click a Load Image node, open the mask editor, paint in a modal, save, close, queue. Do it twice in a row and you notice the modal doesn't remember anything. People have been asking for a live-mask surface in the graph for years, and that's the gap this node fills: paint on the node, paint as you iterate, and keep the mask when the image underneath changes.

    What actually happens when you paint

    Here's the bit that surprises people. Inline Mask has no painter inside the backend - the painting is a browser canvas, and the node's three required inputs are STRING widgets that the frontend fills in for you:

    • image - the source path in ComfyUI/input/, written by the inline upload button.
    • mask_data - the painted mask, serialized as a PNG data URL. White repaints.
    • source_data - the picture embedded in the workflow, if you asked for that.

    At execution the backend decodes whichever of those it got, turns the source into an RGB tensor, and builds the mask from mask_data - or, if there is no mask data, from the alpha channel of the uploaded PNG.

    Because the mask lives in the browser and serializes with the workflow, it saves with your workflow. Reload and the strokes are still there, with no disk round-trip per stroke.

    IS_CHANGED hashes the three strings plus the mode/crop/blend state, and when the source isn't embedded it fingerprints the file by size and mtime instead of hashing a huge PNG on every near-keystroke re-execution - a 0.8.0 fix, and the reason it stopped feeling sluggish on big sources.

    Inputs, outputs, and the two modes

    Set edit_mode at the upper right of the node. mask is straightforward: paint, output image + mask, feed them to an inpaint sampler. crop is the Crop & Blend workspace - you place a dashed crop rectangle, generate into it, adopt the result back, and blend the edges with a second, independent mask. The optional crop_state, blend_data and layer_image widgets are that workspace's serialized state, not something you type into.

    The three outputs:

    • image - the current source, or the cropped region when a crop is locked. This goes to your inpaint node and to Image Compare.original.
    • mask - the repaint mask at source resolution. To your inpaint node's mask input.
    • full_image - the whole picture with any adopted layer stitched in. This is the one you save; image may only be the crop.

    Note what isn't there: no result input socket. Adoption happens only through Image Compare's Adopt this result button, and the pack's server-side prompt normalizer actively strips an obsolete result link if you load a workflow from an older version. Old tutorials with a feedback wire are out of date.

    The 0.8.0 change worth knowing about

    The README still walks through a ✓ merge button and a ◐ blend layers step in that order. The changelog for 0.8.0 removed the merge step: adoption now bakes the layer into the source immediately and drops you back into repaint-mask editing, and right-click (or Escape) releases the selected region and flattens any remaining layer in one action. So if you follow a video tutorial and can't find ✓, your install is newer than the tutorial. Hold right mouse to preview the original before committing; release to put the workspace back.

    Install

    cd ComfyUI/custom_nodes
    git clone https://github.com/adachi0130/Adachi-Inpaint-Workbench.git
    

    Then a full ComfyUI restart and a hard browser refresh (Ctrl+F5 / Cmd+Shift+R) - the frontend modules are what draw the canvas, so a plain refresh on a half-loaded cache will show you a node with no editor in it. There are no models to download and no extra Python packages: requirements.txt lists numpy, Pillow, torch, all shipped with ComfyUI. Windows users can run install.ps1 -ComfyUIPath "D:\ComfyUI_windows_portable\ComfyUI" instead, which backs up any previous install.

    Common issues

    The node is empty after loading a shared workflow. Source images are referenced by relative input/ path by default. Either ask the sender to enable Embed source in workflow, or drop the image at the same path. Masks travel inside the JSON; sources don't, unless embedded.

    "Failed to save workflow draft" on every edit. Embedding a large source can blow past browser draft storage. The pack handles the normal case by uploading an exact copy and switching to an input-file reference, but if draft storage is already wedged, save with Ctrl+S and reload with Ctrl+F5 to reset ComfyUI's disabled-draft state. Bigger sources: disable workflow persistence before embedding, or export the file - exports don't count against the quota.

    Your cut-out disappeared when you reopened a moved workflow. An unmerged crop layer is a relative reference under input/adachi-inpaint-layers/. Copy that file along, or flatten before you move. Unmerged layers are image files; masks aren't.

    Hard limits: 64 MiB per file, 32 megapixels, 16384 px on a side. And this is still-image inpainting - 4D latents and single-frame 5D latents, not multi-frame video.

    CategoryAdachi-Inpaint-Workbench

    Inputs (7)

    NameTypeDefaultDescription
    imageSTRINGInput image path populated by inline upload.
    mask_dataSTRINGEditor mask PNG data URL; white repaints.
    source_dataSTRINGEmbedded source image for portable workflows.
    edit_modeoptCOMBOmask2 options: mask, crop
    crop_stateoptSTRING{}—
    blend_dataoptSTRING—
    layer_imageoptSTRING—

    Outputs (3)

    NameTypeDescription
    imageIMAGE—
    maskMASK—
    full_imageIMAGE—