Apply Denoise Mask Scheduler (use step)
Kill the inpainting seam by scheduling when your mask actually bites
- model
- MODEL
The one-sentence version
If your masked inpainting keeps coming back with a visible border where the fix meets the untouched image, this node attacks the cause directly: instead of clamping the non-mask area to the original pixels at every single step, it schedules when the mask is actually applied during sampling. Give up a little pixel-exactness, lose the seam. That's the whole trade, and it's exactly what the pack's README says - minor alterations to parts of the non-mask area are the point, not the bug.
It's an experimental pack from a Korean developer (the source comments are in Korean) with almost no community footprint - zero mentions in the usual ComfyUI subs as of this writing. So treat it as a tool you try, not a workflow staple. It also only has one "scheduler_type" implemented, skip, with the author promising more "as effective ones are identified." You've been warned.
How it works
You're used to the mask doing one thing. This node turns it into a three-phase schedule keyed to the step index:
- Before
start_step- the mask is skipped (set to all-ones). The whole frame goes through the denoise trajectory as one region, so structure, lighting, and prompt adherence settle in with no hard boundary to fight. - Between
start_stepandend_step- the mask bites, scaled bymask_alpha. The fixed region gets regenerated, the rest is held. - After
end_step- skipped again, so the final harmonizing steps aren't fighting a seam either.
Mechanically it's a thin layer over ComfyUI's own inpainting internals. The node stamps a denoise_mask_function into your model's model_options, then swaps in its own version of KSamplerX0Inpaint.__call__ (the sampler ComfyUI uses when a noise mask is present) so that function is actually consulted each step. It restores the original __call__ when sampling finishes, and modern ComfyUI already ships the denoise_mask_function hook natively - this pack just puts a schedule in front of it. Nothing exotic, which also means: on a plain txt2img run with no mask, this node is a silent no-op.
The inputs that matter
Of the five inputs, a beginner sets three:
start_step(default 0) - the first step where the mask is applied. Raise it to skip the chaotic high-noise early steps.end_step(default 20) - the last step where the mask is applied. Lower it to let the final detail steps run unconstrained.mask_alpha(default 1.0) - mask strength while active. At 1.0 the mask is applied at full strength; at 0.8, 20% of the original pixels bleed into the fix region, which softens edges further.
model is your checkpoint's MODEL, and scheduler_type currently only has one choice, skip - there's nothing to fiddle with there.
The one output is a MODEL, and it's the whole point: wire this MODEL into your KSampler's model input instead of the raw checkpoint. The mask still comes from your normal inpainting setup (SetLatentNoiseMask, or a mask loaded with your image) - this node never touches that.
Install
ComfyUI Manager: search for ComfyUI-denoise-mask-scheduler and install. Or from your ComfyUI directory:
cd custom_nodes
git clone https://github.com/MiddleKD/ComfyUI-denoise-mask-scheduler.git
Then restart ComfyUI. There's no requirements.txt and nothing to download - it's pure Python on top of the torch/torchvision ComfyUI already ships. The repo's example workflow references an SDXL inpainting ControlNet (inpainting_xl/diffusion_pytorch_model.fp16.safetensors) and a JuggernautXL checkpoint, but those are examples, not dependencies; standard masked inpainting works fine. The pack also registers a "Dynamic image resize" node - unrelated to this one, ignore it.
Common issues
- Nothing changes. You're almost certainly not actually inpainting - the node only does anything when a noise mask reaches the sampler. Check your mask wiring first.
- The window is wrong. Defaults (0–20) cover a whole 20-step run, so with defaults you get (a soft version of) plain inpainting - the scheduling is what you're buying. Pull
start_stepup to ~8 andend_stepdown to ~steps-minus-a-few and you'll feel the difference. Tune against a fixed seed, as with any inpainting pass. - Breaks after a ComfyUI update. It patches a core sampler class. If an update rewrites
KSamplerX0Inpaint, this pack can lag - keep the repo updated via Manager and restart if you see odd sampler errors. The patch self-restores after each run, so a restart also clears any stuck state.
Should you bother?
If seams are your enemy and you can tolerate the non-mask area shifting a little, it's a genuinely clever, zero-dependency fix worth ten minutes of testing. If you need the unmasked pixels bit-identical - the one thing masked inpainting uniquely guarantees these days - this isn't the node for you; that's what crop-and-stitch workflows are for. This is the opposite philosophy, on purpose. Try it on a face fix, see if the border bothers you less than the small changes it buys.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| model | MODEL | — | |
| start_step | INT | 00–100 | — |
| end_step | INT | 200–100 | — |
| mask_alpha | FLOAT | 1.00–1 | — |
| scheduler_type | COMBO | skip | 1 options: skip |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| MODEL | MODEL | — |