Nodes/radiance/VAE Decode (HDR)
ComfyUI Node

VAE Decode (HDR)

Getting real scene-linear values out of a latent

By FXTD-Studios·Created 8 months ago·Updated about 18 hours ago· 246
VAE Decode (HDR)
  • samples
  • vae
  • alpha
  • crop_bbox
  • image
  • metadata
◄target_spacesRGB►
◄tile_sizeAuto►
◄overlap128►
◄temporal_sizeAuto►
◄temporal_overlap2►
◄exposure_adjust0.0►
◄hdr_modeClip (SDR)►
◄display_tonemapNone►
◄inverse_tonemapfalse►
◄target_stops12.0►
◄export_rhdrfalse►
◄rhdr_precisionf32►
◄source_spacesRGB►
◄hdr_outputfalse►
◄decode_noise_scale0.000►
◄hdr_scale_factor1.00►
◄decode_modeAuto (Recommended)►
◄hdr_peak_nits1000►

An ordinary VAE Decode returns pixels in [0, 1]. That's fine if you're making a PNG and useless if you're making an EXR: the whole top of your image's range has been amputated, and a grade can only work with what's left. VAE Decode (HDR) is the decode half of Radiance's HDR pair, and the reason it exists is that values above 1.0 can survive the round trip.

The important detail is how. There are two paths through this node and picking the wrong one is the main way people get confused.

Two paths, and Auto picks between them

Sampler-safe is what you get after a sampler has touched the latent. This is essentially a normal decode, delivering the target colour space you asked for - sRGB for display, or linear light in a chosen gamut, or a camera log target (ARRI LogC4 means AWG4 plus the LogC4 curve; Sony S-Log3 means S-Gamut3.Cine). Nothing magic, just colour-managed.

Direct HDR is what you get when the latent came straight out of VAE Encode (HDR): the encode stamped the latent with metadata and a fingerprint, and if that fingerprint still matches, this node inverts the HDR coding exactly. Values above 1.0 come back as values above 1.0. If the latent is some other latent - say a sampler has reshuffled it past recognition - Direct HDR instead decodes it normally and runs the pixel SDR → HDR model to reconstruct clipped highlights, limited to the hdr_peak_nits mastering peak you set (1000 nits sits at 4.93 on the output scale, where SDR white is 1.0).

decode_mode = Auto (Recommended) figures out which one applies by checking that fingerprint. Leave it there unless you have a reason.

The controls that matter

samples and vae are required, plus target_space (12 options, default sRGB). Then, in rough priority:

  • hdr_mode must match the encode setting. If you encoded with Compress (Log) and decode with Clip (SDR), you get whatever nonsense that implies. This is the number one mistake here.
  • display_tonemap (Reinhard / ACES Filmic / None) is now the sole tonemap control in this node, independent of everything else. None means no tonemap at all - scene-linear values far above 1.0 pass straight through, which in ComfyUI's preview is guaranteed white-out. That's not a bug, it's documented behaviour for people feeding an OCIO-aware viewer.
  • hdr_output skips the final [0, 1] clamp so the IMAGE tensor genuinely carries values above 1.0 and below 0.0. Turn it off when feeding SDR nodes.
  • exposure_adjust (stops, post-decode) and hdr_scale_factor (linear gain, Direct HDR only, RGB only).
  • decode_noise_scale - a real one, worth knowing. Compress (Log) expands values exponentially on the way back, which amplifies any residual VAE decoder artifact in flat highlights into visible grid patterns. This mixes a little Gaussian noise into the latent pre-decode to break that up. The tooltip publishes starting points per camera profile: 0.018 for LogC4 and S-Log3, 0.025 LogC3, up to 0.035 for Log3G10. It defaults to 0, so existing workflows don't shift.

For 4K work there are tile_size and overlap with Auto from VRAM, plus temporal_size/temporal_overlap for video - note the tooltip's warning that temporal_size counts latent frames, unlike ComfyUI's native tiled decode which counts pixel frames.

