Evidence-backed TIFF comparison

JavaScript TIFF libraries compared by capability and measured output.

Documentation claims and pixel conformance are separate signals. “Not verified” means exactly that—not “unsupported.”

From display TIFFs to native scientific and whole-slide rasters.

Full comparison →

TIFF demonstrates how the same codec architecture serves ordinary conversion and specialized raster workloads. Capability cells follow upstream documentation or source; a separate 106-fixture RGBA comparison reports decode coverage, exact output, scientific rasters, and malformed inputs.

LibraryBrowserBigTIFFTilesRegionScientific rasterOME / WSIDecode coverage
PureJsImagebenchmark snapshot · a1f20da · Strict TypeScriptYesYesYesYesYesYes104/106 decoded57 exact2 oracle failures
GeoTIFF.js3.0.5 · Pure JavaScriptYesPartialYesYesYesNo84/106 decoded32 exact11 unsupported · 7 errors · 2 oracle failures · 2 crashes
UTIF.js (utif2)4.1.0 · Pure JavaScriptYesNoYesNoPartialNo74/106 decoded49 exact28 errors · 2 oracle failures · 2 timeouts · 3 crashes
image-js/tiff7.1.3 · Pure JavaScriptYesNoYesNoYesNo41/106 decoded27 exact51 unsupported · 12 errors · 2 oracle failures
image-js1.7.0 · Pure JavaScriptYesNoYesNoPartialNo39/106 decoded33 exact51 unsupported · 14 errors · 2 oracle failures
Jimp1.6.0 · Pure JavaScriptYesNoYesNoNoNo74/106 decoded49 exact28 errors · 2 oracle failures · 2 timeouts · 3 crashes
Sharp / libvips0.35.3 · Native wrapperNoPartialYesPartialPartialNoNot run

Measured 2026-08-10 with v24.16.0 on linux/x64. Methodology, caveats, versions, and evidence.

Grouped by TIFF workflow.

TIFF structure

CapabilityPureJsImagebenchmark snapshot · a1f20daGeoTIFF.js3.0.5UTIF.js (utif2)4.1.0image-js/tiff7.1.3image-js1.7.0Jimp1.6.0Sharp / libvips0.35.3
Classic TIFFYes [source]Yes [source]Yes [source]Yes [source]Yes [source]Yes [source]Yes [source]
BigTIFFYes [source]PartialUpstream documents limited BigTIFF support. [source]NoThe tested BigTIFF files did not expose usable dimensions. [source]No [source]No [source]No [source]PartialProvided by the installed libvips/libtiff build. [source]
StripsYes [source]Yes [source]Yes [source]Yes [source]Yes [source]Yes [source]Yes [source]
TilesYes [source]Yes [source]Yes [source]Yes [source]Yes [source]Yes [source]Yes [source]
Planar samplesYes [source]YesBand- and pixel-interleaved raster output. [source]NoThe decoder warns that PlanarConfiguration 2 should not be used. [source]NoPlanarConfiguration is parsed but not used by the decoder. [source]No [source]No [source]PartialHandled through libvips when supported by its loaders. [source]
Multi-pageYes [source]YesIndexed image access. [source]Yesdecode() returns an array of IFDs. [source]Yes [source]YesdecodeStackFromTiff exposes all top-level pages. [source]NoThe adapter returns only the first page. [source]Yes [source]
SubIFDsYes [source]PartialOverview selection is documented; arbitrary SubIFD graph semantics are not. [source]PartialTags are exposed, but no validated SubIFD graph API is documented. [source]NoOnly EXIF/GPS child IFDs receive dedicated traversal. [source]No [source]No [source]PartialMulti-level loading is available without a public TIFF graph API. [source]
Pyramid selectionYes [source]YesAutomatic overview selection. [source]NoNo pyramid selection API. [source]No [source]No [source]No [source]YesMulti-level TIFF input/output support. [source]
Region decodeYes [source]YesWindow, bounding-box, and sample selection. [source]NodecodeImage materializes the selected IFD. [source]No [source]No [source]No [source]Partialextract() exists; decoder-level boundedness is loader-dependent. [source]
Remote range-backed decodeYes [source]Yes [source]NoInput is an ArrayBuffer. [source]No [source]No [source]No [source]NoNo TIFF-specific HTTP range source API. [source]

Pixel and scientific data

CapabilityPureJsImagebenchmark snapshot · a1f20daGeoTIFF.js3.0.5UTIF.js (utif2)4.1.0image-js/tiff7.1.3image-js1.7.0Jimp1.6.0Sharp / libvips0.35.3
Arbitrary N-channel rasterYesNative RasterBlock output; display mapping is explicit. [source]YesSelected samples as planar or interleaved typed arrays. [source]PartialDecoded sample bytes are exposed, without a typed N-channel raster contract. [source]NoDecoder contract is grayscale/RGB with optional alpha. [source]No [source]NoTIFF is converted to RGBA8. [source]PartialRaw channels are exposed within libvips image-band constraints. [source]
Unsigned integer samplesYes1 through 64-bit subsets documented by pixel family. [source]YesUInt1-32, with documented drawbacks below 32 bits. [source]Yes [source]Yes8- and 16-bit integer output. [source]YesSupported output is mapped into Image bit-depth contracts. [source]PartialSupported inputs are normalized to RGBA8. [source]Yes [source]
Signed integer samplesYesSigned 8/16-bit native output. [source]Yes [source]Not verifiedNo [source]No [source]No [source]PartialNative libvips formats exist; TIFF loader behavior is build-dependent. [source]
Float16Yes [source]Yes [source]Not verifiedNo [source]No [source]No [source]PartialDepends on libvips loader support. [source]
Float32Yes [source]Yes [source]PartialFloat32 display conversion exists; native typed semantics are not documented. [source]Yes [source]NoThe adapter explicitly rejects float TIFF data. [source]PartialSome float display conversion; no native precision output. [source]Yes [source]
Float64Yes [source]Yes [source]Not verifiedYes [source]NoThe adapter explicitly rejects float TIFF data. [source]No [source]Yes [source]
High bit depthsYesNative 16/24/32/64-bit output where applicable. [source]Yes [source]PartialVarious depths decode, but toRGBA8 normalizes output. [source]Partial16-bit unsigned and 32/64-bit float output. [source]Partial8- and 16-bit Image output. [source]PartialNormalized to RGBA8. [source]Yes [source]
Native-precision raster outputYes [source]Yes [source]PartialDecoded IFD data is exposed without a stable typed scientific-raster contract. [source]YesTyped IFD data arrays. [source]PartialTyped Image output, but float and arbitrary-channel TIFF are rejected. [source]No [source]PartialRaw output is available, but not a TIFF-native N-channel API. [source]

Color

CapabilityPureJsImagebenchmark snapshot · a1f20daGeoTIFF.js3.0.5UTIF.js (utif2)4.1.0image-js/tiff7.1.3image-js1.7.0Jimp1.6.0Sharp / libvips0.35.3
GrayscaleYes [source]Yes [source]Yes [source]Yes [source]Yes [source]Yes [source]Yes [source]
RGBYes [source]Yes [source]Yes [source]Yes [source]Yes [source]Yes [source]Yes [source]
RGBA / alphaYes [source]PartialreadRGB can expose a declared alpha extra sample. [source]Yes [source]Yes [source]Yes [source]Yes [source]Yes [source]
PaletteYes [source]Yes [source]Yes [source]Yes [source]Yes [source]Yes [source]Yes [source]
CMYKYesNumeric conversion plus bounded lut16 A2B0 ICC subset. [source]YesDeterministic numeric conversion. [source]PartialDisplay conversion exists for supported layouts. [source]No [source]No [source]PartialOnly conversion to RGBA8. [source]Yes [source]
YCbCrYes [source]Yes [source]PartialDisplay conversion exists for supported layouts. [source]No [source]No [source]PartialOnly conversion to RGBA8. [source]Yes [source]
CIELabYes [source]Yes [source]PartialDisplay conversion exists for a subset. [source]No [source]No [source]PartialOnly conversion to RGBA8. [source]Yes [source]
ICC conversionPartialRGB profiles and CMYK lut16 A2B0; not a general ICC engine. [source]NoICC tag parsing is present; readRGB does not apply the profile. [source]NoNo ICC transform is documented. [source]No [source]No [source]No [source]Yes [source]

Compression

CapabilityPureJsImagebenchmark snapshot · a1f20daGeoTIFF.js3.0.5UTIF.js (utif2)4.1.0image-js/tiff7.1.3image-js1.7.0Jimp1.6.0Sharp / libvips0.35.3
NoneYes [source]Yes [source]Yes [source]Yes [source]Yes [source]Yes [source]Yes [source]
PackBitsYes [source]Yes [source]Yes [source]No [source]No [source]Yes [source]PartialDepends on the packaged libtiff build. [source]
LZWYes [source]Yes [source]Yes [source]Yes [source]Yes [source]Yes [source]Yes [source]
DeflateYes [source]Yes [source]Yes [source]Yes [source]Yes [source]Yes [source]Yes [source]
ZstandardYes [source]Yes [source]No [source]No [source]No [source]No [source]PartialDepends on the packaged libtiff build. [source]
LERCYes [source]Yes [source]No [source]No [source]No [source]No [source]PartialDepends on the packaged libtiff build. [source]
CCITT MH / G3 / G4Yes [source]NoNot listed among enabled compression decoders. [source]Yes [source]No [source]No [source]Yes [source]PartialDepends on the packaged libtiff build. [source]
JPEG-in-TIFFYes [source]Yes [source]Yes [source]No [source]No [source]Yes [source]Yes [source]
Old JPEG-in-TIFFYes [source]NoExplicitly reported unsupported by the tested decoder. [source]YesCompression 6 is listed; the broad corpus still contains failures. [source]No [source]No [source]PartialDocumented by UTIF, but broad corpus cases failed. [source]PartialDepends on libtiff compatibility. [source]
JPEG 2000-in-TIFFYesAperio compression 33003/33005. [source]NoNo JPEG 2000 TIFF decoder is documented. [source]No [source]No [source]No [source]No [source]PartialDepends on installed libvips/OpenJPEG support. [source]
WebP-in-TIFFPartialRequires explicit WebP codec composition. [source]PartialUses browser-supported image formats where available. [source]No [source]No [source]No [source]No [source]PartialDepends on installed libtiff/libwebp support. [source]
SGILog / LogLuvYes [source]NoNo SGILog/LogLuv photometric conversion is exposed. [source]No [source]No [source]No [source]No [source]PartialDepends on the packaged libtiff build. [source]

Specialized TIFF ecosystems

CapabilityPureJsImagebenchmark snapshot · a1f20daGeoTIFF.js3.0.5UTIF.js (utif2)4.1.0image-js/tiff7.1.3image-js1.7.0Jimp1.6.0Sharp / libvips0.35.3
OME-TIFF pixel decodeYes [source]YesGeneric TIFF IFD/raster access. [source]PartialGeneric first-IFD pixels; no OME plane assembly. [source]PartialGeneric page decoding only. [source]PartialGeneric TIFF stack only. [source]PartialFirst displayable page only. [source]PartialGeneric pages/levels without OME dataset semantics. [source]
OME Z/C/T dataset APIYesValidated Z/C/T plane and metadata API. [source]NoNo OME XML or Z/C/T dataset API. [source]No [source]No [source]No [source]No [source]No [source]
GeoTIFF pixel decodeYes [source]Yes [source]PartialGeneric TIFF pixels only. [source]PartialGeneric TIFF pixels only. [source]PartialGeneric TIFF pixels and tag metadata only. [source]PartialFirst displayable page only. [source]Yes [source]
GeoTIFF geospatial APIYes [source]Yes [source]No [source]No [source]No [source]No [source]NoNo geospatial coordinate API. [source]
Remote COG workflowYes [source]Yes [source]No [source]No [source]No [source]No [source]No [source]
Whole-slide pyramid pixelsYes [source]PartialGeneric IFD and overview access without slide semantics. [source]PartialIndividual IFD decoding without a slide API. [source]PartialPages can be selected, without pyramid semantics. [source]PartialTop-level stack only. [source]PartialFirst displayable page only. [source]PartialLoader-dependent multi-level access. [source]
Whole-slide semantic APIYes [source]No [source]No [source]No [source]No [source]No [source]No [source]
Aperio SVS profileYes [source]PartialGeneric TIFF levels only; no Aperio profile API. [source]No [source]No [source]No [source]No [source]PartialOpenSlide/libvips support is installation-dependent. [source]
Extensible vendor profilesYesTyped registry; bundled Aperio profile and external Leica example. [source]NoCustom compression decoders exist, not vendor semantic profiles. [source]No [source]No [source]No [source]No [source]No [source]

Robustness and API

