ARCPaint KSampler
The sampler that edits only what you masked and leaves the rest alone
- model
- positive
- negative
- latent_image
- latent
Every whole-frame editor - Qwen-Image-Edit, Kontext, Klein in its default mode - has the same dirty secret: it re-encodes the image and regenerates everything, so the pixels you didn't ask to change still drift a little with every edit. ARCPaint KSampler is the mask-based rebuttal to that. It runs an ordinary sampling pass through the region you masked, then restores the untouched context around it. Think of it as a stock KSampler wearing an inpainting mask, tuned for Flux 2 Klein and packaged under ARC/Paint.
It exists because of a very 2026 problem. Klein is great at editing, but it's not trained for masked inpainting - feed it a full frame and it changes all of it. This is one of DHan315's ARC-node family, built for the ARC CTRL runtime, and the README admits the team's prior user-tested LanPaint workflow is still the visual benchmark while ARCPaint gets tuned. The difference matters: LanPaint's training-free trick adds Langevin "thinking" iterations before each denoise step, which is powerful and slow. ARCPaint deliberately drops that for an independent implementation from plain ComfyUI and PyTorch primitives - the README is emphatic that it neither imports, wraps, nor redistributes LanPaint code - doing the whole job in a single sampling pass, one that's tuned to Klein's fast distilled profile.
How it works
The node reads the mask off your latent's noise_mask - it takes no mask input of its own. If there's no mask, or it's all zero, the node doesn't error; it silently falls back to a plain full-frame sample. With a real mask the pipeline is:
- Expand the authored mask by
context_expand(in latent pixels) so the model has room to harmonize across the boundary rather than pasting a hard-edged blob. - Sample once through that expanded mask - one plain sampling pass, no second refinement stage in v0.1.1.
- Feather and restore. Outside the authored mask, the original latent blends back in over a soft transition band sized by
edge_feather. Context is restored in latent space, so no separate compositing node afterward.
Output is a single LATENT that still carries the authored mask on its noise_mask, handy if you want to chain another pass. Wire it to a VAE Decode and you're done.
The inputs that actually matter
The first ten inputs are stock KSampler: model, positive/negative conditioning, latent_image, seed, steps, cfg, sampler_name, scheduler, denoise. Then come ARCPaint's own inputs:
context_expand(default 2) - how far the sampling region grows beyond your mask, in latent pixels. At an 8x image-to-latent scale that's roughly a 16-pixel image-space radius per unit. Crank it when you get seams; keep it modest or you're recomputing half the frame.edge_feather(default 2) - width of the transition band between generated and restored context. Too low and you'll see the boundary; too high and the blend band eats into your edit.paint_stepsandrefine_denoise- legacy no-ops. v0.1.1 removed the old secondary refinement pass; these only exist so old workflows load without rewiring.
The defaults tell you what it was tuned for: steps 4, cfg 1.1, euler_ancestral, normal scheduler, denoise 0.91 - that's the Klein distilled 4-step profile. The MODEL input is model-agnostic (Klein 4B and 9B share the same paint stage), but if you feed it an SDXL or SD 1.5 checkpoint, treat the defaults as suggestions only; a cfg of 1.1 will look awful on a model that expects 7.
Installing it
ComfyUI Manager is easiest - search ARCPaint and install. Or drop it in by hand:
cd ComfyUI/custom_nodes
git clone https://github.com/DHan315/ARCPaint
Then restart ComfyUI; the nodes show up under ARC/Paint. Good news: there are no extra dependencies. dependencies = [] in the pyproject, no model files to download, no torch/CUDA pinning - it's pure ComfyUI primitives. If you run ARC CTRL's own bundled ComfyUI, it manages and syncs this folder for you automatically.
Where people get burned
- Nothing edits - the whole image regenerates. Your latent almost certainly has no
noise_mask. Run your mask through aSetLatentNoiseMask(or equivalent) node between the VAE encode and this sampler. The node won't warn you; the silent full-frame fallback is a code path, not an error. - You expect a second pass to refine quality.
paint_stepsandrefine_denoiselook like refinement knobs and do exactly nothing in 0.1.1. If the result is undercooked, raisestepsordenoise, not those. - Edges look wrong. Your
context_expandandedge_featherare too small for the resolution, or you're feeding non-Klein defaults (that cfg 1.1 again).
One honest caveat: this pack is brand new (v0.1.1, September 2026) with essentially zero community usage to learn from. It's the ARC baseline, not a claim of parity with LanPaint or FLUX.1 Fill - if the result disappoints, compare against those before you blame the workflow.
Inputs (14)
| Name | Type | Default | Description |
|---|---|---|---|
| model | MODEL | — | |
| positive | CONDITIONING | — | |
| negative | CONDITIONING | — | |
| latent_image | LATENT | — | |
| seed | INT | 00–18446744073709550000 | — |
| steps | INT | 41–100 | — |
| cfg | FLOAT | 1.100–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.910–1 | — |
| paint_steps | INT | 00–8 | — |
| context_expand | INT | 20–12 | — |
| edge_feather | INT | 20–12 | — |
| refine_denoise | FLOAT | 0.000–1 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| latent | LATENT | — |