Save Plate (linear)
Where a Nuke roundtrip silently goes wrong
- images
- alpha
- written_path
Generating something is the easy half. Getting it back into a comp as a linear EXR that merges cleanly over your plate is the half that eats an afternoon, because ComfyUI's default Save Image writes a PNG with the sRGB-ish values it was handed, and your comp wants linear light. This node is the other end of the roundtrip: it writes EXR (or TIFF/PNG/JPG), applies the inverse colorspace transform on the way out, and keeps float precision and values above 1 when the format allows it.
It's part of Samuelsujan's EXR Plate pack - same author, same premise. Load with keep linear or linear -> sRGB, drive the model, then set this node's colorspace to the inverse of whatever you did on the way in, and what lands on disk is linear again.
How it works
images is a batch, and frame i is written to your numbered path starting at first_frame. The path handling does more work than it looks:
- The extension always comes from
format- path says.png, dropdown saysexr, you get.exr. Deliberate: a path wired from the Path Builder can't disagree with the writer. - Point it at a bare folder (existing directory, trailing slash, no extension) and it names files itself:
plate.####.exr, orplate.exrfor a single frame. - Multi-frame with no padding in the path gets
.####inserted rather than failing, and missing output directories are created for you.
Per frame: the colorspace transform goes on first, then an optional resize, then a channel flip and the write. EXR goes out half- or full-float with ZIP compression; TIFF and PNG are scalings into the integer range; JPG at quality 95. Wire in alpha and it's resized with the RGB and written as the fourth channel - except JPG, which has no alpha.
And there's send_to_nuke: after the write, the node POSTs the path and frame range to the Nuke-side bridge listener (default http://127.0.0.1:8189) so a Read node with your fresh render appears in the comp. If Nuke isn't up it prints a console warning and the write still succeeds - it will not fail your render over this.
The inputs you'll set
path- absolute, with####for sequences. Wire it from a Path Builder if you don't want to think about versions.format-exris the one that keeps HDR values above 1. TIFF and PNG are integer formats: everything clamps to 0–1 on write.png (16-bit)gives you precision, not range - a 16-bit staircase inside the same 0–1 window, not a replacement for EXR. JPG throws away alpha and is only useful as a review proxy.colorspace- defaults tosRGB -> linear, and this is the field that quietly breaks things. See below.bit_depth- EXR only, ignored elsewhere.half (16-bit)is plenty for a beauty plate; gofloat (32-bit)for depth/Z or anything you'll grade hard.custom_width/custom_height- resize on save; set one and the other keeps aspect.alpha,send_to_nukeandnuke_hostare the optional ones; leave the last two alone unless you're running the bridge.
The only output is written_path (a STRING - one path, or first .. last (N frames)). It's an output node, so it always runs and the cache works backward from it. There's no image preview from this node, so keep a Preview Image on the branch before it if you want to eyeball the result.
Installing it
Same pack, same one-time setup - ComfyUI Manager → search EXR Plate (Samuelsujan's EXR Plate) → Install, or:
cd ComfyUI/custom_nodes
git clone https://github.com/Samuelsujan/comfyui-exr-plate
python -m pip install -r comfyui-exr-plate/requirements.txt # opencv-python>=4.6, numpy
Restart ComfyUI and hard-refresh the browser. The pack's web/ folder has to stay a subfolder of the pack - WEB_DIRECTORY = "./web" in __init__.py is what makes ComfyUI serve it.
Where people get burned
- The double-decode. The most common way to wreck a roundtrip: load with
keep linear(tensor is now linear) and save on the defaultsRGB -> linear. You've applied an sRGB decode to linear data and the plate comes back crushed and contrasty. Rule of thumb: if the loader, the saver and everything in between don't care about colorspace, set both tokeep linear. The pack ships apassthrough_api_format.jsonworkflow for exactly this test - Load → Save, keep linear, clamp off - and the check is to Merge (minus) the result against the source in Nuke and gain up. Black frame means a lossless pipeline. - Clipping already happened upstream. If the loader's
clamp_outputwas on, no save setting gets the headroom back. That decision lives at the loader. - Overwrites. Same path plus same frame range means the second queue silently eats the first render. There's no auto-versioning here - that's the Path Builder's job.
- Relative paths. Unlike the loader, this node doesn't resolve against ComfyUI's input directory; a relative path lands relative to the ComfyUI process's working directory, which is not where you think. Give it an absolute path.
- Alpha direction. The
alphainput is a plain MASK, and this pack reads and writes the channel as-is (white = opaque), while core Load Image hands out1 - alpha(white = transparent). If your composite comes back with the matte punched out backwards, that mismatch is why. send_to_nukedoing nothing. It's aurllibcall to localhost; if Nuke isn't open with the bridge loaded you get a console line and nothing else. Not an error, just a no-op.
Inputs (11)
| Name | Type | Default | Description |
|---|---|---|---|
| images | IMAGE | — | |
| path | STRING | Output path with #### / %04d for sequences. e.g. /out/END_130_comp.####.exr | |
| first_frame | INT | 10–999999 | — |
| custom_width | INT | 00–16384 | Resize on save. 0 = keep. One value set = keep aspect. |
| custom_height | INT | 00–16384 | — |
| format | COMBO | EXR = float, keeps HDR values above 1. tiff/png/jpg are integer formats: values are clamped to 0-1 on write. jpg has no alpha. | |
| colorspace | COMBO | sRGB -> linear | Inverse of whatever the load applied, so the plate lands back in linear for Nuke. |
| bit_depth | COMBO | EXR only; ignored for other formats. | |
| alphaopt | MASK | — | |
| send_to_nukeopt | BOOLEAN | false | After writing, push the result to Nuke as a Read node (needs the Samuelsujan bridge listener running in Nuke). |
| nuke_hostopt | STRING | http://127.0.0.1:8189 | Nuke bridge listener address. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| written_path | STRING | — |