Field Distance
Turn a Hard Mask Into a Gradient You Can Actually Breathe With
- field
- mask
Every hard mask wants to be a ramp
A binary mask is a terrible thing to blend with. Feather it two pixels and you get a visible band; feather it forty and you've lost the shape. What you actually want is a distance field - a grayscale image where each pixel's value is how far it is from the edge of the mask. Then a falloff isn't a blur setting, it's a number: take the field, run it through Field Threshold at whatever band width you want, and the ramp width is a real parameter with real units.
That's Field Distance. It takes any MASK, binarises it, and returns the exact Euclidean distance to the contour.
Why you'd want one
Distance fields earn their keep anywhere a mask needs to fade rather than cut: glow and bloom regions, gradual inpainting seams, region-based colour grading, depth-ish compositing where the influence should fall off with distance from a subject. They're also the standard trick for building an outline - threshold the field in a band around zero and you get a contour stroke of any width. And because the output is a normal 0–1 mask, it still composes with everything else in the pack: multiply it by noise with Field Combine and your falloff stops being an obvious circle.
How it works, and the one detail that trips people
It computes a true Euclidean distance transform, using SciPy's exact implementation (imported lazily, so its absence can't break the pack's import - and it's a ComfyUI core requirement anyway).
The deliberate detail: it measures to the contour, not to pixel centres. A naive distance transform reports the distance to the nearest opposite-class pixel centre, so the pixel just inside a boundary reads 1 and a signed field jumps from +1 to −1 with no zero anywhere. Field Distance applies a per-side half-pixel correction, so the straddling pixels land at exactly ±0.5 and the zero level sits on the contour.
The consequence is worth internalising: Field Distance and Field Morphology disagree by exactly half a pixel, by construction. One measures to the contour, the other counts pixels. Neither is wrong; they're answering different questions. Don't chase that discrepancy.
Normalisation differs per mode and it matters. outward and inward are non-negative and normalise as raw / max_distance. Only both is signed, and it uses s / (2 * max_distance) + 0.5. Giving a non-negative quantity a symmetric bound would squash it into the top half of the range - plausible-looking, silently wrong.
The inputs
field- the MASK to measure from.mode-outward,inwardorboth. Outward is the default and the one you want for falloff that spreads away from a subject.max_distance- the distance at which the output saturates, as a fraction of the longer frame edge, default 0.1. Like every length in this pack, it's frame-relative, not pixels: 0.1 means a tenth ofmax(width, height). That's what keeps a 512 preview and a 2048 render the same picture.threshold- binarises a soft input before measuring, default 0.5. You can't distance-transform a gradient, so this is where a feathered mask gets flattened.
Output is a single mask.
In both mode the contour lands at exactly 0.5, so Field Threshold at its default settings recovers your original mask - a neat way to confirm the node did what you thought. Empty and full masks are handled directly rather than handed to the transform, which would return unspecified values on an input with no background.
Install
Manager → ComfyUI Field, or:
cd ComfyUI/custom_nodes
git clone https://github.com/jeremieLouvaert/ComfyUI-Field
Restart ComfyUI. No downloads, no pip installs. Category is AKURATE/Fields/Reshape.
Getting burned
The threshold widget is the sleepiest footgun here. If you feed Field Distance a soft mask - say, the output of Field From Edges with coverage 0.4 - it doesn't measure distance from the soft edge; it snaps everything above 0.5 to solid and measures from there. If your distance field has a rim exactly where the soft input had one, threshold is the reason.
Second: in both mode, remember the sign convention. Inside the shape is above 0.5, outside is below. A chain that assumes "bright = near the contour" (true in outward) will behave differently in both.
Third, the boring one: a large max_distance on an image full of small shapes gives you a mostly-white frame with faint gradients. That's not the node failing; you asked for distances out to a big fraction of the frame and everything was close.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| field | MASK | — | |
| mode | COMBO | outward | 3 options: outward, inward, both |
| max_distance | FLOAT | 0.1000.001–1 | — |
| threshold | FLOAT | 0.500–1 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| mask | MASK | — |