Nodes/Inline Image/Inline Image
ComfyUI Node

Inline Image

Paint an inpaint mask on the node itself — no Photoshop round trip

By DHan315·Created 3 days ago·Updated 2 days ago· 0
Inline Image
    • image
    • mask
    image
    mask_data

    Every manual inpainting workflow starts with the same chore: you need a mask, and ComfyUI has never made getting one comfortable. Core ComfyUI ships a mask editor, but it's a separate screen and the workflow stops while you're in it; the other classic route is painting in Krita or Photoshop, exporting a PNG, and loading it back in - which is exactly the kind of app-jumping that makes people say ComfyUI isn't "an easy interface for inpainting." Inline Image (class InlineMask, from the small DHan315/ComfyUI-InlineImage pack) is the "skip all that" option. It's a LoadImage that also shows a live preview and a brush canvas inside the node body, so you load an image, rotate or mirror it, paint a mask, and feed both downstream without your workflow ever leaving the graph.

    Look for it as Inline Image under the Inline category - the graph display name is "Inline Image," not "InlineMask." The mask always comes from a real source file, so everything downstream (blur it, feather it, feed it to a detailer) still applies.

    How it works

    Underneath it's two layers doing one job. The Python half is basically a LoadImage clone: the image widget is the same dropdown of files in your ComfyUI/input/ folder that core LoadImage uses, and the node resolves that file, EXIF-transposes it, and emits a normal IMAGE tensor. The frontend half is where the magic lives. A custom JS widget renders the image onto a canvas inside the node, lets you paint, and then encodes your painting as a base64 PNG data URI into a second, hidden widget called mask_data.

    Here's the part worth knowing before you get confused by an "empty" mask: when you haven't painted anything, mask_data is empty and the node returns a full-white (all-ones) mask. So an untouched Inline Image behaves exactly like a LoadImage - nothing downstream gets masked until you actually edit. The moment you brush, the frontend stuffs the data URI into the widget, IS_CHANGED fires on the image name plus a hash of the mask, and the graph re-runs with your real mask.

    Rotation and mirroring are also real, not just cosmetic. Clicking rotate or flip re-renders the canvas and uploads the result back to input/ as an inline_mask_transformed_*.png, then points the image widget at that new file - so the IMAGE output really is rotated and your painted mask stays aligned with it.

    The inputs and outputs that matter

    Two inputs, one you touch and one you shouldn't:

    • image - the file dropdown plus the standard paste/drag/upload affordance. Same pool as LoadImage.
    • mask_data - the hidden string that holds your painting. You don't type in it; the frontend writes it for you.

    Outputs: image (IMAGE) and mask (MASK). The obvious wiring is inpainting: send image through VAEEncode, feed mask to SetLatentNoiseMask (or the sampler's mask slot) on the latent, then run img2img at a sensible denoise - the classic rule of thumb is 0.3–0.6 for a fix, higher for a full regeneration of the area. Since the node stores the original mask too, you can also blur it before sampling if your edges look hard.

    Installing it

    Zero dependencies, zero model files - the pyproject.toml declares none, and the Python only uses numpy, torch, PIL, and core ComfyUI's folder_paths. Install via ComfyUI Manager (search Inline Image), or manually:

    cd ComfyUI/custom_nodes
    git clone https://github.com/DHan315/ComfyUI-InlineImage
    

    Then restart ComfyUI (or hit Refresh in Manager). That's the whole setup - nothing to download and no extra pip installs to fight.

    Common issues

    • "Missing node" when you load a workflow that uses it. Workflows store the node by graph name; remember this one displays as Inline Image, and Manager indexes it that way, so search for that rather than "InlineMask."
    • The mask seems to do nothing at first. That's by design - an unpainted node emits an all-white mask, which in an inpaint sampler means "denoise everything." If that's not what you want, paint: brush white over the region you want regenerated, black stays untouched.
    • Your input/ folder quietly grows. Every rotate/flip/paste uploads a fresh PNG into it. Fine in small doses; be aware if you're batch-processing.
    • Don't delete the web/ folder from the install. The whole editor lives there, and without it the node has no canvas - you'd be left with a plain loader.

    Honest caveat: this is a tiny, early-stage pack from an author with no community footprint yet, so don't expect a big wiki or a bug tracker full of answers. But the code is short, readable, and dependency-free - if you hit an edge case, the fix is usually greppable in one JS file.

    CategoryInline

    Inputs (2)

    NameTypeDefaultDescription
    imageCOMBO1 options: example.png
    mask_dataSTRING

    Outputs (2)

    NameTypeDescription
    imageIMAGE
    maskMASK