In your apps · plan, 28 September 2026
A figure standard is only useful if your tools speak it.
The profile is meant for the applications scientists already use: notebooks, image viewers, plotting libraries, and medical, microscopy and astronomy tools. It covers both halves of the name: the range a figure reaches above white, and the depth it holds, which the file states in its metadata. An app can adopt it at three levels, and each level stands on its own.
Three levels of support
Read and write conforming figures
Export the current view as a profile file: a gain-map JPEG, a 16-bit HDR PNG or an AVIF, with the declared mapping as metadata. Open one, and show its mapping and colour bar in data units.
Works in any app today, because the file is the ordinary figure wherever HDR is missing.
Render HDR in the app's own window
Apply the declared mapping on the GPU and draw above white. Each platform has its own route:
- Windows: a 16-bit float (scRGB) swap chain.
- macOS and iOS: extended dynamic range (EDR) Metal layers.
- The web: WebGPU with extended tone mapping.
Interface text stays at SDR white.
The most native feel, and the most work per app.
One library, many bindings
One small core library holds the mapping, the tone curve, the encoders and the conformance validator, with bindings for Python, JavaScript/WebAssembly, Julia, Java and MATLAB. Apps call it instead of reimplementing it.
Every implementation is held to the same golden test vectors.
Where to start
The first targets are apps that handle high-range data, are open source, and have a plugin system. None of those listed below has native HDR output for scientific data that we have found.
| App | Field | Why it fits | First step |
|---|---|---|---|
| Jupyter | everything | Notebooks render in a browser, and browsers already show HDR images | Show udeep figures as HDR inline (level 1); a WebGPU widget for live views (level 2) |
| napari | microscopy | A Python viewer for 12–16-bit data, with plugins | An export plugin (level 1), then an HDR canvas (level 2) |
| Makie | Julia plotting | Scientific plotting with GPU back ends | HDR export from CairoMakie: an extension of the Julia binding draws an HDR image and colour bar and saves the figure as a gain-map JPEG with its declaration (Makie 0.24). The Python validator accepts the files. Other Makie back ends are not covered, and the package is not yet installable on its own |
| Fiji / ImageJ | microscopy | The most widely used open image-analysis tool, with Java plugins | An export plugin through the core's Java binding |
| 3D Slicer | medical imaging | CT and MR, where window/level already means two ranges | Extend-style export of a window plus above-window structure |
| ParaView | simulation | Float data and GPU rendering; Python scripting | An export filter (level 1) |
| Astronomy (astropy, SAOImage DS9) | astronomy | Float images whose asinh stretches exist because 8-bit clips | An astropy-style visualisation helper that writes profile files |
What an app has to do
From the profile's viewer rules, in short:
- Apply the file's declared mapping exactly, fitted to the display's headroom.
- Never apply automatic tone mapping, "Auto HDR" or SDR-to-HDR expansion to scientific images.
- Keep interface text, axes and labels at SDR white.
- Show a colour bar in data units, with the value at SDR white marked.
- Never lift masked (saturated, clipped or invalid) pixels above white.
- When exporting, write the profile metadata, and write the ordinary SDR figure as the base.
What the conformance validator (python -m udeep.validate FILE.jpg) can check of this list,
given only the file:
- Item 6, the metadata half: every required property is present and the declaration is consistent, and the
stated precision (
MaxDataStep) is right. With--sdr FIGURE.pngit also confirms that the named SDR figure is the one the file was made from, where the file declares its digest. - Item 1, the declaration half: the declared mapping rises everywhere and can be inverted, and nothing in the file is brighter than the declared headroom (5 % over is allowed for a lossy gain map).
- Not from the file alone: whether the pixels are that mapping of the data, whether the base image is the
ordinary SDR figure, and item 5. These need the figure that made the file
(
udeep.mpl.validate(fig, path)); the command-line tool reports them as skipped. - Not checked by any program: item 4. Items 2 and 3 are about how the app behaves on screen.
The shared core
The reference implementation today is Python (udeep). It reproduces all 90 golden vectors, which come
in 14 kinds. A JavaScript port reproduces the 55 that it implements (11 kinds: it does not compute stored levels
or write the metadata and caption). A small native core with a C interface exists in Rust: all 14 kinds are implemented; it reproduces all 90 cases. It has an
optional gain-map JPEG writer, and no PNG writer or validator yet. The aim is that every language and app
shares one implementation:
core (Rust, C ABI): scale · tone curve · colour maps · PQ/HLG · gain map · metadata · validate ├─ Python udeep (matplotlib, Jupyter, napari, 3D Slicer, ParaView) ├─ JS / WASM udeepcore (web viewers, Jupyter widgets) ├─ Julia UDeepCore (through the core; figures with CairoMakie) ├─ Java (planned) (Fiji / ImageJ) └─ MATLAB (planned)
A second implementation also matters for standardisation: standards bodies look for independent implementations that pass the same tests.