ꘇ top.bloom
Glow in four passes, no diffusion model involved
- image
- image
Bloom is the cheapest trick in post-processing and the one people keep re-downloading a whole node pack for: take the bright parts of an image, blur them, add them back, and suddenly a render looks like it was shot with a real lens. top.bloom does exactly that, as a TOP operator inside ꘇ Genereti (languel/genereti, by Liuboto, MIT) - the TouchDesigner-flavoured realtime layer for ComfyUI.
And it's worth naming why you'd care in a graph that generates images: this is not a second diffusion pass. A gamma curve is a gamma curve, a blur is a blur, and glow is a bright-pass blur. Doing this with img2img means burning a sampler run, gambling on a seed, and probably rewriting the face you liked. Doing it here is a deterministic, millisecond operation that does the same thing every time. That's the argument for the whole post-processing layer, and bloom is its poster child.
Mechanism
The implementation is a textbook bloom and the description doesn't oversell it. The node subtracts threshold from every pixel's colour, clamps the negative values to zero, and normalizes by 1 - threshold - that's the bright pass. Everything below the threshold becomes black.
Then it blurs that bright pass with a separable nine-tap Gaussian: five weights per direction, applied horizontally, then vertically, so you get a proper 2D Gaussian for ten taps instead of eighty-one. The taps are premultiplied before interpolation and un-premultiplied after, which is the reason glow around a transparent object doesn't pick up a grey fringe.
Finally it adds the blurred bright pass back over the source, scaled by strength, with alpha taken as the maximum of the original and the glow - so bloom around a transparent element expands the alpha rather than being clipped by it. The docs describe this as four GPU passes with reusable intermediate textures.
The three controls
threshold(0.6) - what counts as bright. Lower it and more of the image glows; too low and everything turns to fog.radius(12, range 0–128) - the blur radius in source pixels, not normalized. This is the honest kind of parameter: a 12-pixel glow is a 12-pixel glow regardless of image size, so you'll want a bigger radius for bigger images. It's also the thing that decides whether you get an LED sparkle or a soft-focus haze.strength(0.8, range 0–4) - how much of the glow lands on top. Going above 1 is legitimate here; 2+ gives a heavy, blown-out look that's genuinely useful for stylised visuals.
Output is a single image (IMAGE). It's chainable, so top.bloom → top.displace → top.feedback is a normal pattern - the pack's own examples use bloomed feedback to drive its own displacement.
Live versus Queue
Live, this runs in the browser on WebGPU, which is why a glow effect is interactive rather than a button you press and wait on. On Queue, the pack runs the same spatial stages in PyTorch on the input tensor's device. The two paths were tested against each other and the docs record the same result the rest of the texture utilities got: a maximum difference of one 8-bit colour level on synthetic comparison cases. That's a stronger guarantee than most packs offer, and it means a preview you tuned in Live will survive a render.
One boundary to keep in mind: live operators store straight rgba8unorm (8-bit), while Queue stays floating point. Near-neutral values can quantize slightly differently.
Install
Manager → search "Genereti" → install ꘇ Genereti → restart ComfyUI → refresh the browser page. Manual:
git clone https://github.com/languel/genereti.git custom_nodes/genereti
./.venv/bin/python -m pip install -r custom_nodes/genereti/requirements.txt
Pinned dependency is just comfyui-frontend-package>=1.53.6. ComfyUI 0.38.2+, Python 3.11+, and a browser with WebGPU for the live path - that's a hard requirement, not a nice-to-have, and it's why the pack's own testing notes say Chromium. Workflows ship under Workflows → Genereti.
Gotchas
No WebGPU, no live bloom. Without it you may still be able to queue (the PyTorch path is plain torch), but the interactive reason to use this node evaporates. Check chrome://gpu if you're unsure.
Batch shape has rules. Your optional second image (where applicable) or batch broadcasts: a single image B spreads across A's batch, but otherwise batch sizes must match or you get an error. Bloom itself is single-input, so this bites more in neighbouring operators - worth knowing while you're in the family.
It is not a sharpening or detail pass. Bloom makes images that look more photographic and less like a flat render, but it does not recover detail. If that's what you need, you want a model-based upscaler or a cleanup pass, not a glow.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| threshold | FLOAT | 0.600–1 | — |
| radius | FLOAT | 12.000–128 | — |
| strength | FLOAT | 0.800–4 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |