OCIO Write
Write a float EXR master, a ProRes, or an archival FFV1 — the endpoint of the color chain
- images
- video
- alpha
- audio
- path
OCIO Write is where the pack's whole argument lands: it takes your image batch and writes it to disk color-managed, in the formats a real pipeline actually uses - EXR, DPX, TIFF, PNG, JPEG for stills and sequences; ProRes, DNxHR, FFV1, h264, hevc in MOV, MXF, MP4 or Matroska for video. It's Nuke's Write, and it exists because Save Image quietly 8-bits everything, and that's where dynamic range goes to die. The pack measured one LTX-2.5 generation: 2304 distinct brightness levels from the model, 220 through the stock write path. This node keeps all of it.
The core is two fields and one idea: input_colorspace (your working space, default sRGB - Display) → output_colorspace (what you encode into - the format picks a sensible default, EXR → ACEScg). Nothing is clamped on the way, and values below 0 and above 1 survive into any float container. The profile dropdown presets the whole node for known HDR sources: LTX 2.3 HDR (linear Rec.709 → ACEScg, because Lightricks' own node already undid the LogC3 curve), the two LumiPic log profiles, Seedance 4K 10-bit (a placeholder until that encoding is published), and SDR Rec.709 delivery. There is no LTX 2.5 preset on purpose - 2.5's HDR is ACEScct, and you undo that curve with OCIO LogConvert instead.
The choices that matter
- container -
still image,sequence, orvideo. That drives everything below it. - video_codec - seventeen of them. The measured table is worth internalizing:
ffv1(.mkv, 16-bit 4:4:4) is the only one that returns this pack's input bit-exact - md5 of the stream equals what went in, and it's a Library of Congress preservation format.hevc_444_12is the only genuine 12-bit encode - ffmpeg's ProRes and DNxHR encoders top out at 10 bits no matter what their names claim (ProRes reads back as 12 and carries 10). h264/hevc are review codecs, and on an HDR output space they auto-move to 10-bit (BT.2100 has no 8-bit form). - bit_depth - narrows to the format: EXR 16f/32f, DPX 10/16, TIFF 8/16/32f, PNG 8/16, JPEG 8.
- compression (EXR only) -
zip(default) is lossless. dwaa/dwab are lossy: measured, they cost about half the distinct values, multiply the error by ~80, and quantise float32 to half before compressing - a32f + DWAAfile is a half-precision file that still says "float." Review copy yes, master or data pass never. - auto_range - pulls frame range, start number and fps from the upstream OCIO Read automatically. Handy on canvas; API-only note: an API prompt gets whatever is in its JSON, so set the four fields explicitly for farm work.
- view (optional) - how to cross the scene/display boundary. Left on
(none), the conversion is colorimetric: scene-linear 4.0 leaves as 1.78 and the container clips it flat. Pick an ACES 2.0 view and the output transform rolls highlights off instead. Use a view for a review deliverable, not a master.
Wire in alpha for RGBA, fps to carry the source rate, audio to mux a track, and metadata from OCIO Read to carry the plate's camera/lens/timecode into the file. A native ComfyUI video input lets you record a Load Video clip (or a Seedance-style API node's VIDEO) straight out with all these settings.
Where people get burned
The empty-folder "success." If a render reports success and the folder is empty, ComfyUI's front end likely dropped this output node via a partial-execution target list - a skipped node has nothing to report its own absence with. Queue from the graph instead. Second, EXR needs OPENCV_IO_ENABLE_OPENEXR=1 set before ComfyUI starts. Third, the preview tells the truth: the node shows what it wrote in its output colorspace, so a wrong colorspace pick looks visibly wrong rather than quietly wrong - trust that. Install via Manager (Nightly - the numbered registry versions are stale/flagged) or clone https://github.com/SlavaSexton/ComfyUI-OCIO + pip install -r requirements.txt. Video containers need a full ffmpeg build on PATH.
Inputs (28)
| Name | Type | Default | Description |
|---|---|---|---|
| profile | COMBO | none | Sets from/output colorspace; the HDR presets also force EXR 16f. LTX 2.3 is for 2.3 only: it expects linear, because Lightricks' own node already undid LogC3. There is no 2.5 preset - undo 2.5's ACEScct curve with OCIO LogConvert instead. SDR Rec.709 delivery: read docs/NODES_IO.md first. |
| input_colorspace | COMBO | sRGB - Display | Colorspace from the active OCIO (ACES) config. |
| output_colorspace | COMBO | ACEScg | Colorspace from the active OCIO (ACES) config. |
| container | COMBO | sequence | 3 options: still image, sequence, video |
| still_format | COMBO | exr | Used for still image / sequence (hidden for video). EXR keeps float, so negatives and values above 1.0 survive. DPX is the film-pipeline interchange format, integer, 10 or 16 bit. TIFF is float only at 32f. PNG and JPEG are integer and clip both tails; JPEG is always 4:2:0. |
| video_codec | COMBO | prores_4444 | Video only. The node's footer states the depth once you pick. FFV1 is the only one that returns this pack's input unchanged (16-bit, .mkv, archival). HEVC 4:4:4 12-bit is the only genuine 12-bit encode - ffmpeg's ProRes and DNxHR encoders top out at 10 whatever the format is nominally worth. 8-bit: DNxHR HQ, h264, hevc, and those two move to 10-bit on an HDR output. Measured table: README.md. |
| bit_depth | COMBO | 16f | Per format: JPEG 8; PNG 8/16; TIFF 8/16/32f; EXR 16f/32f; DPX 10/16. The list narrows to the chosen format. |
| compression | COMBO | zip | EXR compression. ZIP (default) / ZIPS / RLE are lossless; PIZ is lossless and suits grain. DWAA / DWAB are much smaller and LOSSY: measured on a real frame they cost about half the distinct values and multiply the error by eighty, at 16f as well as 32f, and they quantise float32 to HALF before compressing - so 32f + DWAA is a half-precision file that still says 'float'. Pick DWAA for a review or comp copy, never for a master or a data pass (depth, normals, IDs). EXR only. |
| auto_range | BOOLEAN | true | ON: first_frame / last_frame / start_number / fps come from the upstream OCIO Read. CANVAS ONLY - an API prompt gets whatever is in its JSON, with this ticked and doing nothing. Set the four explicitly for farm work. |
| first_frame | INT | 10–100000000 | still image: WHICH frame to save - a number outside the batch is refused by name, not rounded to the nearest frame. sequence/video: first frame to write (frame numbers, auto-filled from the source, e.g. 86). |
| last_frame | INT | 00–100000000 | sequence/video: last frame to write (0 = to the end; auto-filled from the source, e.g. 97). Ignored for a still image. |
| start_number | INT | 10–100000000 | OUTPUT file numbering start (auto-filled to the source's first frame, e.g. 86; set 1 for 0001..). This is the re-base, NOT retime. |
| source_start | INT | 10–100000000 | (auto) the source's first frame number, used to map first_frame/last_frame to the batch. Set by the wire. |
| raw_data | BOOLEAN | false | Nuke 'Raw Data': write the pixels as-is, skipping the from->out colorspace conversion. The file is also left UNTAGGED - no chromaticities on a still, no colour primaries / transfer / matrix on a movie - because unconverted pixels have no delivery space to name. |
| colorspace_in_name | BOOLEAN | true | Put the output colorspace in the file name, before the frame number: name_acescg.0001.exr. Uses the sanitized output_colorspace (or 'raw' when Raw Data is on). |
| output_folder | STRING | Empty = the ComfyUI output dir. Relative or $OUTPUT/sub sits under it; an absolute path is written there. PREFER relative or $OUTPUT: this value is stored in the workflow, and other nodes embed the workflow into the files they write. | |
| filename | STRING | ocio_out | Base name. Numbering / extension are added automatically. |
| auto_colorspace | BOOLEAN | true | Superseded and inert - it changes nothing, which is why it is hidden. Use profile = 'auto', which traces the upstream source and covers more cases. Kept only so earlier saved graphs keep their other values in place. |
| viewopt | COMBO | (none) colorimetric, no tone map | How to cross the scene/display boundary, on the pairs that cross it at all. Left on (none) the conversion is colorimetric: no tone mapping, so scene-linear 4.0 leaves as 1.78 and the container clips it flat. Pick a view and the ACES Output Transform is applied instead, rolling highlights off - 4.0 becomes 0.905, and nothing reaches 1.0 until about 128. Use a view for a review deliverable; leave it off for a technical re-encode or for material already scene-referred. Ignored entirely when both colorspaces sit on the same side, so camera logs and ACEScg to ACEScct are untouched. |
| imagesopt | IMAGE | An image / sequence / video frame batch to write. Mutually exclusive with the ComfyUI Video input. | |
| videoopt | VIDEO | A ComfyUI native VIDEO (e.g. Load Video) to render out with ALL these Write settings (container, codec, colorspace, bit depth). Mutually exclusive with the image input; the movie's own frame rate is used for a video container. | |
| alphaopt | MASK | Optional alpha channel -> RGBA (EXR / TIFF / PNG; ignored for JPEG). Wire OCIO Read's alpha output, or any MASK. | |
| fpsopt | FLOAT | 24.0001–240 | Video frame rate. Wire OCIO Read's fps output here to carry the source rate. This sets the TIME BASE, it does not resample: the same frames are written either way, so a 24fps batch written at 48 plays twice as fast and half as long (a conform, not a frame-rate conversion). NTSC rates go out as their exact rational, 23.976 as 24000/1001. |
| render_nonceopt | STRING | Internal, hidden. The Render button bumps it so a repeat render to the same path really re-writes; ComfyUI would otherwise cache an identical Write and skip it. | |
| audioopt | AUDIO | Optional track. A video container muxes it in, trimmed to the frames written so it cannot drift; a sequence gets a sidecar .wav, because EXR / TIFF / PNG hold no audio. Formats per container: docs/NODES_IO.md. | |
| metadataopt | STRING | (optional) Wire OCIO Read's 'metadata' output here - or any node that emits the same JSON - to carry the plate's camera, lens, editorial attributes AND its start timecode into the written file. Claims a colour transform invalidates are dropped, not copied: docs/NODES_IO.md. A wire this node cannot read is reported and ignored; it never stops the render. | |
| write_audioopt | BOOLEAN | true | OFF: no sound at all, no muxed track and no sidecar .wav, even when one is wired or a native Video input brings its own. ON: the wired input wins, else the Video input's track. Off for a picture-only master. |
| write_sidecaropt | BOOLEAN | true | The <name>.json written beside the render, holding the FULL metadata set. ON is right for a movie, whose container cannot hold all of it - check `sidecar_only` in the file to see exactly what would be lost. OFF is safe for EXR, whose header already carries everything (`sidecar_only` is empty there), and for any delivery that must be the picture files and nothing else. It never affects the pixels, the container tags or the .wav beside a sequence. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| path | STRING | — |