PercentileToPixelValueTonemap
Your HDR preview is a white blob. This is the fix.
- images
- IMAGE
If you've run a DiffusionLight workflow and stared at a featureless white rectangle where your environment map should be, you've hit the exact problem this node exists to solve. An HDR image carries brightness values above 1.0 - that's the whole point of it, the sun in a light probe is legitimately 20× brighter than the sky. But your monitor, your browser preview, and PreviewImage all assume 0-to-1. Feed them an HDR and they just clamp everything to white.
PercentileToPixelValueTonemap squashes the HDR down into a range you can actually look at, and it does it intelligently - by anchoring on a percentile rather than a fixed maximum.
How it works
The code is short enough to read in one go, and it's worth it because this is the one place in the pack where the math is doing something you might not expect:
- Gamma decode. If
gammaisn't 1.0, it raises the image to1/gamma. The default 2.4 is the display-gamma assumption, converting the sRGB-ish output back toward linear light. Setgammato 1.0 to skip this entirely. - Find the anchor. It computes the
percentilevalue across all pixels of each image - default 90, so it finds the pixel brightness below which 90% of the image sits. This istorch.quantileover the flattened image, computed per image in the batch. - Normalize. Divide the whole image by that percentile value and multiply by
pixel_value(default 0.9). Net effect: the 90th percentile of your image lands at 0.9, everything brighter rolls off above it, everything darker sits proportionally below.
The result is a tonemapped IMAGE in roughly 0–1 that PreviewImage can display and that an SDR node downstream can process.
The inputs you'll actually touch
- percentile (default 90) - the anchor point. Raise it toward 100 to preserve more highlight detail at the cost of a darker overall image; lower it for a brighter, punchier preview.
- pixel_value (default 0.9) - what that percentile maps to. Combined with percentile it's your exposure knob.
- gamma (default 2.4) - set 1.0 to skip gamma correction if you're feeding linear data or want a straight normalized view.
Where it fits (and where it doesn't)
Honest note: this node is not in the stock diffusionlight-workflow.json. The shipped workflow previews the three exposure brackets as ordinary SDR images instead, and saves the HDR straight to disk. So don't go hunting for a missing connection - you add this node yourself when you want to look at the HDR you just built, or when you want a tonemapped version to feed into another SDR pipeline. It's a helper, and a genuinely useful one.
The one real gotcha is that normalization is per-image in the batch. If you feed it a batch of different exposures, each one gets its own percentile anchor, so relative brightness between frames isn't preserved. That's usually what you want for previews; just know it's not a global exposure operator.
Installing it
Standard for the pack: ComfyUI Manager → search "DiffusionLight-ComfyUI", or:
cd ComfyUI/custom_nodes
git clone https://github.com/pureexe/DiffusionLight-ComfyUI
Then restart. This node needs no extra models - it's pure torch math on the tensors already in your graph, so you can drop it into any existing workflow that produces an IMAGE.
It's not a glamorous node, but it's the one that turns an invisible result into something you can check with your own eyes before you commit it to a 3D render. That's worth a place in your graph.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| images | IMAGE | — | |
| percentile | FLOAT | 90.000–100 | — |
| pixel_value | FLOAT | 0.90000–1 | — |
| gamma | FLOAT | 2.40-1000–1000 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| IMAGE | IMAGE | — |