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 · ChangelogThe 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
npm install -g gainmapbrew install kirkstrobeck/tap/gainmapcurl -fsSL https://gainmaps.com/install.sh | shFlags
| Flag | Description |
|---|---|
| -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-place | Overwrite the original JPEG (implies force) |
| -f, --force | Overwrite existing outputs |
| --no-clobber | Skip existing outputs (default) |
| -n, --dry-run | Print planned paths, write nothing |
| --stdout | Write one conversion to stdout |
| --stdin | Read 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, --recursive | Recurse 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, --verbose | Log every file to stderr |
| --quiet | Errors only |
| --continue | Keep going after a failed file |
| -h, --help | Show help |
| -V, --version | Print version |
| --update / --self-update | Upgrade CLI when a newer release exists |
| --no-update-check / --offline | Skip the update check |
| --auto-update | Auto-update if a newer version exists |
Exit codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Conversion error |
| 2 | Usage / 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.jpgCustom output filename
gainmap photo.jpg -o hdr.jpgFile out as WebP
gainmap photo.png --out dest.webpDirectory out-type WebP
gainmap photo.png --out ./out --out-type webpRecursive PNG outs
gainmap -R ./shots --out ./out --out-type pngOverwrite JPEG in place
gainmap -i photo.jpgConvert a whole folder
gainmap ./shotsRecursive with output dir
gainmap -R ./shots -o ./outRecursive, skip raw subfolder
gainmap -R --exclude "**/raw/**" ./shotsPNG to JPEG gain map
gainmap photo.pngMax boost, checkerboard matte
gainmap --boost 1 --matte checkerboard logo.jpgDry run
gainmap -n -R ./shotsSelf-update
gainmap updateAgent 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/gainmapsThe 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; acceptstext,typeClassName,intensityultra-fill-canvas.tsx— the WebGPU canvas rectangleultra-fill.ts—startUltraFill(canvas, { intensity }): WebGPU session, 1×1 rgba16float surfacetext-ultra.ts— constants and helpers includingTEXT_ULTRA_FOUNDATION_RATIO = 0.75,foundationHeadroomFor(), andTEXT_ULTRA_INTENSITY = 4.0ultra-overlay.ts—ultraOverlayGeometry()for the SVG bleedultra.css— CSS gate: hidden by default, shown whenhtml[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 gainmapencodeRgbaToUltraHdrJpeg
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 = {}
): GainMapEncodeResultGainMapEncodeOptions
| Option | Type | Description |
|---|---|---|
| boost | number (0–1) | UI boost level. 0.5 = default photo headroom (~3.34×). |
| headroom | number | Explicit headroom multiplier; overrides boost when set. |
| quality | number (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"