Developer reference

Developers

Gainmaps ships a CLI for batch encoding, an agent skill for Ultra HDR text effects, and a TypeScript library for programmatic encoding. All three are open source.

CLI

v1.1.0 · Changelog

The gainmap CLI converts images to Ultra HDR JPEG gain maps (ISO 21496-1). JPEG, PNG, WebP, and other inputs write a sibling JPEG by default (photo.png photo-gain.jpg). Pass -o filename.jpg to choose a path, or -i to overwrite a JPEG original.

Your original file is never modified unless you pass --in-place.

Output writes to a sibling: photo.jpg photo-gain.jpg. An existing output is skipped by default (--no-clobber). Use --force (-f) to overwrite.

Installation

npmnpm install -g gainmap
Homebrewbrew install kirkstrobeck/tap/gainmap
curlcurl -fsSL https://gainmaps.com/install.sh | sh

Flags

FlagDescription
-o, --out, --output <path>File (single input), directory (required for recursive/multi-file; mirrors source dirs), or - for stdout
--out-type <type>Output format when --out is a directory (jpg jpeg png webp avif tif tiff gif). File --out uses the extension; --out-type must agree if both are set. jpg/jpeg write Ultra HDR gain maps; other types encode via sharp. HEIC/HEIF/SVG are input only. Unknown flags error.
--suffix <str>Output filename suffix (default: -gain)
-i, --in-placeOverwrite the original JPEG (implies force)
-f, --forceOverwrite existing outputs
--no-clobberSkip existing outputs (default)
-n, --dry-runPrint planned paths, write nothing
--stdoutWrite one conversion to stdout
--stdinRead image bytes from stdin
-q, --quality <1-100>Encode quality for JPEG, WebP, and AVIF (default 92)
--boost <0-1>HDR boost (default 0.5)
--headroom <n>Explicit headroom multiplier; overrides --boost
--model <name>highlight (default) | window
--matte <name>white (default) | checkerboard
--max-size <px>Fit longest edge before encode
-R, -r, --recursiveRecurse into directories
--ext <list>Comma-separated extensions to include
--exclude <glob>Skip matching paths (repeatable)
-j, --jobs <n>Parallel conversions (default: CPU count, max 8)
-v, --verboseLog every file to stderr
--quietErrors only
--continueKeep going after a failed file
-h, --helpShow help
-V, --versionPrint version
--update / --self-updateUpgrade CLI when a newer release exists
--no-update-check / --offlineSkip the update check
--auto-updateAuto-update if a newer version exists

Exit codes

CodeMeaning
0Success
1Conversion error
2Usage / missing / empty input

File-type contract

By default, every input writes a sibling Ultra HDR JPEG (e.g. photo.jpg photo-gain.jpg; non-JPEG inputs also become .jpg).

Pass --out dest.webp (or another known extension) to write that format via sharp, or use a directory --out with --out-type. jpg/jpeg still embed a gain map; PNG and WebP keep alpha. HEIC/HEIF/SVG are input only. Unknown flags exit 2 as unsupported option.

Examples

Convert a single JPEG

gainmap photo.jpg

Custom output filename

gainmap photo.jpg -o hdr.jpg

File out as WebP

gainmap photo.png --out dest.webp

Directory out-type WebP

gainmap photo.png --out ./out --out-type webp

Recursive PNG outs

gainmap -R ./shots --out ./out --out-type png

Overwrite JPEG in place

gainmap -i photo.jpg

Convert a whole folder

gainmap ./shots

Recursive with output dir

gainmap -R ./shots -o ./out

Recursive, skip raw subfolder

gainmap -R --exclude "**/raw/**" ./shots

PNG to JPEG gain map

gainmap photo.png

Max boost, checkerboard matte

gainmap --boost 1 --matte checkerboard logo.jpg

Dry run

gainmap -n -R ./shots

Self-update

gainmap update

Agent Skill

The ultra-text skill teaches any coding agent how to add Ultra HDR letterforms to headlines and logotypes using selectable text, measured SVG masks, a foundation canvas at 75% of the specified headroom, and a brighter inset rgba16float canvas.

The skill is recommended because the effect is not just a mask: the foundation headroom, 0.5 px inset, 0.3 px inner-mask blur, and layer order all work together to avoid crispy edges and outline artifacts. It can be updated over time as the implementation improves. See the Ultra text demo →

Installation

Run the following command in your project, then ask your agent to add Ultra text to a heading. Use Copy skill for the install command and Copy prompt for a one-click implementation prompt.

Add Ultra text to your project

Recommended path: install the agent skill so your project can pick up future Ultra text refinements over time. It teaches any coding agent how to add Ultra HDR letterforms to headlines and logotypes.

npx skills add kirkstrobeck/gainmaps

The skill is the recommended path because this rendering stack has edge-treatment details that can be updated without retyping the implementation by hand. View skill source on GitHub →

What the skill provides

After installing, the agent gains access to a reference implementation with these source files:

  • ultra-word.tsx — accessible mask-and-canvas component; accepts text, typeClassName, intensity
  • ultra-fill-canvas.tsx — the WebGPU canvas rectangle
  • ultra-fill.tsstartUltraFill(canvas, { intensity }): WebGPU session, 1×1 rgba16float surface
  • text-ultra.ts — constants and helpers including TEXT_ULTRA_FOUNDATION_RATIO = 0.75, foundationHeadroomFor(), and TEXT_ULTRA_INTENSITY = 4.0
  • ultra-overlay.tsultraOverlayGeometry() for the SVG bleed
  • ultra.css — CSS gate: hidden by default, shown when html[data-ultra="on"]

Library

The gainmap package exports a programmatic encode API from the gainmap/encode entrypoint. It runs in Node.js 18+ and is suitable for server-side or build-time encoding.

For the gain map format itself — what the output file contains and how displays render it — see the format docs →

Installation

npm install gainmap

encodeRgbaToUltraHdrJpeg

The primary encode function. Accepts raw RGBA pixel data and returns an Ultra HDR JPEG as a Uint8Array.

function encodeRgbaToUltraHdrJpeg( pixels: Uint8Array, // RGBA, 8 bits per channel width: number, height: number, options: GainMapEncodeOptions = {} ): GainMapEncodeResult

GainMapEncodeOptions

OptionTypeDescription
boostnumber (0–1)UI boost level. 0.5 = default photo headroom (~3.34×).
headroomnumberExplicit headroom multiplier; overrides boost when set.
qualitynumber (1–100)JPEG quality of the SDR base. Default 92.
hdrModel"highlight" | "window"Highlight-selective (default) or window-calibrated HDR model.
matte"white" | "checkerboard"Background matte for transparent pixels. Default white.

TypeScript example

import { encodeRgbaToUltraHdrJpeg } from "gainmap/encode"; import { readFile, writeFile } from "node:fs/promises"; import sharp from "sharp"; const input = await readFile("photo.png"); const { data, info } = await sharp(input) .ensureAlpha() .raw() .toBuffer({ resolveWithObject: true }); const result = encodeRgbaToUltraHdrJpeg( new Uint8Array(data), info.width, info.height, { boost: 0.7, quality: 90 }, ); await writeFile("photo-gainmap.jpg", result.output); console.log(result.note); // "Gain map JPEG · 3.94× · 3024×4032"
Developers · Gainmaps