Nodes/XENodes/SDR to HDR
ComfyUI Node

SDR to HDR

Turn your SDR generations into real HDR — no model, just math

By xeinherjer-dev·Created 6 months ago·Updated a day ago· 2
SDR to HDR
  • images
  • video
  • images
  • video
peak_nits400
itm_knee0.00
itm_exponent1.00
color_spaceHDR PQ

Everything ComfyUI hands you is SDR. Wan, LTX, Flux, your upscaled stills - it all lands in the same sRGB/BT.709 container that's been stuck around 100 nits for decades. If you've got an HDR monitor or TV, or you want to upload video to YouTube in HDR10, you're throwing away most of the luminance range before the file even exists. This node is the fix: it expands SDR into proper HDR - inverse tone mapping plus a real PQ or HLG encode - and it does it with zero model downloads. It's pure tensor math running on your GPU.

It's one node out of the xeinherjer-dev/ComfyUI-XENodes pack, and it's the front end of that pack's HDR story: expand here, then hand the result to the pack's own SaveVideo (or Save HDR Video) node to get a 10-bit file out. The node accepts both stills and whole videos, so it slots into a video workflow as the last color step before encoding.

How it works

Read the source and it's refreshingly unmagical - a four-step pipeline, chunked so it doesn't eat your VRAM on long clips:

  1. Decode sRGB to linear light (the standard IEC 61966-2-1 curve).
  2. Soft-knee inverse tone mapping on luma - it scales each pixel's RGB by the same ratio so hue stays put while brightness expands.
  3. Convert from BT.709 to BT.2020 primaries.
  4. Encode to PQ (SMPTE ST 2084, what HDR10 uses) or HLG with a proper OETF.

No neural network, no ComfyUI model files, no API. The "AI" part is whatever generated the SDR in the first place; this is pure post.

The inputs that matter

There are four knobs, and you'll mostly touch two of them.

  • images (IMAGE) and video (VIDEO) - you need at least one. Feed it a batch of frames or a whole video; if you give it both, the video's alpha, audio, and frame rate ride along. Give it neither and it raises an error.
  • peak_nits - the headline control. Default 400, range 100–10000. SDR white (100 nits) gets mapped to whatever you set here. 400 is a sensible default for typical PC panels; go 1000 if your target is an HDR TV or YouTube's HDR pipeline.
  • itm_knee - where expansion starts. At 0.0 (default) the whole tonal range stretches from black. At 0.8, midtones stay put and only highlights get the boost - the "gentle" mode that keeps skin tones and textures looking natural instead of glowing.
  • itm_exponent - curve shape. 1.0 is linear and punchy, 2.0 is quadratic and soft. Most content looks better between 1.0 and 2.0; you don't need to go hunting above that.
  • color_space - HDR(PQ) (default) for PC, mobile, web, YouTube; HDR(HLG) for 4K broadcast TV. Pick PQ unless you know your delivery target is a broadcast pipe.

Both outputs matter: images (the expanded tensor in [0,1]) and video (a 10-bit video stream that the pack's SaveVideo node detects as HDR and encodes with yuv420p10le). If you started from a video, take the video output; if from stills, take images.

Installing it

Standard custom-node fare. Via ComfyUI Manager, search "ComfyUI-XENodes" and hit install. Or:

cd ComfyUI/custom_nodes
git clone https://github.com/xeinherjer-dev/ComfyUI-XENodes.git xenodes

Restart ComfyUI and you're done - there are no model files to fetch. The conversion itself needs nothing but PyTorch. One heads-up for the full HDR pipeline: the pack's save nodes call ffmpeg (they bundle imageio-ffmpeg as a dependency, and the README also asks for a system ffmpeg in PATH), so make sure you've got one or the other before you try to actually write an HDR file. Note the pack is written against ComfyUI's newer V3 node API - that's why it stays compatible with the latest ComfyUI builds, and it's not a sign that something's broken if you don't see a legacy NODE_CLASS_MAPPINGS.

Where people get burned

The big one: PQ output looks wrong on an SDR monitor, by design. PQ encodes up to 10,000 nits, so a 400-nit image previewed on a normal panel looks dim and washed out. That's not a bug - check it on an HDR display or an HDR-aware player before you judge it.

Second: don't pipe the output through a generic SDR save node. You'll clip it back to 8-bit and undo the whole point. Use this pack's SaveVideo (it detects the HDR color space and picks a 10-bit pixel format) or Save HDR Video/Save HDR Image for the fancier paths.

And third, the taste one: peak_nits way above your display's actual capability doesn't make things "more HDR," it just crushes your highlights into a band you can't see. Start at 400, go up only when your delivery target asks for it.

Categoryxenodes/color

Inputs (6)

NameTypeDefaultDescription
peak_nitsFLOAT400100–10000Peak brightness in nits. SDR white (100 nits) will be mapped to this target luminance in HDR.
itm_kneeFLOAT0.000–1Inverse Tone Mapping (Soft-Knee) threshold. 0.0 starts expansion from black. 0.8 preserves SDR midtones and applies expansion to highlights.
itm_exponentFLOAT1.001–10Expansion curve exponent. 1.0 = Linear (punchy/bright), 2.0 = Quadratic (soft/natural), >2.0 = even softer transition.
color_spaceCOMBOHDR PQTarget HDR color space. 'HDR PQ' (HDR10) is recommended for PC, Mobile, Web, and YouTube. 'HDR' (HLG) is designed for 4K TV broadcast.
imagesoptIMAGEThe SDR images or video frames to expand.
videooptVIDEOOptional: The SDR video to expand.

Outputs (2)

NameTypeDescription
imagesIMAGEThe expanded HDR images/frames [0, 1].
videoVIDEOThe expanded HDR video (10-bit, compatible with SaveVideo).