〽️ Image Ops Bloom
Give your highlights the glow a real lens would have
- image
- mask
- image
- mask
Diffusion models render a highlight as a flat patch of white. A camera doesn't. Point one at a streetlight or a window at golden hour and the brightest thing in frame bleeds into its neighbours - that's the lens and the sensor giving up, and it's a huge part of why photographs read as photographs. ImageOps Bloom is a two-line fix for that: threshold the bright stuff, blur it, add it back.
Reach for it whenever an otherwise good render looks dead at the bright end. Neon signs, sunsets, candles, wet asphalt reflecting a lamp, screen glare on a cheek. The KB's take on this whole layer is the right framing: post-processing.md argues the cheap deterministic pass beats a re-gen, and photorealism.md lists lens behaviour - including glow and halation - as one of the things models "rarely generate". So generate the composition, then add the optic.
How it works
The guts are exactly what you'd guess, and that's the point. In the pack's nodes/bloom.py:
bright = (rgb - threshold).clamp(min=0.0)
bright = _apply_blur(bright, radius, 0.0)
out_rgb = rgb + bright * intensity_t
Subtract the threshold, clamp negatives to zero (that's a bright-pass), Gaussian-blur the result, add it back scaled by intensity. The author's comment in the source explains the one interesting decision: the glow is added, not screened. A screen blend computes 1 - (1-a)(1-b), which falls apart once pixels exceed 1.0 - and on HDR/scene-linear sources they do. Additive bloom keeps those values correct instead of clamping them, which is why this node is safe to drop in front of a float pipeline.
The knobs that matter
threshold(default 0.8, 0 → 4): brightness above which pixels glow. This is the one you actually tune. 0.8 on a normal 0–1 sRGB image means only near-white pixels participate; drop it to 0.5–0.6 for a hazy, dreamy look.intensity(default 0.5, 0 → 4): how much glow gets added. At 0 the node short-circuits and returns the input untouched.radius(default 12, 0 → 128): the Gaussian blur size of the glow, in pixels. Small = tight halo around a bulb. Large = atmospheric haze across the frame.invert_maskandbypass- the usual pair. Bypass returns the input unchanged so you can A/B by toggling one boolean.
Inputs are image (an IMAGE/VIDEO batch) and an optional mask; outputs are image and mask. Wire the image output into your Preview or Save Image node, or straight into an upscaler. The mask output carries the effect mask through if you fed one, so downstream mask consumers stay in sync.
Installing it
The whole pack installs the same way:
cd ComfyUI/custom_nodes
git clone https://github.com/MajoorWaldi/ComfyUI-Majoor-ImageOps
Then restart ComfyUI and hard-refresh the browser (Ctrl+F5, or Cmd+Shift+R on macOS). That last step isn't optional here - this pack ships its own frontend JS for the queue-free live preview, and a stale cache is the number-one reason the preview looks broken. ComfyUI Manager works too: search the pack title, ComfyUI-Majoor-ImageOps. There are no model downloads and no extra pip packages beyond what ComfyUI already installs. The real requirement is ComfyUI 0.19.0 or newer - the pack is built on the V3 node schema (comfy_api.latest.io) and simply won't register on an older install.
Where people get burned
- "Nothing happens." Either
intensityis at 0, orthresholdis above your brightest pixel. If your image maxes out around 0.85 and threshold sits at 0.8, you're glowing a sliver of one highlight. Lower it and watch the histogram on an ImageOps Preview node. - Radius is in pixels, not percent. 12 is a gentle halo on a 1024px image and basically nothing on a 6K upscale. Scale it with resolution.
- Stacking bloom after bloom clips to white. The glow is additive, so three passes at intensity 1.0 will blow out every highlight. Use one node with a bigger radius.
- If you're on a weak GPU,
IMAGEOPS_COMPUTE_DEVICE=cpukeeps the node on the CPU (it's fast enough for a still), and the pack's live preview settings are under "ImageOps" in ComfyUI's settings panel.
Inputs (7)
| Name | Type | Default | Description |
|---|---|---|---|
| bypass | BOOLEAN | false | — |
| threshold | FLOAT | 0.800–4 | Brightness level above which pixels start to glow. |
| intensity | FLOAT | 0.500–4 | Strength of the added glow. |
| radius | INT | 120–128 | Gaussian blur radius of the glow, in pixels. |
| invert_mask | BOOLEAN | false | — |
| imageopt | IMAGE,VIDEO | Images/Video input. | |
| maskopt | MASK | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| mask | MASK | — |