Advanced Paint Canvas
Paint a mask and get Nuke-style hardness, expansion and blur math for free
- reference_image
- painted_image
- processed_mask
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:
- Hardness -
mask_hardnessthresholds 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. - Expansion -
mask_expansionmorphologically dilates (positive) or erodes (negative) by N pixels. The "grow the mask a bit so the inpaint sees context" knob. - Blur -
mask_blur_radiusblurs the edge, andmask_blur_strengthblends 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.
Inputs (13)
| Name | Type | Default | Description |
|---|---|---|---|
| canvas_width | INT | 51264–4096 | Canvas width in pixels. |
| canvas_height | INT | 51264–4096 | Canvas height in pixels. |
| brush_type | COMBO | paint | paint: composite color strokes onto the image. mask_only: build the mask without coloring. |
| brush_color | STRING | #000000 | Hex color used by the paint brush (ignored in mask_only mode). |
| brush_opacity | FLOAT | 1.000–1 | Brush stroke opacity (0 = invisible, 1 = fully opaque). |
| brush_hardness | FLOAT | 0.800–1 | Brush profile hardness (0 = soft, 1 = hard edge). |
| brush_size | INT | 201–500 | Brush diameter in pixels. |
| mask_hardness | FLOAT | 0.500–1 | Threshold for the solid inner core: pixels brighter than (1 - hardness) clamp to 1.0. |
| mask_expansion | INT | 0-100–100 | Morphological dilate (positive) / erode (negative) in pixels. |
| mask_blur_radius | FLOAT | 0.00–100 | Gaussian blur radius applied to the mask edge in pixels. |
| mask_blur_strength | FLOAT | 1.000–1 | Blend factor between the hard mask (0) and the fully-blurred mask (1). |
| canvas_data | STRING | Internal base64 PNG payload from the JS canvas widget. Do not edit manually. | |
| reference_imageopt | IMAGE | Optional background image; painted strokes are composited over it when supplied. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| painted_image | IMAGE | Painted RGB image (composited over reference_image when supplied). |
| processed_mask | MASK | Processed mask after hardness, expansion, and blur stages. |