ComfyUI Node

MaskFlow

Stop repainting the same mask every single run

By 90-RED·Created a day ago·Updated a day ago· 0
MaskFlow
  • image
  • mask
  • image
  • mask
draw_masktrue
countdown_seconds60
invertfalse
blur_onfalse
blur5
noise_onfalse
noise_kindfilm grain
noise_amount50
noise_size1
noise_blur0
use_versionlatest
source_name
if_no_masksend blank

The mask editor has amnesia, and that's the actual problem

ComfyUI's built-in mask editor is a one-shot tool. Paint a mask on a LoadImage node, save, queue - then you want denoise 0.5 instead of 0.7, or a softer edge, or you come back tomorrow, and you repaint. Nothing hits disk, there's no history, and the editor opens on the source image: if your graph crops to 1024 before sampling, you were painting a picture nothing downstream ever saw.

MaskFlow is one node from a single-author pack (90-RED, MIT, still on 1.0.x) that fixes both: drop it wherever you like in the graph, and every capture is archived as a numbered file you can load back.

How it actually works

It doesn't ship a painting UI - it borrows ComfyUI's official MaskEditor via the clipspace route and adds bookkeeping. In draw mode the backend parks on a threading event (polled in 0.25s slices, so Cancel still interrupts) while the frontend opens the real editor. You paint, the editor uploads its composite, and the backend reads the mask out of the alpha channel - painted = transparent, mask = 1 − alpha, the convention LoadImage uses.

Captures land as 8-bit grayscale PNGs in output/MaskFlow/<source>/v00001.png - five-digit padded, numbered max+1 so a deleted file's number is never reused, sorted numerically so v10 follows v9. The folder comes from walking the executing prompt's graph up to your LoadImage node; source_name overrides it.

The decision worth caring about: the archive stores the raw drawing, before any shaping. Invert, feather and grain are applied at execution, every run - so you can retune the edge on a mask you drew last week without reopening the editor.

The inputs that matter

The handful you'll actually touch:

  • draw_mask - ON opens the editor and waits. OFF loads from the archive, picking the file named by use_version (latest or a specific vNNNNN). Draw once, then flip to OFF and iterate on the sampler forever.
  • blur_on / blur - the Edge Blur feather in px, default 5. Separable Gaussian with replicate padding, so a white plateau doesn't darken at the border. Classic inpainting advice feathers at 4–12px; the default sits in that band.
  • noise_on / noise_kind / noise_amount / noise_size / noise_blur - grain for the soft edge: film grain, shadow grain (weighted to the dark side of the transition), digital grain (blocky clumps), cloud fbm (fractal). The grain's weight is the mask's own soft band, so it fills the blurred edge and leaves flat areas alone. Seeded off the image hash, so identical settings reproduce the mask exactly.
  • countdown_seconds - 60 by default: at zero the panel clicks the editor's Save for you. 0 waits for you to close it.
  • source_name - empty for auto-derivation, or type a name to pin the archive folder.

Optional mask seeds the editor with a mask you already have; invert flips white/black. if_no_mask decides what a failed load does: send blank carries on with an empty mask, cancel interrupts - which is what you want in a batch.

Outputs are image (pass-through, so the node can sit anywhere before your sampler) and mask, which feeds your masked sampling - SetLatentNoiseMask, InpaintModelConditioning, a crop-and-stitch pair. In load mode the change signature is the newest archive's mtime, so an idle queue skips the node and everything behind it; draw mode always reruns.

Install

cd ComfyUI/custom_nodes
git clone https://github.com/90-RED/ComfyUI-MaskFlow
# restart ComfyUI

Or search "MaskFlow" in ComfyUI Manager. No models, no downloads, no network calls - it writes only inside output/MaskFlow/. One Python requirement, comfyui-frontend-package>=1.51.0; the README says it was built against ComfyUI 0.35.0 / frontend 1.52.7.

Where people get burned

Old ComfyUI and the node never shows up. It registers through comfy_entrypoint (the V3 node API) and deliberately has no NODE_CLASS_MAPPINGS, so a build without comfy_api.latest errors on import or lists nothing.

Frontend coupling is the one real fragility. The panel hides the native widgets and drives them by DOM, the countdown auto-save clicks the editor's Save by hunting its check icon, and the capture hooks window.fetch on the editor's clipspace-mask-*.png upload - all observed behaviour of a frontend the author doesn't control. If the panel misbehaves after an update, check the frontend build first.

Draw mode blocks the queue - by design; you're meant to be at the screen. Fire it headless and it waits out the timeout (at least 10 minutes), then continues without a mask.

Noise does nothing? blur is 0, so there's no soft band for the grain to live in.

A version you just saved isn't in the dropdown - the list rebuilds with node definitions. Press F5.

Your archive folder is called node_17. Auto-naming needs a LoadImage upstream; anything else falls back to the node id, and recreating that node orphans the masks. Pin source_name if it matters.

Huge images stall the editor. The official MaskEditor is the bottleneck - one corpus report measured 3200×5500 taking 20 seconds to load. Crop first and put MaskFlow after that, which is where it wants to be anyway.

Where it sits

One mask, one image, once? The built-in editor is fine and this is overkill. MaskFlow earns its place when you're iterating - same mask, ten denoise values - or when the region only exists after a crop. It's new, small and single-author with almost no community footprint yet, so treat it as early-adopter gear, and for faces-and-hands-at-volume stick with the detector-driven detailers instead of painting by hand.

CategoryMaskFlow

Inputs (15)

NameTypeDefaultDescription
imageIMAGE
draw_maskBOOLEANtrueON: open the mask editor. OFF: use the newest (or chosen) archived mask, no editor
countdown_secondsINT600–3600Editor auto-saves after this many seconds (0 = wait for manual close). Only in draw mode
invertBOOLEANfalseInvert the mask (white <-> black)
blur_onBOOLEANfalseFeather the mask edge. The switch at the front of the Edge Blur row on the node drives this
blurINT50–1000Mask edge feather radius in px (a Photoshop-style blur). 0 = hard edge (and then the noise has no soft band to sit on)
noise_onBOOLEANfalseApply the edge noise. The switch at the front of the noise row on the node drives this
noise_kindCOMBOfilm grainEdge-noise style (grayscale - this is a mask). Click the thumbnail on the node to switch style. The grain fills the soft (blurred) edge and leaves the flat areas alone
noise_amountINT500–100Noise strength / opacity in %
noise_sizeINT11–200Grain size in px (bigger = coarser blobs)
noise_blurINT00–200Blur applied to the noise itself, in px (softens the grain)
use_versionCOMBOlatestWhich archived mask version to use (load mode). 'latest' = newest
source_nameSTRINGArchive folder name. EMPTY = auto-derive from the connected LoadImage node. Type a name to override
if_no_maskCOMBOsend blank2 options: send blank, cancel
maskoptMASKOptional external mask to start from

Outputs (2)

NameTypeDescription
imageIMAGE
maskMASK