API reference

Public API and runtime contracts.

Codec registration, immutable image pipelines, operation options, sources, sinks, safety limits, metadata preservation, and typed errors.

Current mainTypeScript declarations included

ImageLibrary

Create a frozen library with the codec implementations your application accepts. Registration order is preserved in formats(); duplicate format names are rejected.

Signature
function createImageLibrary(registration: Iterable<ImageCodec> | ImageLibraryConfiguration): ImageLibrary

interface ImageLibrary {
  formats(): readonly string[]
  open(input: ImageInput, options?: ImageOpenOptions): Promise<Image>
}

interface ImageOpenOptions {
  frame?: number
  resolutionLevel?: number
  tolerantDecoding?: boolean
  limits?: ImageLimitOptions
}

formats()

Returns the format names registered in this library.

returns readonly string[]

open(input, options?)

Creates a source, validates optional zero-based frame and resolution-level selections, enforces the input-byte limit, probes registered codecs, and returns a lazy image pipeline. TIFF supports explicit top-level frame selection and resolutionLevel selection, where level 0 is the full-resolution frame and later levels are reduced-resolution SubIFDs ordered from largest to smallest. Codecs reject nonzero selections they do not support. Static GIFs decode without a selection; animated GIF metadata remains available, but pixel output requires an explicit { frame: 0 } to prevent silent animation loss. Baseline JPEG restart recovery defaults to tolerantDecoding: true for compatibility with malformed real-world files. Set { tolerantDecoding: false } to require exact restart sequencing and permit an explicitly registered JPEG decode accelerator.

returns Promise<Image>

Codec imports

EntryNamed export
purejsimage/codecs/jpegjpegCodec
purejsimage/codecs/jpeg2000jpeg2000Codec
purejsimage/codecs/pngpngCodec
purejsimage/codecs/gifgifCodec
purejsimage/codecs/icoicoCodec
purejsimage/codecs/webpwebpCodec
purejsimage/codecs/bmpbmpCodec
purejsimage/codecs/tifftiffCodec
purejsimage/codecs/avifavifCodec
purejsimage/codecs/experimental/heicexperimentalHeicCodec, experimentalHeifCodec
purejsimage/codecs/allallCodecs

allCodecs contains the nine default codecs and intentionally excludes experimental HEIF/HEIC.

Experimental HEIC opt-in

TypeScript
import { createImageLibrary } from 'purejsimage'
import { allCodecs } from 'purejsimage/codecs/all'
import { experimentalHeicCodec } from 'purejsimage/codecs/experimental/heic'

const images = createImageLibrary({
  codecs: [...allCodecs, experimentalHeicCodec],
})
Experimental and opt-in. HEIC commonly contains HEVC/H.265 image data that may be subject to third-party patent rights. PureJsImage's MIT license covers this implementation's copyright and grants no third-party patent rights. Users and distributors are responsible for determining whether their use requires additional licenses, particularly for commercial products, services, or high-volume distribution. The codec is shipped in the package but is never loaded by the root entry, allCodecs, or the default browser demo. See the experimental HEIF/HEIC contract.

TIFF documents, rasters, and profiles

purejsimage/tiff exposes the validated TIFF IFD graph independently of the display-image pipeline. openTiffDocument(source, options?) returns a TiffDocument with stable top-level and SubIFD directory objects. Each TiffDirectory exposes its absolute source offset; getDirectoryByOffset(offset) resolves parsed metadata pointers. A directory reads bounded, cached public tag values and creates either an ImageDecoder for display pixels or a RasterDecoder for native-precision, arbitrary-channel samples. readBytes(offset, length, { maxBytes }) performs an exact bounded private-metadata read and returns a defensive copy without exposing the underlying ImageSource.

Scientific OME-TIFF
import { MemorySource } from 'purejsimage'
import { openOmeTiff } from 'purejsimage/scientific'
import { openTiffDocument } from 'purejsimage/tiff'

const document = await openTiffDocument(new MemorySource(bytes))
const dataset = await openOmeTiff(document)
for await (const block of dataset.readPlane({
  z: 0, c: [0, 1, 2], t: 0, resolutionLevel: 0,
  x: 0, y: 0, width: 512, height: 512,
})) {
  consume(block)
}

openOmeTiff() validates OME dimensions, sample type, channel metadata, physical pixel size, dimension order, and explicit or implicit TiffData mappings before exposing a MultidimensionalRasterDataset. readPlane() preserves native sample types and can combine separately stored channel planes without materializing a source-sized display bitmap.

purejsimage/pathology exports openAperioSvs(), aperioSvsProfile, and the generic WholeSlideImage contract. Aperio levels and label, macro, or thumbnail images decode by requested region. TIFF compression tags 33003 and 33005 use the reusable createJpeg2000CodestreamDecoder(); JPEG 2000 reconstruction remains bounded to the current TIFF tile or strip, though the current codestream decoder retains that segment's complete component state.

Third-party TIFF profile authoring

A separately shipped integration implements TiffProfile<T> using only package exports. Register profiles with createTiffProfileRegistry(). Detection runs every profile in isolation, orders matches by descending integer priority then id, reports detector failures, and rejects equal-priority matches as ambiguous. For an explicitly selected profile, registry.openWith(document, profile) preserves T and returns Promise<T>.

Vendor profile
import { createTiffProfileRegistry } from 'purejsimage/tiff'
import type { TiffProfile } from 'purejsimage/tiff'

export const vendorProfile: TiffProfile<VendorDataset> = {
  id: 'vendor-format',
  priority: 50,
  async detect({ document }) {
    const tag = await document.topLevelDirectories[0]?.getTag(270, {
      maxBytes: 1024 * 1024,
    })
    return tag?.kind === 'ascii' && tag.value.startsWith('Vendor')
  },
  async open({ document }) {
    const pointer = await document.topLevelDirectories[0]?.getTag(65000, {
      maxBytes: 24,
    })
    if (pointer?.kind !== 'numbers') throw new Error('Vendor pointer is missing')
    const directory = document.getDirectoryByOffset(pointer.values[0] ?? -1)
    if (!directory) throw new Error('Vendor IFD is missing')
    const length = pointer.values[2] ?? -1
    const metadata = await document.readBytes(
      pointer.values[1] ?? -1, length, { maxBytes: 1024 * 1024 },
    )
    return openVendorDataset(directory, metadata)
  },
}

const registry = createTiffProfileRegistry([vendorProfile])
const dataset = await registry.openWith(document, vendorProfile)
Public boundary. Profile code must bound every metadata read with getTag(tag, { maxBytes }) or readBytes(offset, length, { maxBytes }), validate metadata-to-IFD mappings before decoding, and retain structured unsupported errors for ambiguous vendor semantics. Tag values are cached, but each caller's limit is checked before cache access; byte-valued tags and raw reads return defensive copies. The checked examples/tiff-profile-leica driver compiles as an external package and imports only purejsimage, purejsimage/tiff, and purejsimage/pathology.

See the dedicated TIFF guide for the complete supported-format list, scientific and whole-slide workflows, output profile, memory model, and current limitations →

Zstandard decompression

The portable purejsimage/compression/zstd subpath exports the reusable first-party decoder used by TIFF Compression=50000. It has no format-specific parameters and does not load native code, WebAssembly, or a runtime dependency.

TypeScript
import { decodeZstd } from 'purejsimage/compression/zstd'

const output = decodeZstd(compressed, {
  expectedOutputBytes: 8192,
  maxOutputBytes: 8192,
  maxWindowBytes: 64 * 1024 * 1024,
})

maxOutputBytes and maxWindowBytes default to 64 MiB. Supplying expectedOutputBytes preallocates the exact bounded output and rejects a different decoded size. Dictionary-dependent frames fail with UNSUPPORTED_OPERATION.

Optional WASM acceleration

JPEG and PNG provide first-party WebAssembly accelerators as explicit package entries. The root, browser, and codec imports never load them automatically. Register either provider through ImageLibraryConfiguration; ineligible or unavailable work continues through the corresponding TypeScript codec.

Node.js
import { createImageLibrary } from 'purejsimage'
import { wasmJpegAccelerator } from 'purejsimage/accelerators/wasm/jpeg'
import { wasmPngAccelerator } from 'purejsimage/accelerators/wasm/png'
import { allCodecs } from 'purejsimage/codecs/all'

const images = createImageLibrary({
  codecs: allCodecs,
  accelerators: [wasmJpegAccelerator, wasmPngAccelerator],
})

Browser applications use createImageLibrary from purejsimage/browser; the two accelerator import paths are unchanged and resolve to browser-safe loaders.

Configuration

The ready-made providers use defaults chosen to avoid paying module and copy overhead on small images. Use the factory exports when an application needs different thresholds or input limits.

TypeScript
interface WasmJpegAcceleratorOptions {
  minimumPixels?: number
  minimumEncodePixels?: number
  maximumInputBytes?: number
  maximumEncoderRowBytes?: number
}

interface WasmPngAcceleratorOptions {
  minimumPixels?: number
  minimumEncodePixels?: number
  maximumRowBytes?: number
}

function createWasmJpegAccelerator(options?: WasmJpegAcceleratorOptions): ImageCodecAccelerator
function createWasmPngAccelerator(options?: WasmPngAcceleratorOptions): ImageCodecAccelerator

Both pixel thresholds default to 65,536. JPEG accepts at most 32 MiB of compressed input and a 16 MiB encoder row by default; PNG accepts at most a 16 MiB row. Every supplied value must be a positive integer.

Eligible workflows

ProviderAccelerated workTypeScript fallback examples
JPEGCommon full-image baseline YCbCr decode and baseline gray8, rgb8, or rgba8 encodeProgressive output, crop or scaled decode, ICC-transformed decode, metadata-preserving output, small images, and inputs beyond configured limits
PNGFull-image, non-interlaced 8-bit grayscale, RGB, or RGBA decode without tRNS; adaptive-filter gray8, rgb8, or rgba8 encodePalette, grayscale-alpha, sub-byte, 16-bit, tRNS, Adam7, APNG frames, crop, compression level 0, small images, and rows beyond configured limits
Runtime behavior. Modules load lazily and are reused. SIMD is preferred when the runtime validates it, with scalar WASM and then TypeScript as fallbacks. Node retains native zlib; browsers retain platform compression streams. Codec parsing, metadata, limits, CRC validation, sources, and sinks remain in JavaScript, and pixel processing remains bounded by rows or blocks.

Image

Every transform returns a new immutable Image sharing the same input context. No pixel work occurs until an output method is called.

metadata()

Reads source metadata and applies planned transforms to width, height, format, alpha, and orientation fields without executing the pixel pipeline.

returns Promise<ImageMetadata>

autoOrient()

Applies EXIF orientations 1–8 when present. Orientations that reorder rows use bounded-memory temporary tile storage.

returns Image

keepExif()

Opts into retaining source EXIF in a compatible output. EXIF is stripped by default; pixel reorientation normalizes a retained orientation tag to 1.

returns Image

keepIcc()

Opts into retaining a compatible source ICC profile instead of converting tagged samples and stripping the profile.

returns Image

crop(options)

Crops to an integer rectangle. x and y may be zero; width and height must be positive. Out-of-bounds crops fail.

returns Image

resize(options)

Resizes by one dimension or two. Supports contain, cover, fill, inside, and outside fits. Lanczos 3 is the default; nearest and bilinear sampling are available for explicit lower-cost resizing.

returns Image

window(options)

Maps numeric grayscale samples to gray8 through an explicit center and width, overriding the source's normal display range for this pipeline.

returns Image

lut(options)

Applies a 256-entry lookup table. gray8 input may produce gray8, rgb8, or rgba8; rgba8 input uses independent interleaved channel tables and remains rgba8.

returns Image

rotate(degrees, options?)

Rotates clockwise by any finite number of degrees. Quarter turns are exact; other angles use bilinear sampling and expand the canvas.

returns Image

flip()

Mirrors pixels vertically, from top to bottom.

returns Image

flop()

Mirrors pixels horizontally, from left to right.

returns Image

jpeg(options?)

Appends JPEG output. Quality is 1–100; background accepts transparent, #RRGGBB, or #RRGGBBAA; chroma subsampling is 420, 422, or 444.

returns Image

png(options?)

Appends 8-bit PNG output. Node supports compression levels 0–9; browser CompressionStream output currently supports the default level 6.

returns Image

webp(options?)

Appends static lossy or lossless WebP output. Lossy quality is 1–100. For lossless output prefer png(); WebP lossless is not yet size-competitive.

returns Image

bmp(options?)

Appends BMP output. The alpha option selects 32-bit RGBA or 24-bit RGB output.

returns Image

tiff(options?)

Appends canonical Classic TIFF output: little-endian, chunky 8-bit RGB or RGBA, independently Deflate-compressed strips, and horizontal prediction. Node supports compression levels 0–9; browser CompressionStream output supports level 6.

returns Image

encode(format, options?)

Generic equivalent to the format-specific encoder methods for jpeg, png, webp, bmp, and tiff.

returns Image

toBuffer(options?)

Executes the pipeline and collects encoded chunks into Node.js bytes. The runtime value remains a Buffer, exposed through the portable Uint8Array public type so consumers do not need Node ambient declarations.

returns Promise<Uint8Array>

toUint8Array(options?)

Executes and returns portable encoded bytes. Available in Node and browsers.

returns Promise<Uint8Array>

toBlob(options?)

Executes and returns a Blob with the registered output codec MIME type. Intended for browser previews, uploads, and downloads.

returns Promise<Blob>

toSink(sink, options?)

Executes into a custom ImageSink for streaming or application-owned output.

returns Promise<void>

toFile(path, options?)

Node-only file output. If encoding fails or is cancelled, the partial output file is removed.

returns Promise<void>
Progressive output. Set progressive: true to emit a refinement-based progressive JPEG. This retains compact quantized coefficient planes until all scans are written; baseline output remains the bounded-row default.

Operation options

CropOptions

TypeScript
interface CropOptions {
  x: number
  y: number
  width: number
  height: number
}

ResizeOptions

TypeScript
type ResizeFit = 'contain' | 'cover' | 'fill' | 'inside' | 'outside'
type ResizeKernel = 'nearest' | 'bilinear' | 'lanczos3'
type Background = 'transparent' | `#${string}`

type ResizeOptions = {
  width?: number
  height?: number
  fit?: ResizeFit
  position?: 'center'
  background?: Background
  withoutEnlargement?: boolean
  kernel?: ResizeKernel
} // width or height is required
OptionDefaultNotes
fitcover with two dimensionsFit, position, and background require both width and height.
positioncenterCurrently center only; valid with contain.
backgroundtransparentValid with contain. Accepts six- or eight-digit hex.
kernellanczos3Lanczos 3 minimizes downscale aliasing. Bilinear is faster but lower quality; nearest preserves hard pixel edges.
withoutEnlargementfalseCaps the scale at 1 where the fit permits it.

WindowOptions and LutOptions

TypeScript
interface WindowOptions {
  center: number
  width: number
}

interface LutOptions {
  table: Uint8Array // 256 × output channel count, interleaved
  format: 'gray8' | 'rgb8' | 'rgba8'
}

Windowing precedes geometric transforms and converts native numeric grayscale blocks directly to gray8. A grayscale LUT treats each table entry as an output color. An RGBA LUT maps each input channel through the corresponding interleaved channel table.

RotateOptions

TypeScript
interface RotateOptions {
  background?: Background // transparent by default
}

Positive degrees rotate clockwise. Arbitrary angles expand to the smallest integer canvas that contains the result and use the requested six- or eight-digit hex background, or transparency by default.

Encoder options

TypeScript
interface JpegEncodeOptions {
  quality?: number                // 1–100
  progressive?: boolean          // refinement-based output; false by default
  background?: Background
  chromaSubsampling?: '420' | '422' | '444'
  restartInterval?: number       // 0–65535; 0 disables restart markers
}

interface PngEncodeOptions { compressionLevel?: number } // 0–9
interface WebpEncodeOptions { lossless?: boolean; quality?: number }
interface BmpEncodeOptions { alpha?: boolean }
interface TiffEncodeOptions {
  compression?: 'deflate'
  predictor?: 'horizontal'
  layout?: 'strips'
  compressionLevel?: number       // 0–9; browser supports 6
}

EXIF and ICC preservation

Encoders strip EXIF and ICC metadata unless the pipeline calls keepExif() or keepIcc(). The two choices are independent and apply when the pipeline executes.

FormatRead for preservationWrite on output
JPEGEXIF and RGB ICCEXIF and RGB ICC
PNGEXIF and compatible ICCEXIF and compatible ICC
WebPEXIF and RGB ICCEXIF and RGB ICC
HEIF / HEIC (experimental)EXIF and RGB ICCNot implemented
TIFFCompatible ICC onlyCompatible ICC only
Other formatsNot implementedNot implemented

With keepIcc(), tagged samples remain in their source color space and the original profile is embedded in the output, avoiding a double conversion. The profile must match the output pixel model. With keepExif(), autoOrient(), rotate(), flip(), and flop() normalize a retained EXIF orientation to 1 because the pixels have already moved.

Explicit capability boundary. Asking for preservation across an unsupported source or output combination throws an ImageError instead of silently dropping requested metadata.

ImageMetadata

TypeScript
interface ImageMetadata {
  width: number
  height: number
  format: string
  mimeType: string
  hasAlpha: boolean
  orientation?: number
  colorSpace?: string
  colorProfile?:
    | { kind: 'icc'; description?: string }
    | {
        kind: 'nclx'
        primaries: number
        transferCharacteristics: number
        matrixCoefficients: number
        fullRange: boolean
      }
  bitDepth?: number
  chromaSubsampling?: '400' | '411' | '420' | '422' | '440' | '444'
  codecProfile?: number
  frames?: number
}

Optional fields depend on what the container and registered codec can determine safely. HEIF and AVIF expose validated ICC descriptions when present or the numeric nclx primaries, transfer characteristics, matrix coefficients, and range. Animated formats may report frame counts even when the current pixel decoder handles only a still subset.

Inputs & sources

TypeScript
type BrowserImageInput = ArrayBuffer | Blob | ImageSource | Uint8Array
type NodeImageInput = BrowserImageInput | string

interface ImageSource {
  readonly size: number
  read(offset: number, length: number, options?: { signal?: AbortSignal }): Promise<Uint8Array>
}

MemorySource

Zero-copy reads over an ArrayBuffer or Uint8Array.

BlobSource

Range reads through Blob.slice().

FileSource

Node-only. Use await FileSource.open(path) to build a file-backed source explicitly.

HttpRangeSource

Optional bounded HTTP range reads with request and byte statistics. Import from purejsimage/sources/http-range. A per-read signal is combined with the source-lifetime signal and cancels an in-flight fetch.

For an in-range read, custom read() implementations must return exactly min(length, size - offset) bytes as a Uint8Array, or reject when the backing read fails. Returned bytes may be reused or invalidated when the next read begins. Short, oversized, detached, and rejected reads surface as ImageError results. Public open, metadata, decode, raster, whole-slide, source-read, and terminal output options accept an optional AbortSignal; cancellation aborts the active sink and is rethrown as AbortError.

Safety limits

Pass a partial limits object to open(). Every provided value must be a positive safe integer.

LimitDefaultEnforcement
maxWidth100,000Declared and transformed width
maxHeight100,000Declared and transformed height
maxPixels268,435,456Width × height using overflow-safe arithmetic
maxInputBytes134,217,728 (128 MiB)Before source contents are read
maxFrames1,000Declared frame count
maxDecodedBytes1,073,741,824 (1 GiB)Worst-case dimensions and streaming expansion
Configure application limits. The defaults are general guardrails, not a recommendation that every deployment accept 100,000-pixel dimensions or 128 MiB inputs.

ImageError

TypeScript
type ImageErrorCode =
  | 'INVALID_INPUT'
  | 'LIMIT_EXCEEDED'
  | 'TRUNCATED_INPUT'
  | 'UNSUPPORTED_FORMAT'
  | 'UNSUPPORTED_OPERATION'
CodeMeaning
INVALID_INPUTMalformed image structure, invalid options, dimensions, or source contract.
LIMIT_EXCEEDEDA configured input, dimension, frame, decoded-byte, or temporary-storage budget was exceeded.
TRUNCATED_INPUTThe source ended before the codec could read required bytes or pixels.
UNSUPPORTED_FORMATThe format is unknown or its codec was not registered.
UNSUPPORTED_OPERATIONThe format is recognized, but the requested coding feature, decode subset, or encoder is not implemented.

Advanced contracts

The Node root and browser entry export the primitives used to author custom codecs, accelerators, and sinks: ImageCodec, ImageCodecAccelerator, ImageLibraryConfiguration, ImageDecoder, ImageEncoder, CodecRegistry, ImageSink, Uint8ArraySink, PixelBlock, PixelFormat, and BufferPool. Node additionally exports BufferSink, FileSink, and FileSource.

These interfaces support custom codecs and sinks. A decoder yields ordered pixel blocks; an encoder consumes them and writes encoded chunks to a sink. Implementations must preserve the release callback and source-buffer lifetime contracts.

View public exports on GitHub →