Outputs: image, and metadata, a JSON string naming the path that ran and whether the scale factor was applied. Read that string when something looks off; it answers the question you're about to ask on a forum.

Installing Radiance

Manager → search Radiance → install → restart → refresh. Manual:

cd ComfyUI/custom_nodes
git clone https://github.com/fxtd-studios/radiance.git
cd radiance
python -m pip install -r requirements.txt

Windows portable: use python_embeded\python.exe. Radiance is GPL-3.0, ~147 visible nodes, and installs OpenEXR, OpenImageIO, OpenColorIO, diffusers and accelerate. If you use the Direct HDR reconstruction path on an ordinary latent, the SDR → HDR weights live in ComfyUI/models/radiance/ and download on first use - set RADIANCE_ALLOW_DOWNLOADS=0 to forbid that. Those weights are non-commercial only.

Troubleshooting

Everything is white. Either display_tonemap is None with real HDR values on the wire (expected), or hdr_mode doesn't match the encoder. Check metadata before anything else.

Highlights look like a halftone grid. Increase decode_noise_scale toward the tooltip value for your profile.

I get SDR values above 1.0 when I expected HDR. hdr_output is off, or the latent is sampler-touched and you're in practice on the sampler-safe path. That's by design.

Heads up about the pack in general. When the suite was announced, the top comment on the r/StableDiffusion thread criticised specific nodes for collapsing precision to 8-bit while advertising a 32-bit pipeline. The criticism was fair at launch and the project has tightened up considerably since - 3.5.0 is candid about what's heuristic and what isn't - but on a shot that matters, verify the round trip rather than trusting the feature list.

CategoryFXTD STUDIOS/Radiance/Generate

Inputs (22)

NameTypeDefaultDescription
samplesLATENTLatent to decode: from a sampler, or straight from VAE Encode (HDR), whose embedded metadata lets Auto invert the HDR coding exactly.
vaeVAEVAE matching the model (and the VAE used by VAE Encode (HDR), if any).
target_spaceCOMBOsRGBOutput colour space. Sampler-safe decodes deliver it as chosen: sRGB is display-ready for Preview/Save; Linear, ACEScg, ACES 2065-1 and Rec.2020 Linear are linear light in that gamut; a camera log target applies its gamut and curve (ARRI LogC4 = AWG4 + LogC4, Sony S-Log3 = S-Gamut3.Cine). Direct HDR output is scene-linear: sRGB and Raw become Linear there.
tile_sizeoptCOMBOAutoTile size for decode. Auto queries VRAM.
overlapoptINT12832–256Overlap between tiles. 128px optimal for cosine blending.
temporal_sizeoptCOMBOAutoTemporal chunk size in LATENT frames (not pixel frames — unlike ComfyUI's native 'VAE Decode (Tiled)', which counts pixel frames and divides internally by the VAE's temporal compression). Auto sizes it from the same VRAM budget as tile_size, computed jointly since a larger spatial tile leaves less room per frame. A manual value forces chunking at that many latent frames regardless of tile_size. Images (4D latents) are not affected.
temporal_overlapoptINT20–32Overlap in latent frames between temporal chunks. 0 = hard cuts at chunk boundaries. A small value (1–2) blends them instead. Only active when the video actually needs temporal chunking (temporal_size='Auto' decides on its own, or set a manual value smaller than the clip's latent frame count).
exposure_adjustoptFLOAT0.0-10–10Post-decode exposure in stops.
alphaoptIMAGEAlpha channel from Radiance VAE 4K Encode.
hdr_modeoptCOMBOClip (SDR)Must match encode setting. 'Compress (Log)' (v2.3): true HDR — soft-shouldered, highlight-denoised, no hard clamp. Produces clean HDR output across the full log-curve dynamic range. 'Soft Clip': inverts tanh rolloff to recover highlights. 'Passthrough': extended sRGB range.
display_tonemapoptCOMBONonev2.3.8: display_tonemap is NOW THE SOLE TONEMAP CONTROL — independent of hdr_output. 'Reinhard' or 'ACES Filmic': tonemap ALWAYS fires for Compress(Log), even when hdr_output=True. Reinhard → smooth rolloff [0,∞)→[0,1), hue-preserving. ACES Filmic → filmic contrast, clips cleanly above ~10 lin. 'None': NO tonemap regardless of hdr_output. Scene-linear values far above 1.0 pass through — GUARANTEED OVEREXPOSURE in ComfyUI preview. Use only with an OCIO-aware downstream viewer. Ignored for all non-Compress(Log) hdr_modes.
inverse_tonemapoptBOOLEANfalseExpand SDR to HDR (recover highlights).
target_stopsoptFLOAT12.08–16Target dynamic range for inverse tonemap.
export_rhdroptBOOLEANfalseExport .rhdr sidecar for Radiance Viewer.
rhdr_precisionoptCOMBOf32RHDR export precision. 'f16' (fp16, default): smaller files, values capped at 65504 — adequate for most VFX material. 'f32' (fp32): full 32-bit range, ~2× file size — use for shots with extreme linear values (direct sun, fire VDBs).
source_spaceoptCOMBOsRGBKept for saved workflows. The log curve and camera gamut are read from the latent's HDR Encode metadata; this widget is not used.
hdr_outputoptBOOLEANfalse32-BIT HDR OUTPUT — when True, skips the final [0,1] clamp so the output IMAGE tensor carries full 32-bit scene-linear values (>1.0 for bright highlights, <0.0 for below-black). Required for Linear/ACEScg/Log target spaces in HDR VFX pipelines. Disable when feeding SDR nodes (preview, PNG export, etc.).
decode_noise_scaleoptFLOAT0.0000–0.1v2.4 — Decode-time noise injection. Mixes a small amount of Gaussian noise into the latent BEFORE VAE decode using lerp: noised = (1-scale)*latent + scale*noise. Breaks up coherent VAE decoder grid artifacts in flat highlight regions. Effect is most valuable for Compress (Log) mode where the log→linear decompression exponentially amplifies any residual decoder artifact in the highlight band. 0.0 = disabled (default — safe for existing workflows). Per-profile recommended starting points: ARRI LogC4: 0.018 Sony S-Log3: 0.018 ARRI LogC3: 0.025 (matches LTX default) Panasonic V-Log: 0.022 DaVinci Intermediate: 0.030 RED Log3G10: 0.035 Ignored for non-Compress(Log) hdr_modes.
hdr_scale_factoroptFLOAT1.000.1–10Linear gain on the Direct HDR output (RGB only, alpha untouched). Not applied to sampler-safe SDR decodes or to log targets; the metadata output says when it was skipped.
decode_modeoptCOMBOAuto (Recommended)Auto: Direct HDR when the latent comes straight from VAE Encode (HDR) (its fingerprint still matches), sampler-safe for anything a sampler touched. Sampler: standard SDR decode, never log inversion. Direct HDR: scene-linear output above 1.0, no display tonemap; a VAE Encode (HDR) latent is log-inverted exactly, any other latent is decoded and its clipped highlights are reconstructed by the pixel SDR -> HDR model. The metadata output names the path that ran.
hdr_peak_nitsoptFLOAT1000200–10000Direct HDR on an ordinary sampler latent: the mastering peak the reconstructed highlights are limited to. Output is linear with SDR white = 1.0 = 203 nits (BT.2408), so the peak sits at peak/203 (4.93 at 1000). Not used when the latent is log-encoded by HDR Encode.
crop_bboxoptBOUNDING_BOXOptional: connect RadianceResolution's crop_bbox output to crop off model-alignment padding (e.g. 1920x1088 -> 1920x1080) after decode.

Outputs (2)

NameTypeDescription
imageIMAGEDecoded image tensor.
metadataSTRINGJSON string with decode settings and applied hdr_scale_factor.