JFA
The JFA Node's Distance Field Trick
- mask
- image
- dist_mask
- dist_image
- threshold_mask
- jfa_coords
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. Switchdistance_modetopixelsand set something concrete like 16 if you want predictable widths.distance_falloff-linear,smoothstep, orsmootherstep. The smooth ones taper the ends of the gradient, which usually blends nicer.invert_maskflips 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.
Inputs (9)
| Name | Type | Default | Description |
|---|---|---|---|
| source_channel | COMBO | Red | 4 options: Red, Green, Blue, Alpha |
| threshold | FLOAT | 0.500–1 | — |
| threshold_preview | BOOLEAN | false | — |
| distance_mode | COMBO | percentage | 2 options: percentage, pixels |
| distance | FLOAT | 0.050–2048 | — |
| distance_falloff | COMBO | linear | 3 options: linear, smoothstep, smootherstep |
| invert_mask | BOOLEAN | false | — |
| maskopt | MASK | — | |
| imageopt | IMAGE | — |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| dist_mask | MASK | — |
| dist_image | IMAGE | — |
| threshold_mask | IMAGE | — |
| jfa_coords | IMAGE | — |