Chrome Ball to Envmap
A Chrome Ball Is the Whole Sky — This Node Unwraps It
- ball_images
- image
Photographers have captured lighting for a century with one cheap trick: put a mirrored ball in the shot. Every light in the scene - the sun, the windows, the overhead fluorescents - shows up compressed into that ball's reflection. Unwrapping it back into a usable environment map is what chrome_ball_to_envmap does, and it's the middle step that makes kijai's DiffusionLight pack work.
What it actually does
This is the port of the DiffusionLight research trick (Google's "Light Probes for Free by Painting a Chrome Ball"): instead of photographing a real ball, you paint one with SDXL and a special LoRA. The LoRA makes the model put a perfect mirrored chrome sphere in the middle of the image, reflecting the lighting you described in your prompt. The problem is that a sphere is a distorted view of the world - one half of the sky in the middle, horizon squeezed toward the edges. This node takes that ball photo and warps it into a proper 2:1 equirectangular environment map (a "long/lat" panorama, width exactly twice the height), which is the format relighters and 3D engines expect.
How the unwrap works
The math is old-school and worth understanding because it explains the settings. For every pixel of the output envmap it computes the direction the sphere would reflect toward you, derives the surface normal from that reflection (the code uses Blender's coordinate convention), then uses that normal as a lookup coordinate to sample the ball image. In practice that means torch.nn.functional.grid_sample does a bilinear warp of the ball photo - no model, no learned network, just geometry and a resample.
Each image in your input batch gets its own envmap, so it's batch-friendly: three ball frames in, three envmaps out, ready to feed straight into exposure_to_hdr.
The two numbers you actually set
- envmap_height (INT, default 256): the height of the output envmap in pixels. Width is always 2× this. 256 is honestly plenty for a lighting probe - the example workflow runs 1024, which is overkill unless you're printing.
- scale (INT, default 4): internal supersampling. The node computes the warp at
envmap_height × scaleand downsamples to the final size, so higher = smoother sampling around the ball's horizon. Default 4 is the sweet spot; you'll only feel the cost as a slightly slower warp.
That's the whole input list. There's nothing to tune for lighting quality, which is refreshing.
Installing it
Same install as the whole pack. In ComfyUI Manager, search "DiffusionLight" and install kijai/ComfyUI-DiffusionLight, or:
cd ComfyUI/custom_nodes
git clone https://github.com/kijai/ComfyUI-DiffusionLight
Restart ComfyUI. No requirements.txt - just torch and numpy, already present, so this is one of the rare packs with zero dependency pain. The only file you need to place by hand is the bundled LoRA diffusion_light_sdxl_lora_comfy.safetensors into ComfyUI/loras. It's the ready-to-go converted version; the original diffusers LoRA from the research repo won't load in ComfyUI as-is.
Gotchas worth knowing
- The ball wants a black background. The unwrap samples with border padding, so whatever surrounds the ball bleeds into the envmap's outer edges. The example workflow prompts for "a perfect mirrored reflective chrome ball sphere" on dark - keep your probe generation clean and the ball filling a good chunk of the frame.
- It doesn't create lighting, it measures it. If your chrome ball is lit badly, your envmap is badly lit - this node is faithful to its input, flaws and all.
- Not every generated "ball" is valid. If SDXL draws a matte or deformed sphere, the warp still runs and gives you garbage. Regenerate rather than troubleshooting the node.
kijai - the author behind WanVideoWrapper and DepthAnythingV2 - kept this one blessedly simple. It's a 30-line geometry utility, not a model, which is exactly why it doesn't drift or need version updates. If your goal is a believable light probe for relighting, this is the node that turns a painted ball into something you can actually use.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| ball_images | IMAGE | — | |
| envmap_height | INT | 2561–2048 | — |
| scale | INT | 41–30 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |