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.”
Shared raster engine · measured TIFF depth
From display TIFFs to native scientific and whole-slide rasters.
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.
| Library | Browser | BigTIFF | Tiles | Region | Scientific raster | OME / WSI | Decode coverage |
|---|---|---|---|---|---|---|---|
| PureJsImagebenchmark snapshot · a1f20da · Strict TypeScript | Yes | Yes | Yes | Yes | Yes | Yes | 104/106 decoded57 exact2 oracle failures |
| GeoTIFF.js3.0.5 · Pure JavaScript | Yes | Partial | Yes | Yes | Yes | No | 84/106 decoded32 exact11 unsupported · 7 errors · 2 oracle failures · 2 crashes |
| UTIF.js (utif2)4.1.0 · Pure JavaScript | Yes | No | Yes | No | Partial | No | 74/106 decoded49 exact28 errors · 2 oracle failures · 2 timeouts · 3 crashes |
| image-js/tiff7.1.3 · Pure JavaScript | Yes | No | Yes | No | Yes | No | 41/106 decoded27 exact51 unsupported · 12 errors · 2 oracle failures |
| image-js1.7.0 · Pure JavaScript | Yes | No | Yes | No | Partial | No | 39/106 decoded33 exact51 unsupported · 14 errors · 2 oracle failures |
| Jimp1.6.0 · Pure JavaScript | Yes | No | Yes | No | No | No | 74/106 decoded49 exact28 errors · 2 oracle failures · 2 timeouts · 3 crashes |
| Sharp / libvips0.35.3 · Native wrapper | No | Partial | Yes | Partial | Partial | No | Not run |
Measured 2026-08-10 with v24.16.0 on linux/x64. Methodology, caveats, versions, and evidence.
Capability matrix
Grouped by TIFF workflow.
TIFF structure
| Capability | PureJsImagebenchmark snapshot · a1f20da | GeoTIFF.js3.0.5 | UTIF.js (utif2)4.1.0 | image-js/tiff7.1.3 | image-js1.7.0 | Jimp1.6.0 | Sharp / libvips0.35.3 |
|---|---|---|---|---|---|---|---|
| Classic TIFF | Yes [source] | Yes [source] | Yes [source] | Yes [source] | Yes [source] | Yes [source] | Yes [source] |
| BigTIFF | Yes [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] |
| Strips | Yes [source] | Yes [source] | Yes [source] | Yes [source] | Yes [source] | Yes [source] | Yes [source] |
| Tiles | Yes [source] | Yes [source] | Yes [source] | Yes [source] | Yes [source] | Yes [source] | Yes [source] |
| Planar samples | Yes [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-page | Yes [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] |
| SubIFDs | Yes [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 selection | Yes [source] | YesAutomatic overview selection. [source] | NoNo pyramid selection API. [source] | No [source] | No [source] | No [source] | YesMulti-level TIFF input/output support. [source] |
| Region decode | Yes [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 decode | Yes [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
| Capability | PureJsImagebenchmark snapshot · a1f20da | GeoTIFF.js3.0.5 | UTIF.js (utif2)4.1.0 | image-js/tiff7.1.3 | image-js1.7.0 | Jimp1.6.0 | Sharp / libvips0.35.3 |
|---|---|---|---|---|---|---|---|
| Arbitrary N-channel raster | YesNative 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 samples | Yes1 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 samples | YesSigned 8/16-bit native output. [source] | Yes [source] | Not verified | No [source] | No [source] | No [source] | PartialNative libvips formats exist; TIFF loader behavior is build-dependent. [source] |
| Float16 | Yes [source] | Yes [source] | Not verified | No [source] | No [source] | No [source] | PartialDepends on libvips loader support. [source] |
| Float32 | Yes [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] |
| Float64 | Yes [source] | Yes [source] | Not verified | Yes [source] | NoThe adapter explicitly rejects float TIFF data. [source] | No [source] | Yes [source] |
| High bit depths | YesNative 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 output | Yes [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
| Capability | PureJsImagebenchmark snapshot · a1f20da | GeoTIFF.js3.0.5 | UTIF.js (utif2)4.1.0 | image-js/tiff7.1.3 | image-js1.7.0 | Jimp1.6.0 | Sharp / libvips0.35.3 |
|---|---|---|---|---|---|---|---|
| Grayscale | Yes [source] | Yes [source] | Yes [source] | Yes [source] | Yes [source] | Yes [source] | Yes [source] |
| RGB | Yes [source] | Yes [source] | Yes [source] | Yes [source] | Yes [source] | Yes [source] | Yes [source] |
| RGBA / alpha | Yes [source] | PartialreadRGB can expose a declared alpha extra sample. [source] | Yes [source] | Yes [source] | Yes [source] | Yes [source] | Yes [source] |
| Palette | Yes [source] | Yes [source] | Yes [source] | Yes [source] | Yes [source] | Yes [source] | Yes [source] |
| CMYK | YesNumeric 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] |
| YCbCr | Yes [source] | Yes [source] | PartialDisplay conversion exists for supported layouts. [source] | No [source] | No [source] | PartialOnly conversion to RGBA8. [source] | Yes [source] |
| CIELab | Yes [source] | Yes [source] | PartialDisplay conversion exists for a subset. [source] | No [source] | No [source] | PartialOnly conversion to RGBA8. [source] | Yes [source] |
| ICC conversion | PartialRGB 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
| Capability | PureJsImagebenchmark snapshot · a1f20da | GeoTIFF.js3.0.5 | UTIF.js (utif2)4.1.0 | image-js/tiff7.1.3 | image-js1.7.0 | Jimp1.6.0 | Sharp / libvips0.35.3 |
|---|---|---|---|---|---|---|---|
| None | Yes [source] | Yes [source] | Yes [source] | Yes [source] | Yes [source] | Yes [source] | Yes [source] |
| PackBits | Yes [source] | Yes [source] | Yes [source] | No [source] | No [source] | Yes [source] | PartialDepends on the packaged libtiff build. [source] |
| LZW | Yes [source] | Yes [source] | Yes [source] | Yes [source] | Yes [source] | Yes [source] | Yes [source] |
| Deflate | Yes [source] | Yes [source] | Yes [source] | Yes [source] | Yes [source] | Yes [source] | Yes [source] |
| Zstandard | Yes [source] | Yes [source] | No [source] | No [source] | No [source] | No [source] | PartialDepends on the packaged libtiff build. [source] |
| LERC | Yes [source] | Yes [source] | No [source] | No [source] | No [source] | No [source] | PartialDepends on the packaged libtiff build. [source] |
| CCITT MH / G3 / G4 | Yes [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-TIFF | Yes [source] | Yes [source] | Yes [source] | No [source] | No [source] | Yes [source] | Yes [source] |
| Old JPEG-in-TIFF | Yes [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-TIFF | YesAperio 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-TIFF | PartialRequires 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 / LogLuv | Yes [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
| Capability | PureJsImagebenchmark snapshot · a1f20da | GeoTIFF.js3.0.5 | UTIF.js (utif2)4.1.0 | image-js/tiff7.1.3 | image-js1.7.0 | Jimp1.6.0 | Sharp / libvips0.35.3 |
|---|---|---|---|---|---|---|---|
| OME-TIFF pixel decode | Yes [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 API | YesValidated 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 decode | Yes [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 API | Yes [source] | Yes [source] | No [source] | No [source] | No [source] | No [source] | NoNo geospatial coordinate API. [source] |
| Remote COG workflow | Yes [source] | Yes [source] | No [source] | No [source] | No [source] | No [source] | No [source] |
| Whole-slide pyramid pixels | Yes [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 API | Yes [source] | No [source] | No [source] | No [source] | No [source] | No [source] | No [source] |
| Aperio SVS profile | Yes [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 profiles | YesTyped 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
| Capability | PureJsImagebenchmark snapshot · a1f20da | GeoTIFF.js3.0.5 | UTIF.js (utif2)4.1.0 | image-js/tiff7.1.3 | image-js1.7.0 | Jimp1.6.0 | Sharp / libvips0.35.3 |
|---|---|---|---|---|---|---|---|
| Bounded region decoding | Yes [source] | Yes [source] | No [source] | No [source] | No [source] | No [source] | PartialDemand-driven libvips pipeline; loader behavior varies. [source] |
| Malformed-input bounds | Yes [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] |
Reproducible corpus run
Decode coverage, exact pixels, and failures.
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.
| Library | Attempted | RGBA compared | Decoded | Exact | Mismatch | Unsupported | Error | Oracle failure | Timeout | Crash | Native raster | Malformed rejected | Malformed accepted | Malformed timeout | Malformed crash |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| PureJsImagebenchmark snapshot · a1f20da | 154 | 106 | 104 | 57 | 47 | 0 | 0 | 2 | 0 | 0 | 44 | 4 | 0 | 0 | 0 |
| GeoTIFF.js3.0.5 | 154 | 106 | 84 | 32 | 52 | 11 | 7 | 2 | 0 | 2 | 44 | 1 | 3 | 0 | 0 |
| UTIF.js (utif2)4.1.0 | 154 | 106 | 74 | 49 | 25 | 0 | 28 | 2 | 2 | 3 | 44 | 0 | 1 | 0 | 3 |
| image-js/tiff7.1.3 | 154 | 106 | 41 | 27 | 14 | 51 | 12 | 2 | 0 | 0 | 44 | 1 | 3 | 0 | 0 |
| image-js1.7.0 | 154 | 106 | 39 | 33 | 6 | 51 | 14 | 2 | 0 | 0 | 44 | 1 | 3 | 0 | 0 |
| Jimp1.6.0 | 154 | 106 | 74 | 49 | 25 | 0 | 28 | 2 | 2 | 3 | 44 | 0 | 1 | 0 | 3 |
Methodology
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.
Sources
Versioned evidence
- PureJsImage conformance snapshot. Main snapshot a1f20da; clean working tree; unreleased; package metadata 0.9.0.
- Pinned 154-file cross-library TIFF conformance report. Source
- PureJsImage generated TIFF capability contract and focused evidence. Source
- PureJsImage TIFF document, raster, source, and profile APIs. Source
- GeoTIFF.js 3.0.5 features and API documentation. Source
- GeoTIFF.js readRGB photometric conversion source. Source
- UTIF.js API and supported compression documentation used by utif2 4.1.0. Source
- UTIF.js strip, tile, planar, and sample conversion source used by utif2 4.1.0. Source
- image-js/tiff 7.1.3 compatibility and native output documentation. Source
- image-js/tiff 7.1.3 decoder source. Source
- image-js 1.7.0 TIFF and TIFF-stack adapters. Source
- Jimp 1.6.0 TIFF adapter backed by utif2. Source
- Sharp 0.35.3 input, raw pixel, multi-page, and libvips documentation. Source
- 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.