WebUI Inpaint
A1111's inpaint tab, rebuilt as one ComfyUI node
- model
- positive
- negative
- vae
- image
- mask
- refiner_model
- refiner_positive
- refiner_negative
- image
- crop
- mask
The thing ComfyUI keeps making you build by hand
Ask people who moved from Forge to ComfyUI what they miss and you get the same answer every time: the inpaint tab. Not the model quality - the interface. Mask, area dropdown, denoise slider, queue, look, adjust, queue again. In ComfyUI that loop is six nodes and a masking modal, and half the published workflows solve it by skipping the composite step entirely (which is how you turn a fine image to mush in four passes).
WebUI Inpaint is the whole tab as one node: it crops or resizes, encodes, samples, decodes, and composites back at your original resolution. Mask in one socket, image out the other. And because it composites, the pixels you didn't paint don't drift.
How it works
Under the hood it does four things in sequence, and knowing them explains most of its behaviour.
It prepares a canvas. Same logic as the separate Inpaint Prepare node: only_masked finds the mask bbox, adds padding, expands to your width/height aspect, crops, pads the leftover, and feathers with mask_grow and mask_blur. whole_image resizes the full frame by resize_mode instead. The canvas gets rounded up to the VAE's own compression ratio.
It fills and encodes. fill = original keeps your pixels. fill = fill paints the masked area with surrounding colour before encoding. latent_noise and latent_nothing go further: after encoding, the masked latent values are replaced with seeded noise or zeros, so the source detail inside your mask can't bleed into neighbouring latent pixels. That last distinction is why these two modes live here and not in the modular Prepare → sampler → Composite path - replacing latent values needs something that owns the latent.
It samples. A normal common_ksampler call with your sampler, scheduler, steps, CFG, seed and denoise. If refiner_model is connected and refiner_switch is below 1, the run is split: base model up to int(steps × refiner_switch), refiner for the rest, with noise disabled at the handoff so you don't re-roll the image.
It composites. Decode, then paste through the effective mask onto the untouched original, at source resolution. Zero-alpha pixels are copied, not blended.
Two edges worth knowing: an empty mask or denoise = 0 skips encode and sampling entirely and returns the original, and denoise = 1 fully denoises the masked region.
The inputs a beginner actually sets
Required sockets: model, positive, negative, vae, image, mask. That's it for wiring - the rest are widgets.
Three widgets do 90% of the work. area - only_masked for detail passes, whole_image when the change is compositional. width/height - the sampling canvas, not the output size; the "Use source size" button fills them from your source, rounded to a multiple of 8. denoise - 0.75 is a default, not a recommendation. For "fix this hand, keep the hand," 0.35–0.55. For "replace this object," 0.8–1.0. If you're getting seams, the answer is usually more padding and less denoise, not more.
batch_size is variants per input image, not the ComfyUI queue count - it repeats the canvas internally and gives you N candidates to flip through in Image Compare.
Outputs:
image- the full composite at source size. → Save Image, Image Compare.result.crop- the decoded sampling canvas. Useful for debugging what the model actually saw.mask- the sampling mask, same size ascrop.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/adachi0130/Adachi-Inpaint-Workbench.git
Full restart of ComfyUI, then Ctrl+F5 / Cmd+Shift+R in the browser. No model files ship with this and none are needed - bring your own checkpoint and VAE. requirements.txt is numpy, Pillow, torch; there's no extra pip step and no frontend build. Windows: powershell -ExecutionPolicy Bypass -File .\install.ps1 -ComfyUIPath "D:\ComfyUI_windows_portable\ComfyUI".
Worth knowing: when this node is connected to an Inline Mask, it also hosts the painting controls - the toolbar, brush settings, mask mode and fill content fold behind an arrow, while Undo / Redo / Invert / Fill / Clear stay visible.
Where it goes wrong
"Image sizes must match. Connect the final image output, not crop." You wired crop into Image Compare. crop is the sampling canvas and may be a different size than your composite.
My output isn't the size I set. Correct - width and height are the sampling canvas, and the composite always returns at source resolution. If you need a different final size, resize after, or take crop.
inpaint_model made things worse. That toggle switches the node to ComfyUI's native InpaintModelConditioning, which is for models actually trained with extra mask channels - SD 1.5 inpainting, FLUX.1 Fill. For a normal checkpoint it's off. The README calls this out specifically for Anima: leave it disabled for ordinary masked img2img.
The refiner errored out. refiner_model has to be a compatible second-stage model with its own conditioning; the check is picky about sampler/scheduler combination. Leave refiner_positive and refiner_negative disconnected and it defaults to your original conditioning, encoded with the refiner CLIP.
It's slow on a big source. only_masked with a canvas near the model's native size - 1024 for SDXL-class, 512 for SD 1.5 - beats sampling at 2048 every time, because you're spending the budget where the mask is.
Inputs (29)
| Name | Type | Default | Description |
|---|---|---|---|
| model | MODEL | — | |
| positive | CONDITIONING | — | |
| negative | CONDITIONING | — | |
| vae | VAE | — | |
| image | IMAGE | Original image; output is restored to this exact size. | |
| mask | MASK | White repaints; black preserves. | |
| area | COMBO | only_masked | Whole image or mask bounding-box inpainting. |
| width | INT | 102464–8192 | Sampling canvas width, independent of final image size. |
| height | INT | 102464–8192 | Sampling canvas height. |
| padding | INT | 320–2048 | Mask-only context padding in original pixels. |
| mask_blur | FLOAT | 4.00–128 | Gaussian mask feathering in original pixels. |
| mask_grow | INT | 0-128–128 | Positive dilates; negative erodes the mask. |
| mask_mode | COMBO | inside | Repaint inside or invert the mask. |
| fill | COMBO | original | Original, surrounding-color fill, latent noise, or zero latent. Latent modes require the all-in-one sampler. |
| resize_mode | COMBO | stretch | Whole-image resize mode; mask-only mode always retains the entire mask. |
| seed | INT | 00–18446744073709550000 | Seed for a reproducible batch of distinct samples. |
| steps | INT | 201–10000 | — |
| cfg | FLOAT | 7.00–100 | — |
| sampler_name | COMBO | 44 options: euler, euler_cfg_pp, euler_ancestral, euler_ancestral_cfg_pp, heun, heunpp2, +38 | |
| scheduler | COMBO | 9 options: simple, sgm_uniform, karras, exponential, ddim_uniform, beta, +3 | |
| denoise | FLOAT | 0.750–1 | Zero returns the original exactly; one fully denoises. |
| batch_size | INT | 11–16 | Number of generated variants per input image. |
| refiner_switch | FLOAT | 0.800–1 | Fraction of steps on the base model when a refiner is connected. |
| inpaint_modelopt | BOOLEAN | false | Enable only for dedicated inpainting models; uses ComfyUI's native InpaintModelConditioning. |
| soft_edgeopt | BOOLEAN | false | Extra feathering, not A1111 Soft Inpainting. |
| soft_strengthopt | FLOAT | 0.500–1 | — |
| refiner_modelopt | MODEL | Optional compatible refiner model, e.g. SDXL refiner. | |
| refiner_positiveopt | CONDITIONING | Encode using the refiner CLIP; defaults to original positive conditioning. | |
| refiner_negativeopt | CONDITIONING | Encode using the refiner CLIP; defaults to original negative conditioning. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| crop | IMAGE | — |
| mask | MASK | — |