WebP codec support
August 24, 2026 ยท View on GitHub
This document is the capability contract for PureJsImage's first-party WebP codec. A checked item is implemented in the current code. An unchecked item is planned and must not be presented as supported until its output is independently validated. The correctness section records the current fixture and benchmark coverage.
Decode
RIFF container and image layout
- RIFF
WEBPdetection and bounded chunk-extent parsing - Simple lossy WebP with a
VP8image chunk - Simple lossless WebP with a
VP8Limage chunk - Extended static WebP with a
VP8Xcanvas - Extended lossy WebP with a separate
ALPHchunk - RIFF odd-length chunk padding
- Canvas dimensions and alpha-presence metadata
- Animated-image detection and
ANMFframe counting - Explicit rejection of animated WebP pixel decode
- Animated WebP frame parsing, timing, blending, disposal, and canvas composition
- Complete
VP8Xreserved-bit, feature-flag, chunk-order, uniqueness, and chunk-dependency validation - Incremental RIFF parsing; metadata and pixel decode currently retain the complete compressed WebP input
Lossless VP8L pixels
- 8-bit ARGB lossless pixel reconstruction
- Normal and simple Huffman code representations
- Literal pixels
- LZ77 backward references and two-dimensional distance mapping
- Color caches
- Spatially varying Huffman entropy groups
- Predictor transform
- Color transform
- Subtract-green transform
- Color-indexing transform and packed palette indices
- Exact alpha reconstruction
- Bounded-row or tiled lossless reconstruction without retaining a full 32-bit source pixel plane
- Avoid duplicate full-frame storage while reversing color-indexing and other transforms
Lossy VP8 pixels
- Intra-coded VP8 key frames, bitstream versions 0-3
- Boolean entropy decoding and updated coefficient probabilities
- Segmentation maps, segment quantizers, and segment filter levels
- One, two, four, and eight coefficient token partitions
- Macroblock and 4x4 luma intra prediction modes
- Chroma intra prediction
- Skip signaling, coefficient token decoding, dequantization, and inverse transforms
- Normal and simple in-loop deblocking filters, including sharpness and reference/mode deltas
- YUV 4:2:0 to 8-bit RGB conversion
- Raw extended alpha
- VP8L-compressed extended alpha
- None, horizontal, vertical, and gradient alpha filtering
- Bounded macroblock-row VP8 reconstruction without full Y, U, V, and RGBA frame allocations
- Crop- and resize-aware reconstruction that avoids RGB conversion for pixels which cannot contribute to the output
- Higher-quality chroma-siting-aware upsampling instead of nearest chroma sample selection
- Alpha-plane reconstruction in bounded rows
Color and metadata
- Static image width, height, alpha, bit-depth, and frame-count reporting
- Opaque, binary-alpha, and partial-alpha output through
rgba8 - Embedded ICC profile (
ICCP) parsing and color-managed conversion - EXIF metadata parsing for opt-in preservation and orientation handling through the public pipeline
- XMP metadata parsing
- Opt-in compatible ICC and EXIF preservation, with metadata stripped by default
Pipeline execution
- Region selection while decoded rows advance
- Bounded
rgba8output blocks during VP8 and VP8L reconstruction - Public crop, resize, WebP-to-WebP, and WebP-to-other-codec pipelines
- True region decode rather than cropping a fully reconstructed source frame
- Decoder-driven scaled output for large downscales
Encode
Choosing lossless output
Lossless WebP is intended for graphics, interface captures, screenshots, and other repeated structure. For photographs, prefer lossy WebP, or PNG when exact lossless photographic preservation is required. Compare both lossless encodings for the actual source because lossless WebP can be larger than PNG on photographic content.
Lossless implemented target
- First-party static VP8L encoding
- Exact 8-bit RGB and alpha preservation
-
gray8,rgb8, andrgba8pipeline input - Ordered pixel input staged in one 32-bit transformed frame with two predictor rows; the encoded payload is buffered so the RIFF length is known before output begins
- Block-adaptive predictor transform across all 14 VP8L modes with VP8L-specified top-row, left-column, and right-edge behavior
- Subtract-green transform
- LZ77 backward references with adjacent-pixel, previous-row, and 16 recent hash-bucket match candidates plus two-dimensional VP8L distance coding
- Adaptive 8-to-10-bit color-cache emission for repeated transformed colors
- Per-image adaptive canonical Huffman trees with a complete-tree fallback
- Spatially varying Huffman entropy groups
- Cross-color and packed color-indexing transform selection
- Compression
effortcontrol from 0 through 6 - Near-lossless preprocessing through
nearLosslessquality from 0 through 100 - Output below PureJsImage PNG across the pinned deterministic graphic, JPEG-decoded, and production-style transparent logo fixtures
- Public
image.webp({ lossless: true })andimage.encode('webp', { lossless: true })APIs
Lossy implemented target
- First-party static intra-only VP8 encoding
- Quality control from 1-100
- YUV 4:2:0 output
-
gray8,rgb8, andrgba8pipeline input - Exact alpha preservation through an uncompressed
ALPHchunk when needed - 4x4 luma and 8x8 chroma DC prediction
- Forward transforms, quantization, coefficient coding, and standard coefficient probabilities
- Public
image.webp()andimage.encode('webp')APIs
Lossy planned
- Bounded macroblock-row encoding; the current encoder retains full Y, U, V, optional alpha, reconstruction, and output buffers
- Rate-distortion-driven luma and chroma prediction-mode selection
- 16x16 luma prediction and Walsh-Hadamard Y2 coding where beneficial
- Adaptive coefficient probabilities and optimized token partitions
- Segmentation, skip decisions, and per-segment quantization
- Normal or simple loop-filter selection and tuning
- Compressed and filtered
ALPHoutput - Better quality-to-quantizer mapping and compression-effort controls
- Output-size targeting and perceptual quality tuning
Common container output planned
- Animated WebP encoding
- ICC and EXIF writing when explicitly preserved
- XMP writing or preservation
- Explicit control over ICC and EXIF stripping versus preservation
Correctness and safety contract
- Validate RIFF and chunk extents, VP8/VP8L dimensions, lossless transform structure, Huffman trees, references, partitions, and entropy reads
- Apply configurable input-size, dimension, pixel-count, frame-count, and decoded-byte limits before decoding
- Reject truncated files, unsupported animations, VP8 interframes, reserved color-space modes, and unsupported alpha compression explicitly
- Verify six independently encoded, checksum-pinned static fixtures from the official WebP galleries and Wikimedia Commons
- Check exact lossless reference pixels and tolerance-bounded lossy reference pixels
- Cover odd dimensions, transparent lossless graphics, lossy photographs, and compressed lossy alpha in the permanent benchmark profile
- Benchmark metadata, decode, conversion, crop, resize, lossy encode, and lossless encode in isolated processes
- Gate VP8 and VP8L decode memory with reproducible 4000x3000 pressure fixtures and oracle-derived output samples
- Gate lossy decode against a pinned libwebp encode and gate lossy encode through an independent libwebp decode with PSNR floors in the focused test suite
- Decode benchmark WebP output in a separate libwebp-backed oracle process and require pinned pixels before accepting a timing
- Decode lossless encoder output through pinned Sharp/libwebp and require exact RGBA pixels, including deterministic high-entropy and graphic fixtures
- Require pinned deterministic PNG and JPEG-decoded benchmark fixtures to remain smaller as lossless WebP than as PureJsImage PNG, and record the pinned production-style logo result alongside libwebp in an isolated size and peak-RSS
- Document and benchmark the lossless encoder's source-sized 32-bit transformed frame and potentially source-sized buffered payload
- Account for cumulative VP8/VP8L planes, transforms, alpha, and output in a dedicated working-memory limit; the general decoded-byte limit does not yet represent every simultaneous allocation
- Expand the pinned corpus across independent libwebp versions, browsers, graphics tools, and real upload sources
- Cover ICC and EXIF preservation with focused round-trip fixtures
- Add animated, XMP, unusual partition, alpha-preprocessing, and transform-stress fixtures as those capabilities are implemented
- Add malformed RIFF, chunk, Huffman, LZ77, VP8 partition, coefficient, and decompression-bomb fuzzing with strict allocation limits
- Complete the 225-file Imazen WebP corpus decode-to-PNG baseline with 223 successful static decodes and two structured animated-input rejections
- Keep opt-in scalar and SIMD Rust/WASM VP8 row conversion and VP8L decode and encode transforms at exact TypeScript parity, with bounded row or block copies and transparent TypeScript fallback after setup or kernel failures