Nodes/ComfyUI-CustomNodePacks/Advanced Paint Canvas
ComfyUI Node

Advanced Paint Canvas

Paint a mask and get Nuke-style hardness, expansion and blur math for free

By Code2Collapse·Created 6 months ago·Updated a day ago· 52
Advanced Paint Canvas
  • reference_image
  • painted_image
  • processed_mask
canvas_width512
canvas_height512
brush_typepaint
brush_color#000000
brush_opacity1.00
brush_hardness0.80
brush_size20
mask_hardness0.50
mask_expansion0
mask_blur_radius0.0
mask_blur_strength1.00
canvas_data

An interactive paint canvas where your strokes don't just get rasterized - they get run through procedural mask math. Advanced Paint Canvas (MEC) is the first node in the pack's paint suite, and it's the one that replaces "paint a blob, hope the mask is usable" with "paint, then let hardness, expansion, and blur stages turn it into a proper matte."

The workflow it feeds: you paint where you want something fixed, and the mask that comes out the other end is already clean enough to drive an inpaint or a local diffusion pass. That's the whole point of the suite - paint → fix → refine → build.

How it works

The JS canvas widget writes your drawing into a hidden canvas_data string (a base64 PNG - don't edit it by hand, the tooltip is blunt about that). The Python node decodes it into RGBA, optionally composites it over a reference_image, and then runs the mask pipeline in order:

  1. Hardness - mask_hardness thresholds the stroke profile: pixels brighter than (1 - hardness) clamp to 1.0, giving the mask a solid core instead of a fuzzy brush smear. Lower hardness = softer core.
  2. Expansion - mask_expansion morphologically dilates (positive) or erodes (negative) by N pixels. The "grow the mask a bit so the inpaint sees context" knob.
  3. Blur - mask_blur_radius blurs the edge, and mask_blur_strength blends between the hard mask (0) and the fully-blurred one (1). This is your feather control.

Brush settings: brush_type (paint composites colored strokes, mask_only builds the mask without coloring - the cleaner mode for masking), brush_color, brush_opacity, brush_hardness, brush_size. Canvas size from canvas_width/canvas_height.

Outputs: painted_image (strokes composited over reference_image if supplied) and processed_mask - the mask after hardness/expansion/blur. The processed mask is the one to wire downstream.

Where it fits

In the pack's own suite it's the front door: paint → MECContextInpainter blends the inpainted result back with smart color/lightness logic → MECToneRefiner corrects the grade → MECBuilderSampler re-renders. But it stands alone fine - a paint canvas whose mask output is already feathered and expanded is useful for any inpaint, and mask_only mode makes it a proper mask editor rather than a paint tool.

Install

cd ComfyUI/custom_nodes
git clone https://github.com/Code2Collapse/ComfyUI-CustomNodePacks.git
pip install opencv-python>=4.7.0 scipy>=1.10.0

or ComfyUI Manager → search "CustomNodePacks", restart, confirm [MEC] Loaded .... It falls back to scipy/torch when cv2 is missing, but install cv2 anyway - the whole pack wants it. If processed_mask comes back empty, you likely painted with brush_opacity at 0 or forgot the canvas widget wrote anything - check the canvas_data field isn't empty after you draw.

CategoryC2C/Paint

Inputs (13)

NameTypeDefaultDescription
canvas_widthINT51264–4096Canvas width in pixels.
canvas_heightINT51264–4096Canvas height in pixels.
brush_typeCOMBOpaintpaint: composite color strokes onto the image. mask_only: build the mask without coloring.
brush_colorSTRING#000000Hex color used by the paint brush (ignored in mask_only mode).
brush_opacityFLOAT1.000–1Brush stroke opacity (0 = invisible, 1 = fully opaque).
brush_hardnessFLOAT0.800–1Brush profile hardness (0 = soft, 1 = hard edge).
brush_sizeINT201–500Brush diameter in pixels.
mask_hardnessFLOAT0.500–1Threshold for the solid inner core: pixels brighter than (1 - hardness) clamp to 1.0.
mask_expansionINT0-100–100Morphological dilate (positive) / erode (negative) in pixels.
mask_blur_radiusFLOAT0.00–100Gaussian blur radius applied to the mask edge in pixels.
mask_blur_strengthFLOAT1.000–1Blend factor between the hard mask (0) and the fully-blurred mask (1).
canvas_dataSTRINGInternal base64 PNG payload from the JS canvas widget. Do not edit manually.
reference_imageoptIMAGEOptional background image; painted strokes are composited over it when supplied.

Outputs (2)

NameTypeDescription
painted_imageIMAGEPainted RGB image (composited over reference_image when supplied).
processed_maskMASKProcessed mask after hardness, expansion, and blur stages.