Nodes/LumaFlux/Save HDR PNG (16-bit PQ)
ComfyUI Node

Save HDR PNG (16-bit PQ)

Don't Let ComfyUI's Save Image Node Eat Your Dynamic Range

By shreshthsaini·Created 3 months ago·Updated 8 days ago· 2
Save HDR PNG (16-bit PQ)
  • hdr_pq
    filename_prefixlumaflux/hdr

    Here's the failure mode this node exists to prevent. You run a conversion, the SDR-to-HDR node hands you hdr_pq, you wire it into the stock Save Image node out of habit, and you get a gray, muddy, washed-out PNG. Nothing crashed. You just threw away most of what you paid 27 GB of VRAM for.

    The stock saver writes 8 bits per channel and no color tagging. The LumaFlux signal is a PQ/BT.2020 code value that needs 16 bits and a tag saying what it is. This node writes exactly that.

    What lands on disk

    Each frame becomes a 16-bit RGB PNG whose pixel values are the PQ signal scaled by 65535 - code values, not display values. Alongside the image header it carries a cICP chunk declaring primaries 9 (BT.2020), transfer characteristic 16 (SMPTE ST 2084 / PQ), matrix coefficients 0 (RGB) and full range. HDR-aware viewers - Chrome, recent macOS - read that chunk and render the file as HDR instead of as a flat SDR image. Viewers that don't understand cICP still open the file; they just show it wrong.

    The authors wrote the PNG encoder by hand for this, and it's deliberately minimal: signature, IHDR, optional cICP, IDAT, IEND. Same layout the LumaFlux CLI writes, which is why the evaluation scripts can read the frames straight back. If you're feeding a grading or compositing pass, this is the intermediate you want, and 16-bit is the reason - 10-bit delivery formats with 4:2:0 chroma have already thrown information away.

    The two inputs

    hdr_pq is the first output of LumaFlux SDR to HDR. Do not put the preview output here; you'd be writing a tone-mapped SDR image with an HDR tag stuck to it.

    filename_prefix defaults to lumaflux/hdr. A slash in the prefix creates a subfolder under ComfyUI/output/, so the default lands in output/lumaflux/. A batch of 25 frames writes 25 files with an incrementing counter suffix. That's the whole surface - there are no outputs to wire, because a save node is where the graph ends.

    Install

    cd ComfyUI/custom_nodes
    git clone https://github.com/shreshthsaini/LumaFlux
    pip install -r LumaFlux/requirements.txt
    

    Restart ComfyUI afterwards, or install through ComfyUI Manager by searching "LumaFlux". The repository root is the node pack, so clone the whole thing - copying just the comfyui/ folder gets you an ImportError about the lumaflux package. This particular node is the least demanding of the five: it's a numpy array write with no ffmpeg, no adapter download and no gated model login, though you'll still want the Loader and converter upstream to produce anything worth saving.

    Where people get burned

    Your workflow is not in the file. This is the one that catches ComfyUI users specifically, because the ecosystem convention is that a saved PNG is the project file - drag it back onto the canvas and the graph reconstructs. That works because SaveImage stuffs tEXt/iTXt chunks into the PNG. This node's encoder writes none of them. You get a clean, HDR-correct, 16-bit image with zero metadata: no workflow, no prompt, no seed. Archive the workflow JSON yourself, or save a throwaway copy through the normal Save Image node alongside it.

    Flat and gray in the ComfyUI canvas is expected. The web UI doesn't apply the PQ transfer function, so your output preview will look crushed. That's the preview being naive, not the file being wrong. Open it in Chrome, or route the tensor through LumaFlux HDR Preview to look at it properly.

    Viewer support is genuinely patchy. Chrome and recent macOS honor cICP. Plenty of other viewers - including some Windows defaults - ignore it and show you a dark, low-contrast image. If a collaborator says your file is broken, ask what they opened it in before you re-render anything.

    Rows are written unfiltered, so files are larger than they need to be. The encoder uses PNG filter type 0 across the board rather than the adaptive filtering a mature encoder would apply. That's a size cost, not a quality cost - but on a few hundred frames it's a real disk cost, and 16-bit only doubles the bill.

    Don't convert these on the way out. JPEG, WebP, or any 8-bit round trip deletes both the bit depth and the cICP chunk in one step, and there's no recovery. The exported HDR PNG is the master; treat everything you show someone as a derivative.

    CategoryLumaFlux

    Inputs (2)

    NameTypeDefaultDescription
    hdr_pqIMAGEPQ / BT.2020 output of LumaFlux SDR to HDR.
    filename_prefixSTRINGlumaflux/hdr

    Outputs (0)

    No outputs