CapabilityPureJsImagebenchmark snapshot · a1f20daGeoTIFF.js3.0.5UTIF.js (utif2)4.1.0image-js/tiff7.1.3image-js1.7.0Jimp1.6.0Sharp / libvips0.35.3
Bounded region decodingYes [source]Yes [source]No [source]No [source]No [source]No [source]PartialDemand-driven libvips pipeline; loader behavior varies. [source]
Malformed-input boundsYes [source]PartialThe broad isolated run contains process crashes, and malformed inputs are not all rejected. [source]PartialThe broad isolated run contains timeouts and process crashes; malformed inputs are not all rejected. [source]PartialMalformed inputs were not all rejected, although no tested worker crashed. [source]PartialMalformed inputs were not all rejected, although no tested worker crashed. [source]PartialThe broad isolated run contains timeouts and process crashes; malformed inputs are not all rejected. [source]PartialNative loader validation; not exercised by this JavaScript worker report. [source]

Decode coverage, exact pixels, and failures.

Per-file report →

All six JavaScript engines were attempted in isolated child processes on 154 pinned files. The 106 display-image cases use sharp with ImageMagick fallback as the independent raw-RGBA8 oracle; decoded coverage is primary, while exact means every compared channel matched. Forty-four native scientific rasters are not forced through RGBA. Four malformed files test bounded rejection separately.

LibraryAttemptedRGBA comparedDecodedExactMismatchUnsupportedErrorOracle failureTimeoutCrashNative rasterMalformed rejectedMalformed acceptedMalformed timeoutMalformed crash
PureJsImagebenchmark snapshot · a1f20da154106104574700200444000
GeoTIFF.js3.0.5154106843252117202441300
UTIF.js (utif2)4.1.0154106744925028223440103
image-js/tiff7.1.31541064127145112200441300
image-js1.7.0154106393365114200441300
Jimp1.6.0154106744925028223440103

What these numbers do—and do not—mean.

  • Capability claims come from versioned upstream documentation or inspected source. Unknown means not verified, not unsupported.
  • The conformance harness uses pinned corpus files, isolated child processes, a fixed heap limit, a fixed timeout, and independent RGBA output.
  • The PureJsImage row is a clean benchmark snapshot at commit a1f20da06b7fecb7f17b23be7d73f22e16ed7998; 0.9.0 is package metadata, not a release claim. Other JavaScript rows are exact installed dev-dependency versions.
  • Signed, floating-point, wider-than-16-bit, and arbitrary-channel native rasters are not forced through an RGBA oracle.
  • Color-converted and lossy mismatches remain visible; exact equality is not used to claim one valid converter is universally better.
  • Run limits: 30 seconds and 512 MiB per child process, concurrency 2.
  • Environment: v24.16.0, linux/x64; report generated 2026-08-10T22:52:59.765Z.
  • Corpus directories: ../codec-corpus/tiff-conformance/valid, ../codec-corpus/tiff-conformance/edge-cases, ../codec-corpus/tiff-conformance/robustness.

A mismatch in a color-converted or lossy case is visible but is not automatically a decoder defect: compliant converters can differ in rounding, chroma reconstruction, ICC handling, and codec output. Native scientific rasters are reported by type rather than normalized into misleading RGBA. Malformed-input results are separated from valid-file support.

Versioned evidence

  1. PureJsImage conformance snapshot. Main snapshot a1f20da; clean working tree; unreleased; package metadata 0.9.0.
  2. Pinned 154-file cross-library TIFF conformance report. Source
  3. PureJsImage generated TIFF capability contract and focused evidence. Source
  4. PureJsImage TIFF document, raster, source, and profile APIs. Source
  5. GeoTIFF.js 3.0.5 features and API documentation. Source
  6. GeoTIFF.js readRGB photometric conversion source. Source
  7. UTIF.js API and supported compression documentation used by utif2 4.1.0. Source
  8. UTIF.js strip, tile, planar, and sample conversion source used by utif2 4.1.0. Source
  9. image-js/tiff 7.1.3 compatibility and native output documentation. Source
  10. image-js/tiff 7.1.3 decoder source. Source
  11. image-js 1.7.0 TIFF and TIFF-stack adapters. Source
  12. Jimp 1.6.0 TIFF adapter backed by utif2. Source
  13. Sharp 0.35.3 input, raw pixel, multi-page, and libvips documentation. Source
  14. Independent 154-file TIFF corpus sources and licenses. Source

Excluded or historical libraries

  • tiff.js: The legacy Emscripten/libtiff wrapper is not in the reproducible harness. The maintained pure-JavaScript image-js/tiff package is included instead.