ComfyUI Node

JFA

The JFA Node's Distance Field Trick

By moniewski·Created 4 months ago·Updated 4 months ago· 0
JFA
  • mask
  • image
  • dist_mask
  • dist_image
  • threshold_mask
  • jfa_coords
source_channelRed
threshold0.50
threshold_previewfalse
distance_modepercentage
distance0.05
distance_fallofflinear
invert_maskfalse

If you've ever inpainted with a hard 0/1 mask and gotten a seam, you already know the pain this node exists to fix. JFA ("Jump Flood Algorithm") takes any hard mask and turns it into a smooth distance gradient - a per-pixel map of how far you are from the mask edge. That gradient is exactly what Differential Diffusion-style per-pixel denoise wants, which is why the pack's README frames it in one line: gradient in, no seams out.

What it actually does

The Jump Flood Algorithm is a classic graphics trick for finding, for every pixel, its nearest "seed" pixel - without the naive O(pixels × seeds) comparison. It's the same machinery that powers GPU Voronoi diagrams. This node implements it in pure PyTorch: no shaders, no custom CUDA kernel, no helper binaries. Under the hood it thresholds your input into a binary mask, pads it to the next power-of-two square, then runs log2(res) passes with halving step sizes. Each pass has a pixel compare itself against 8 neighbors and keep the nearest seed's coordinates. When the flooding finishes, taking the square root of the distance to that nearest seed gives you an exact Euclidean distance field, normalized by your distance setting.

The result is a proper falloff you can dial in, rather than the boxy gradient you get from a cheap blur. For inpainting that's the difference between "edit bleeds into the unmasked area" and "denoise is high in the mask and gracefully fades to zero outside it."

The inputs that matter

You feed it one of two things:

  • mask (MASK) - the usual path. Any hard mask from a segmentation/rembg-style node goes straight in.
  • image (IMAGE) + source_channel - if you only have an image, it thresholds a single channel (Red/Green/Blue/Alpha) to build the mask. Gotcha: pick Alpha on an RGB image and the code silently falls back to Red. RGB images have no alpha - don't chase that ghost.

Then three controls do 90% of the work:

  • threshold (0.5 default) - the binarization cutoff. Pixels brighter than this become seeds.
  • distance + distance_mode - gradient width. Default is 0.05 in percentage mode, which means 5% of the power-of-two padded image. That's the sneaky part: at a non-power-of-two resolution, a "5%" feels different than you'd expect. Switch distance_mode to pixels and set something concrete like 16 if you want predictable widths.
  • distance_falloff - linear, smoothstep, or smootherstep. The smooth ones taper the ends of the gradient, which usually blends nicer. invert_mask flips the whole gradient when your downstream node wants denoise on the other side.

There's also threshold_preview, a fast path that skips the JFA entirely and just shows you the binarized mask. With a pack this undocumented, leave it on while you're dialing in threshold - it's your sanity check.

Outputs

Four, but you'll realistically use two:

  • dist_mask (MASK) - the gradient, wire this into a Differential Diffusion node as its mask/difference input.
  • dist_image (IMAGE) - the same gradient as a viewable RGB image, handy for eyeballing before you commit.
  • threshold_mask - the binarized mask as an image.
  • jfa_coords - debug visualization of the nearest-seed coordinates. Voronoi-celled psychedelia; ignore it unless you're nerding out on the algorithm.

Installing it

The lazy way: ComfyUI Manager → search "comfyui-JFA" → install → restart. The manual way:

cd ComfyUI/custom_nodes
git clone https://github.com/moniewski/comfyui-JFA

Then restart ComfyUI. That's the whole install - there's no requirements.txt, no model downloads, no API key. It runs on the torch ComfyUI already ships. In an ecosystem where half of setup time is untangling dependency conflicts, a zero-dependency node is a small delight.

Watch out for

The padded-grid thing is the one real trap: JFA runs on a power-of-two square, so percentage-mode distances are measured against that padded size, not your actual image. If you want reproducible widths, pixels mode is your friend. Beyond that, there's not much to break - the node is small, the failure modes are "no input connected" (it raises a clear error) and "my alpha channel did nothing" (see above). It's a niche utility, not a life-changer, but if you do masked inpainting with per-pixel denoise, it's the cleanest distance field you'll get without hand-rolling the math.

Categorycustom/jfa

Inputs (9)

NameTypeDefaultDescription
source_channelCOMBORed4 options: Red, Green, Blue, Alpha
thresholdFLOAT0.500–1
threshold_previewBOOLEANfalse
distance_modeCOMBOpercentage2 options: percentage, pixels
distanceFLOAT0.050–2048
distance_falloffCOMBOlinear3 options: linear, smoothstep, smootherstep
invert_maskBOOLEANfalse
maskoptMASK
imageoptIMAGE

Outputs (4)

NameTypeDescription
dist_maskMASK
dist_imageIMAGE
threshold_maskIMAGE
jfa_coordsIMAGE