Nodes/XENodes/Save HDR Image
ComfyUI Node

Save HDR Image

Real HDR AVIF from an SDR render, no AI models involved

By xeinherjer-dev·Created 6 months ago·Updated a day ago· 2
Save HDR Image
  • images
  • gainmap
filename_prefiximage/ComfyUI
formatavif
codec
peak_nits400
itm_knee0.00
itm_exponent1.00

Your HDR monitor can show brightness your SDR renders don't have - and most of the time ComfyUI never gives it the chance. Save HDR Image is the pack's attempt to change that: it takes a normal IMAGE in the standard 0–1 range, applies inverse tone mapping to expand it into HDR, and encodes the result as a true HDR AVIF with the PQ (SMPTE ST 2084) transfer curve. No AI upscaling or "HDR filter" models - it's pure math plus ffmpeg, which is both the appeal and the reason it's tucked under the experimental category.

The mechanism is worth understanding before you touch the knobs. The node first converts the sRGB image to linear light, then applies a soft-knee expansion: it computes the luminance, lifts everything above a threshold (itm_knee) with a power curve (itm_exponent), and scales the whole thing so that SDR white (100 nits) maps to your peak_nits target. The same expansion ratio is applied to all three channels so hue doesn't shift. Then ffmpeg encodes it as 10-bit AVIF with BT.2020 primaries and the PQ transfer, using zscale for the colorspace conversion. Along the way it produces a gainmap output - an image encoding the per-pixel expansion ratio - which is genuinely handy if you ever want to inspect where the node expanded brightness.

The inputs that matter:

  • images - the tensor to save.
  • peak_nits (100–10000, default 400) - the target peak brightness. 400 is the entry-level HDR/DisplayHDR-400 ballpark; a real HDR1000 panel wants 1000. This is the single most consequential control.
  • itm_knee (0–1, default 0) - where expansion kicks in. 0 expands from black; 0.8 preserves SDR midtones and only lifts highlights. For most content, a small nonzero knee looks far more natural than the punchy-from-zero default.
  • itm_exponent (1–10, default 1) - the expansion curve. 1 is linear (bright/punchy), 2 is quadratic (softer), higher is gentler still.
  • codec - av1 (CPU) or av1_nvenc (GPU), each with a CRF slider; lower CRF, higher quality. Default 2 for AVIF AV1 is already quite high quality.
  • filename_prefix - default image/ComfyUI, same template rules as the sibling save nodes.

The gainmap output is the only output, and it's an IMAGE you can preview or throw away.

Install:

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

or search "ComfyUI-XENodes" in ComfyUI Manager, then restart. This is the node where the README's FFmpeg requirement is load-bearing: it needs a real ffmpeg in PATH - the node raises "FFmpeg executable not found" if not - and ffmpeg must be built with AV1 support (libsvtav1), which stock builds almost always are. Verify with ffmpeg -version.

Now the honest assessment, because "experimental" is doing real work there. First, the metadata path wants exiftool to embed your workflow into the AVIF - if you don't have it, you get the file but no embedded graph (check exiftool -ver). Second, HDR output only looks right on an actual HDR display or player; on an SDR screen it'll look washed out or dim, which makes people think they broke something. Third, libsvtav1 encoding is slow - expect a real wait per image. If you're testing the tone-mapping math, render one frame, not a batch. And the pack-level gotcha: everything loads together, so if no XENodes nodes appear, check the console for a sibling import failure (SaveAudio needs PyAV's av).

Categoryxenodes/deprecated

Inputs (7)

NameTypeDefaultDescription
imagesIMAGEThe images to save.
filename_prefixSTRINGimage/ComfyUIThe prefix for the file to save.
formatCOMBOavifThe image format to save.
codecCOMBOThe codec to use for AVIF encoding.
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.

Outputs (1)

NameTypeDescription
gainmapIMAGE