Nodes/ComfyUI-OCIO/OCIO Write
ComfyUI Node

OCIO Write

Write a float EXR master, a ProRes, or an archival FFV1 — the endpoint of the color chain

By SlavaSexton·Created 2 months ago·Updated 3 days ago· 99
OCIO Write
  • images
  • video
  • alpha
  • audio
  • path
profilenone
input_colorspacesRGB - Display
output_colorspaceACEScg
containersequence
still_formatexr
video_codecprores_4444
bit_depth16f
compressionzip
auto_rangetrue
first_frame1
last_frame0
start_number1
source_start1
raw_datafalse
colorspace_in_nametrue
output_folder
filenameocio_out
auto_colorspacetrue
view(none) colorimetric, no tone map
fps24.000
render_nonce
metadata
write_audiotrue
write_sidecartrue

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, or video. 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_12 is 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 - a 32f + DWAA file 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.

CategoryOCIO

Inputs (28)

NameTypeDefaultDescription
profileCOMBOnoneSets 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_colorspaceCOMBOsRGB - DisplayColorspace from the active OCIO (ACES) config.
output_colorspaceCOMBOACEScgColorspace from the active OCIO (ACES) config.
containerCOMBOsequence3 options: still image, sequence, video
still_formatCOMBOexrUsed 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_codecCOMBOprores_4444Video 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_depthCOMBO16fPer format: JPEG 8; PNG 8/16; TIFF 8/16/32f; EXR 16f/32f; DPX 10/16. The list narrows to the chosen format.
compressionCOMBOzipEXR 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_rangeBOOLEANtrueON: 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_frameINT10–100000000still 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_frameINT00–100000000sequence/video: last frame to write (0 = to the end; auto-filled from the source, e.g. 97). Ignored for a still image.
start_numberINT10–100000000OUTPUT 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_startINT10–100000000(auto) the source's first frame number, used to map first_frame/last_frame to the batch. Set by the wire.
raw_dataBOOLEANfalseNuke '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_nameBOOLEANtruePut 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_folderSTRINGEmpty = 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.
filenameSTRINGocio_outBase name. Numbering / extension are added automatically.
auto_colorspaceBOOLEANtrueSuperseded 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.
viewoptCOMBO(none) colorimetric, no tone mapHow 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.
imagesoptIMAGEAn image / sequence / video frame batch to write. Mutually exclusive with the ComfyUI Video input.
videooptVIDEOA 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.
alphaoptMASKOptional alpha channel -> RGBA (EXR / TIFF / PNG; ignored for JPEG). Wire OCIO Read's alpha output, or any MASK.
fpsoptFLOAT24.0001–240Video 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_nonceoptSTRINGInternal, 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.
audiooptAUDIOOptional 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.
metadataoptSTRING(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_audiooptBOOLEANtrueOFF: 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_sidecaroptBOOLEANtrueThe <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)

NameTypeDescription
pathSTRING