JavaScript/TypeScript bindings for the UltraHDR (ISO 21496-1) gain map specification, backed by upstream google/libultrahdr compiled to WebAssembly via Emscripten/embind.
Dual-licensed under Apache-2.0 OR MIT, matching upstream libultrahdr.
npm install open-ultrahdr
import { isUltraHdr, setLocation } from 'open-ultrahdr';
// Set the location of WASM files (required if not co-located with JS)
setLocation('/path/to/wasm/');
const buffer = await file.arrayBuffer();
if (await isUltraHdr(buffer)) {
console.log('This is an UltraHDR image!');
}
import { decodeUltraHdr } from 'open-ultrahdr';
const buffer = await file.arrayBuffer();
const result = await decodeUltraHdr('image-1', buffer);
// Access components
const sdrBlob = new Blob([result.sdrImage], { type: 'image/jpeg' });
console.log('Image size:', result.width, 'x', result.height);
console.log('HDR capacity:', result.metadata.hdrCapacityMax, 'stops');
import { extractSdrBase } from 'open-ultrahdr';
const ultraHdrBuffer = await file.arrayBuffer();
const sdrBuffer = await extractSdrBase(ultraHdrBuffer);
// Use the SDR image for non-HDR displays
const blob = new Blob([sdrBuffer], { type: 'image/jpeg' });
import { encodeUltraHdr, defaultEncodeOptions } from 'open-ultrahdr';
const sdrBuffer = await sdrFile.arrayBuffer();
const hdrLinearData = await getHdrLinearData(); // Float32Array, 3 values per pixel
const ultraHdr = await encodeUltraHdr('encode-1', sdrBuffer, hdrLinearData, {
...defaultEncodeOptions,
targetHdrCapacity: 4.0,
});
// Create downloadable file
const blob = new Blob([ultraHdr], { type: 'image/jpeg' });
isUltraHdr(buffer: ArrayBuffer): Promise<boolean> - Check if image contains UltraHDR datadecodeUltraHdr(id: string, buffer: ArrayBuffer): Promise<UltraHdrDecodeResult> - Decode UltraHDR imageextractSdrBase(buffer: ArrayBuffer): Promise<ArrayBuffer> - Extract SDR base imagegetMetadata(buffer: ArrayBuffer): Promise<GainMapMetadata> - Get gain map metadata onlyencodeUltraHdr(id: string, sdrBuffer: ArrayBuffer, hdrBuffer: ArrayBuffer, options?: UltraHdrEncodeOptions): Promise<ArrayBuffer> - Encode UltraHDR imagevalidateMetadata(metadata: GainMapMetadata): Promise<boolean> - Validate metadataestimateHdrHeadroom(metadata: GainMapMetadata): Promise<number> - Get HDR headroom in stopsisMeaningfulHdr(metadata: GainMapMetadata): Promise<boolean> - Check if HDR is significantsetLocation(path: string): void - Set WASM file locationinterface GainMapMetadata {
version: string;
baseRenditionIsHdr: boolean;
gainMapMin: number[]; // RGB, log2 scale
gainMapMax: number[]; // RGB, log2 scale
gamma: number[]; // Per-channel gamma
offsetSdr: number[]; // Black point adjustment
offsetHdr: number[]; // Black point adjustment
hdrCapacityMin: number;
hdrCapacityMax: number;
}
interface UltraHdrEncodeOptions {
baseQuality: number; // 1-100
gainMapQuality: number; // 1-100
targetHdrCapacity: number; // Typically 2.0-4.0
includeIsoMetadata: boolean;
includeUltrahdrV1: boolean; // Android compatibility
gainMapScale: number; // 1, 2, or 4
}
emcc, emcmake)# Clone the repository (with submodules — libultrahdr is vendored as one)
git clone --recurse-submodules https://github.com/adamsilverstein/lib-open-ultrahdr.git
cd lib-open-ultrahdr
# Activate emsdk in the current shell
source /path/to/emsdk/emsdk_env.sh
# Install dependencies
npm install
# Build WASM module (emcmake + cmake + emcc)
npm run build:wasm
# Build TypeScript wrapper
npm run build:js
npm test
Dual-licensed under Apache-2.0 OR MIT. See LICENSE for the
preamble plus LICENSE-APACHE and LICENSE-MIT
for the full texts.
This matches the licensing of upstream
libultrahdr, which is bundled as a
git submodule under wasm/third_party/libultrahdr/ and statically linked
into the WASM build.
This project uses GitHub Actions to publish packages to npm.
Create a GitHub release with a version tag (e.g., v0.1.2). The workflow will automatically:
open-ultrahdr-wasm to npmopen-ultrahdr to npmwasm/package.json and js/package.jsonjs/package.json has the correct open-ultrahdr-wasm dependency versionThis project uses npm trusted publishing with OIDC, which eliminates the need for long-lived npm tokens:
Configure trusted publishing for both packages on npm:
open-ultrahdr-wasm and open-ultrahdrnpm-publish.ymlEnsure the GitHub Actions workflow has the required OIDC permissions:
permissions: id-token: write - Required to request the GitHub OIDC JWTpermissions: contents: read - Required to access repository contentsnpm CLI v11.5.1 or higher is required for trusted publishing to work.
No secrets need to be added to the repository. Authentication is handled automatically via GitHub's OIDC token.
Contributions are welcome! Please open an issue or pull request on GitHub.
TypeScript
55.6%
C++
40.7%
CMake
2.4%
JavaScript
1.3%
JavaScript/TypeScript bindings for the UltraHDR (ISO 21496-1) gain map specification, backed by upstream google/libultrahdr compiled to WebAssembly via Emscripten/embind.
Dual-licensed under Apache-2.0 OR MIT, matching upstream libultrahdr.
npm install open-ultrahdr
import { isUltraHdr, setLocation } from 'open-ultrahdr';
// Set the location of WASM files (required if not co-located with JS)
setLocation('/path/to/wasm/');
const buffer = await file.arrayBuffer();
if (await isUltraHdr(buffer)) {
console.log('This is an UltraHDR image!');
}
import { decodeUltraHdr } from 'open-ultrahdr';
const buffer = await file.arrayBuffer();
const result = await decodeUltraHdr('image-1', buffer);
// Access components
const sdrBlob = new Blob([result.sdrImage], { type: 'image/jpeg' });
console.log('Image size:', result.width, 'x', result.height);
console.log('HDR capacity:', result.metadata.hdrCapacityMax, 'stops');
import { extractSdrBase } from 'open-ultrahdr';
const ultraHdrBuffer = await file.arrayBuffer();
const sdrBuffer = await extractSdrBase(ultraHdrBuffer);
// Use the SDR image for non-HDR displays
const blob = new Blob([sdrBuffer], { type: 'image/jpeg' });
import { encodeUltraHdr, defaultEncodeOptions } from 'open-ultrahdr';
const sdrBuffer = await sdrFile.arrayBuffer();
const hdrLinearData = await getHdrLinearData(); // Float32Array, 3 values per pixel
const ultraHdr = await encodeUltraHdr('encode-1', sdrBuffer, hdrLinearData, {
...defaultEncodeOptions,
targetHdrCapacity: 4.0,
});
// Create downloadable file
const blob = new Blob([ultraHdr], { type: 'image/jpeg' });
isUltraHdr(buffer: ArrayBuffer): Promise<boolean> - Check if image contains UltraHDR datadecodeUltraHdr(id: string, buffer: ArrayBuffer): Promise<UltraHdrDecodeResult> - Decode UltraHDR imageextractSdrBase(buffer: ArrayBuffer): Promise<ArrayBuffer> - Extract SDR base imagegetMetadata(buffer: ArrayBuffer): Promise<GainMapMetadata> - Get gain map metadata onlyencodeUltraHdr(id: string, sdrBuffer: ArrayBuffer, hdrBuffer: ArrayBuffer, options?: UltraHdrEncodeOptions): Promise<ArrayBuffer> - Encode UltraHDR imagevalidateMetadata(metadata: GainMapMetadata): Promise<boolean> - Validate metadataestimateHdrHeadroom(metadata: GainMapMetadata): Promise<number> - Get HDR headroom in stopsisMeaningfulHdr(metadata: GainMapMetadata): Promise<boolean> - Check if HDR is significantsetLocation(path: string): void - Set WASM file locationinterface GainMapMetadata {
version: string;
baseRenditionIsHdr: boolean;
gainMapMin: number[]; // RGB, log2 scale
gainMapMax: number[]; // RGB, log2 scale
gamma: number[]; // Per-channel gamma
offsetSdr: number[]; // Black point adjustment
offsetHdr: number[]; // Black point adjustment
hdrCapacityMin: number;
hdrCapacityMax: number;
}
interface UltraHdrEncodeOptions {
baseQuality: number; // 1-100
gainMapQuality: number; // 1-100
targetHdrCapacity: number; // Typically 2.0-4.0
includeIsoMetadata: boolean;
includeUltrahdrV1: boolean; // Android compatibility
gainMapScale: number; // 1, 2, or 4
}
emcc, emcmake)# Clone the repository (with submodules — libultrahdr is vendored as one)
git clone --recurse-submodules https://github.com/adamsilverstein/lib-open-ultrahdr.git
cd lib-open-ultrahdr
# Activate emsdk in the current shell
source /path/to/emsdk/emsdk_env.sh
# Install dependencies
npm install
# Build WASM module (emcmake + cmake + emcc)
npm run build:wasm
# Build TypeScript wrapper
npm run build:js
npm test
Dual-licensed under Apache-2.0 OR MIT. See LICENSE for the
preamble plus LICENSE-APACHE and LICENSE-MIT
for the full texts.
This matches the licensing of upstream
libultrahdr, which is bundled as a
git submodule under wasm/third_party/libultrahdr/ and statically linked
into the WASM build.
This project uses GitHub Actions to publish packages to npm.
Create a GitHub release with a version tag (e.g., v0.1.2). The workflow will automatically:
open-ultrahdr-wasm to npmopen-ultrahdr to npmwasm/package.json and js/package.jsonjs/package.json has the correct open-ultrahdr-wasm dependency versionThis project uses npm trusted publishing with OIDC, which eliminates the need for long-lived npm tokens:
Configure trusted publishing for both packages on npm:
open-ultrahdr-wasm and open-ultrahdrnpm-publish.ymlEnsure the GitHub Actions workflow has the required OIDC permissions:
permissions: id-token: write - Required to request the GitHub OIDC JWTpermissions: contents: read - Required to access repository contentsnpm CLI v11.5.1 or higher is required for trusted publishing to work.
No secrets need to be added to the repository. Authentication is handled automatically via GitHub's OIDC token.
Contributions are welcome! Please open an issue or pull request on GitHub.
TypeScript
55.6%
C++
40.7%
CMake
2.4%
JavaScript
1.3%