Nodes/ComfyUI 1hewNodes/Multi Mask Math Ops
ComfyUI Node

Multi Mask Math Ops

Boolean masks in one node instead of five

By 1hew·Created about a year ago·Updated 2 days ago· 33
Multi Mask Math Ops
  • mask_1
  • mask_2
  • mask
operationor

What it's for

Combining masks is one of those jobs ComfyUI makes you do with a chain: one MaskComposite per operation, plus a preview node to see what you built. Combining the person detector and the hand detector so a detailer covers both. Subtracting a background mask from a subject. Keeping only the pixels where a color mask and a depth mask agree.

Multi Mask Math Ops is that chain collapsed into a single node with an operator dropdown: or, and, subtract (a-b), subtract (b-a), xor. It's not glamorous. It's the node you keep forgetting the name of and then use in nine workflows.

How it works

Wire mask_1 and, if you need it, mask_2; the pack's README describes the input side as dynamic mask_X, so more mask sockets appear as you connect them, and additional inputs fold into the same operation left to right. operation picks the math:

  • or → per-pixel max
  • and → per-pixel min
  • subtract (a-b)clamp(mask_1 - everything else)
  • subtract (b-a)clamp(mask_2 - everything else)
  • xor → chained absolute difference

Those are min/max on the 0–1 float values, not a binarize-then-AND. That's a real design choice with a real consequence: soft, feathered mask edges stay soft through the operation instead of snapping to 0/1, which is exactly what you want when the result goes straight into SetLatentNoiseMask.

Two behaviours you should know before you trust the output:

Sizes are aligned silently. Every mask that isn't the first one is resized to match the first mask's dimensions, via an 8-bit LANCZOS pass. So if your two masks came from different crops or different-sized sources, they get quietly rescaled to the first one's frame rather than erroring. Convenient - and a great way to feed yourself a mask that's subtly in the wrong place.

Batches cycle, they don't error. All inputs are broadcast to the longest batch by index (mask[idx % batch_size]), then processed per frame. subtract (b-a) is also the one asymmetric case: it starts from the second mask and subtracts the rest, so order matters. And xor across more than two masks is chained absolute difference, which is not a true parity XOR for odd counts.

Under the hood the per-frame work fans out across cores with asyncio.to_thread, so long batches don't run strictly one after another.

Inputs and outputs

mask_1 (MASK, required), mask_2 (MASK, optional), operation (COMBO, default or). One output: mask.

That's it, and that's the appeal. Wire the output into a mask-to-image node to eyeball it, or into an inpainting/detailer path. For a quick union-then-inpaint, or with two detector masks and a mask-to-image preview next door is a two-node answer to the question "why doesn't my ADetailer-equivalent cover the hands".

Install

ComfyUI Manager → search 1hewNodes, or:

cd ComfyUI/custom_nodes
git clone https://github.com/1hew/ComfyUI-1hewNodes

Restart. This node needs only torch, numpy and Pillow - the pack's scary dependencies (ultralytics, rembg, transparent-background, onnxruntime, av) are for its YOLO detection, background-removal and video nodes. If you're here for mask plumbing alone, they're not yours to install. The pack uses the current comfy_api.latest schema, so keep ComfyUI reasonably up to date.

Common issues

The masks look misaligned in the result. You connected a mask from a different crop or resolution and it got rescaled to the first input's frame. Align them upstream - the pack's Mask Crop by BBox Mask / Mask Paste by BBox Mask pair exists for exactly that - or make sure the first mask you connect is the one whose geometry you want to keep.

A "union" that isn't the whole thing. Because or is max, overlapping feathered edges blend rather than harden. If you specifically want a crisp union before feeding a binarizing step, that's fine; if you wanted the soft edges, also fine. Just don't expect or to produce a flat white shape.

Empty result after subtract. subtract (a-b) keeps only what's in mask_1 and not in the rest - swap to subtract (b-a) if you had the operands backwards, and note that everything after the second mask is also subtracted.

Everything returns one frame. The output batch length follows the longest input; if you gave a batch of 8 and a batch of 1, you get 8, with the single mask reused across all of them. That's usually what you want and occasionally not.

Category1hewNodes/multi

Inputs (3)

NameTypeDefaultDescription
mask_1MASK
operationCOMBOor5 options: or, and, subtract (a-b), subtract (b-a), xor
mask_2optMASK

Outputs (1)

NameTypeDescription
maskMASK