Nodes/radiance/Write EXR
ComfyUI Node

Write EXR

Beauty, depth, normals and albedo in one file the comp can open

By FXTD-Studios·Created 8 months ago·Updated about 18 hours ago· 246
Write EXR
  • beauty
  • depth
  • normal
  • albedo
  • custom_1
  • custom_2
  • output_path
◄filename_prefixradiance_multipart►
◄bit_depth16-bit Half Float►
◄compressionZIP►
◄custom_1_nameemission►
◄custom_2_namespecular►
◄output_path►
◄remote_path►
◄frame_index1►
◄custom_metadata►

OpenEXR is how a VFX pipeline carries an image, and the reason is boring: it holds float values above 1.0 without argument, and it can hold several named image layers - AOVs, arbitrary output variables - in one file. Nobody wants to track six filenames. Write EXR is the node that makes the one file, with up to six named parts, readable by Nuke, Resolve and Fusion.

What goes in

beauty is the only required image, and it's the one that has to be scene-linear because values are written unchanged. There's no colour conversion anywhere in this node, which is the correct design decision and also the thing that bites people - feed it sRGB-encoded values and you've written a technically-valid EXR full of display-referred numbers that will look wrong in the comp.

Then optional AOVs, each with a fixed destination:

  • depth → a single Z channel in a part called depth. Taken from the first channel only (a grey depth image carries the same value in all three), and not normalized - so a 0–1 normalized depth map and a real world-units depth pass write different data.
  • normal → NX, NY, NZ in a normal part, values unchanged, with no 0–1 → -1..1 remap. So you have to have genuine normal vectors, not a visualisation of them.
  • albedo → albedo.R/G/B.
  • custom_1 / custom_2 → whatever you name them in custom_1_name (default emission) and custom_2_name (default specular). Channel-prefix names are restricted to letters, digits, underscore, hyphen and dot; anything else stops the node rather than silently writing a broken file.

A batch writes one file per frame, named <filename_prefix>.<frame_index, 4-digit padded>.exr, counting up from frame_index (default 1).

The format choices

bit_depth is 16-bit Half Float (default, roughly 11 bits of precision, smaller) or 32-bit Float. Half is fine for most beauty renders; go 32-bit when you have real data in the shadows or a deep comp.

compression gives you ten options and the tooltip is honest about which are which: ZIP, ZIPS, PIZ, RLE and Uncompressed are lossless; PXR24 is lossy on 32-bit float; B44, B44A, DWAA and DWAB are lossy. ZIP is the sane default - it's lossless and it deflates float data well.

output_path handles location: empty means ComfyUI's output folder, a relative path nests inside it, an absolute path is used as-is, and it gets created if missing. remote_path optionally copies the finished file to a second location - a NAS share, say - and a failed copy only logs a warning, so a dead mount won't kill your render. custom_metadata appends header attributes, one key=value per line, stored with a rad_ prefix unless it's a standard EXR name.

One output: output_path (STRING). It's an output node.

The fallback worth knowing about

If multi-part EXR writing isn't available, the node writes one EXR per part instead of failing. That's a good failure mode - you still get your data, just as several files - but it does mean a comp script expecting one file with six parts can come up short. If your downstream tool can't find a part, look at what actually landed on disk.

Install

Part of Radiance. Manager → search Radiance → install, restart, hard-refresh. Or:

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

Windows portable: run the pip step with python_embeded\python.exe. EXR I/O prefers Python's OpenEXR, falls back to OpenImageIO, then to OpenCV - so the pack writes EXR even on Python 3.14, where no OpenEXR wheel exists yet and the requirements file deliberately skips it.

Where people get burned

The metadata thing. Radiance's own release notes are worth reading on this: ComfyUI's PNG writes carry the whole node graph inside the file, which is why dragging an image back onto the canvas rebuilds the workflow. EXR doesn't work that way here - your master file is not your project file. Keep the workflow PNG next to the EXR, or accept that in three months you're reverse-engineering your own grade.

Treating the AOVs as free. Each part is a full-resolution image, so a beauty plus four AOVs on a long clip is five clip-sized buffers. Feed batches, not clips, or you'll watch RAM disappear.

And the classic: writing depth from a preview. If the depth image you wired in is an 8-bit normalised visualisation, you've written a pretty picture into the Z channel, not a depth pass. Comp tools will happily read it and give you wrong defocus. Real data in, real data out.

CategoryFXTD STUDIOS/Radiance/Load & Save

Inputs (15)

NameTypeDefaultDescription
filename_prefixSTRINGradiance_multipartFile name stem. The file is written as <prefix>.<frame_index, 4-digit padded>.exr.
beautyIMAGEMain image, written as the 'beauty' part (R, G, B, plus A if it has 4 channels). Values are written unchanged, so feed scene-linear data. Each frame of a batch is written to its own file.
bit_depthCOMBO16-bit Half FloatPixel type for every part: 16-bit half float (smaller, about 11 bits of precision) or 32-bit float.
compressionCOMBOZIPEXR compression for every part. ZIP, ZIPS, PIZ, RLE and Uncompressed are lossless; PXR24 is lossy on 32-bit float; B44, B44A, DWAA and DWAB are lossy.
depthoptIMAGEDepth AOV written as a single Z channel in a 'depth' part, from the first channel (a grey depth image carries the same value in all three); values are not normalised.
normaloptIMAGENormals AOV written as NX, NY, NZ in a 'normal' part, values unchanged (no 0-1 to -1..1 remap).
albedooptIMAGEAlbedo AOV written as albedo.R/G/B in an 'albedo' part, values unchanged.
custom_1optIMAGEExtra AOV written as <custom_1_name>.R/G/B in a part of that name.
custom_1_nameoptSTRINGemissionPart and channel-prefix name for custom_1. Letters, digits, underscore, hyphen or dot only; anything else stops the node.
custom_2optIMAGEExtra AOV written as <custom_2_name>.R/G/B in a part of that name.
custom_2_nameoptSTRINGspecularPart and channel-prefix name for custom_2. Letters, digits, underscore, hyphen or dot only.
output_pathoptSTRINGOutput folder. Empty writes to the ComfyUI output folder; a relative path is inside it; an absolute path is used as-is. Created if missing.
remote_pathoptSTRINGOptional second folder (for example a NAS or UNC share) the finished file is copied to. A failed copy only logs a warning.
frame_indexoptINT1Frame number of the first image in the batch, used in the file name; later frames count up from it. An AOV with one frame is used for every frame.
custom_metadataoptSTRINGExtra header attributes, one key=value per line. Keys are stored with a 'rad_' prefix unless they are standard EXR names (owner, comments, capDate and so on).

Outputs (1)

NameTypeDescription
output_pathSTRING—