DiffHDR HDRI (Panorama)
Dig the sun back out of a white sky
- model
- vae
- image
- clip
- mask
- hdr
- mask
An equirectangular LDR panorama you shot on a phone, or generated, or grabbed off a stock site, has one fatal problem for 3D work: the sky is a flat white band. Drop it in as an environment light and everything gets lit by a glowing ceiling. DiffHDR HDRI (Panorama) reconstructs the actual sun, the actual sky gradient and the actual reflection off the water, and gives you back an environment map with real radiance above 1.0.
It's the same idea as the pack's all-in-one image/video node - generative radiance inpainting on ComfyUI's native Wan2.1-VACE-14B - but with the separate panorama LoRA (DiffHDR_Pano.safetensors) trained for equirectangular input, and a mask detector tuned for panoramas instead of for temporally-stabilised video.
Why this one is worth a dedicated node
HDRI reconstruction is the one place where the whole point is the highlight. In a video you mostly care that the clipped window region now has plausible content. In a panorama the single brightest pixel matters, because that's what lights your 3D scene and drives your reflections. The README's own demo is a clipped equirect panorama where pulling the result four stops down separates a sun and its reflection out of what was a flat white sky - that's not a thing any curve can do.
It's also the cheap mode. A 2048×1024 panorama is a single latent frame, so it samples in about 1.5–1.7 s per step where a 720p 33-frame video clip runs 8–10 s per step. Ten steps lands around 16–25 s of node time warm. If you just want to see whether this pack is for you, this is the node to try first.
Inputs and outputs
image- sRGB equirectangular, 2:1. Only the first frame of a batch is used, so don't feed it a sequence and hope.width/height- default 2048×1024, both floored to multiples of 16. Note the tooltip: the panorama is stretched, not cropped. Feed it a 2:1 map or you'll distort your horizon.preset/steps/seed- same presets as the rest of the pack.fast(res_multistep/simple/ shift 8) is the default and the one to keep;originalis the reference implementation'seuler/simple/ shift 5 and needs more steps for the same place.sampler/scheduler/shiftare only read whenpresetiscustom.mask(optional) - overrides the automatic over-exposure detection; white means regenerate. Most people never touch it.prompt- defaults to the panorama prompt the model was trained with ("Restore the full dynamic range of this clipped HDRI panorama"). It's ignored unless you connect aclip, and you shouldn't need to: the trained embedding ships with the pack, so the umT5-xxl download buys you very little here.attention/vae_precision- leave them atautoandfp32.fp32is recommended by the author precisely becauseas_loadedcan band highlights, and banding is the failure mode you'd notice most on a big smooth sky gradient.
Outputs are hdr - float, linear, Rec.709 primaries, values above 1.0 - and mask (the regions that were regenerated, useful if you want to comp the original LDR back in elsewhere).
From here: hdr → DiffHDR Save EXR at bit_depth = float and colorspace = acescg if a compositor or Unreal/Blender is downstream, or → DiffHDR Tonemap Preview with a negative exposure if you just want to check that the sun came back. Never route hdr into an 8-bit save or a preview node directly.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/claussteinmassl/ComfyUI-DiffHDR.git
pip install -r ComfyUI-DiffHDR/requirements.txt # OpenEXR + huggingface_hub
Then wan2.1_vace_14B_fp16.safetensors into models/diffusion_models and wan_2.1_vae.safetensors into models/vae (both from Comfy-Org/Wan_2.1_ComfyUI_repackaged), restart, and load workflows/diffhdr_hdri.json from the repo. The Pano LoRA downloads itself on first run into models/loras/DiffHDR/. ComfyUI Manager users can just search the pack title.
Gotchas
- A non-2:1 input is a stretch, not a crop. A 16:9 photo fed in here will come out with a squashed horizon and a weirdly tall sun. Crop to 2:1 first.
- VRAM still isn't small. Measured peak on the panorama path was 36.8 GB on one host and 48.7 GB on another - much lighter than the video path, but not a 12 GB job. GGUF via ComfyUI-GGUF is the lever.
- The mask detector behaves differently from the video one. It's a single-image detector with no temporal stabilisation, because there's nothing to stabilise. If your panorama has a wide, gently-graded bright sky rather than a hard clipped sun, check the
maskoutput before assuming the reconstruction is wrong - the node may simply not have found much to regenerate. - Nodes missing from the menu after install almost always means your ComfyUI is older than the
comfy_apiV3 node interface this pack registers through. It imports cleanly and registers nothing. Update ComfyUI. - Panorama mode is the one case where the README is explicit that the seed lottery is worse, not better: two 50-step runs of the same panorama with different seeds sit 25.3 dB apart, and one seed in three invented a small sun core that lifted the highlight level by 449 %. If a run looks odd in the brightest few pixels, change the seed before you change anything else.
Inputs (16)
| Name | Type | Default | Description |
|---|---|---|---|
| preset | COMBO | fast | fast = res_multistep / simple / shift 8 (recommended, measured on Wan2.1-VACE-14B: matches the 50-step reference at 10-20 steps, 4-7x faster). original = euler / simple / shift 5 (the reference implementation's sampler). custom = use the sampler / scheduler / shift widgets below. |
| model | MODEL | Wan2.1-VACE-14B diffusion model (bf16, fp8 or GGUF). The DiffHDR LoRA is downloaded and applied automatically. | |
| vae | VAE | Wan 2.1 VAE. Used in float32 by default to avoid banding in the log-encoded output. | |
| image | IMAGE | sRGB equirectangular panorama (2:1). Only the first image of a batch is used. | |
| prompt | STRING | Restore the full dynamic range of this clipped HDRI panorama. | Prompt used in training. Only used when a CLIP is connected. |
| width | INT | 204816–8192 | Processing width. The panorama is stretched, not cropped. |
| height | INT | 102416–8192 | Processing height. |
| steps | INT | 201–200 | Sampling steps. 20 is the tuned default; 10 is enough with the fast preset; 50 = reference-implementation default. |
| seed | INT | 420–18446744073709550000 | Noise seed. Long videos use the same seed for every window. |
| sampler | COMBO | res_multistep | Sampler, used when preset = custom. res_multistep and dpmpp_2m are equivalent and reach the 50-step reference in far fewer steps than euler. |
| scheduler | COMBO | simple | Scheduler, used when preset = custom. Only simple was measured; beta is deliberately not offered because it crushes highlights. |
| shift | FLOAT | 8.01–12 | Flow-matching shift, used when preset = custom. 8 measured best with every sampler; 5 is the reference implementation's value. |
| attention | COMBO | auto | auto: SageAttention, then flash-attn if installed, else ComfyUI's default. Unavailable backends fall back to PyTorch SDPA. SageAttention is quantised attention: choose sdpa or flash_attn for bit-reproducible results. |
| vae_precision | COMBO | fp32 | fp32 is recommended. as_loaded saves memory but can cause banding in highlights. |
| clipopt | CLIP | Optional umT5-xxl text encoder. If not connected, the bundled DiffHDR embeddings are used and the prompt is ignored. | |
| maskopt | MASK | Optional mask of regions to regenerate (white = regenerate). Overrides automatic detection. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| hdr | IMAGE | Linear scene-referred HDR (Rec.709 primaries, float32, values above 1.0). Do not route through 8-bit save nodes. |
| mask | MASK | Mask of regenerated regions. |