Performance

Benchmark methodology and recorded results.

Complete decode–transform–encode workflows measured on real AWS Lambda and in isolated local processes, with pinned inputs, absolute memory, and independently validated output.

Real AWS LambdaSeven-engine competitor profileRecorded August 2026Linux x64

Speed, quality, and memory across seven isolated engines.

Default PureJsImage and its explicitly registered JPEG/PNG WASM accelerators are measured as separate engines. Every displayed result passed output validation on commonly supported workflows. Engines use their public defaults; quality is measured separately against an independently decoded exact-area reference. Sharp uses native libvips; PureJsImage WASM and jSquash use WebAssembly; default PureJsImage, Jimp, and image-js are pure JavaScript. In the August 10 profile, default PureJsImage retained 86.7% lower northstar RSS than Jimp and 87.6% lower than image-js.

When Sharp is the better choice: if native libvips is deployable and throughput or latency is the primary constraint, use Sharp. It was 1.9×–12.2× faster than the default PureJsImage TypeScript path across these five common-workflow medians. PureJsImage trades that ceiling for browser and edge portability, no required native addon or WASM, and a measured 2.3 MiB single-package install instead of Sharp’s 18.9 MiB and six production packages.

53.0%faster JPEG → PNG · WASM vs default
38.9%faster 100 MP PNG · WASM vs default
0invalid outputs or errors
Node.js 24.16 · Linux x64 · Intel i7-10700 · recorded August 2026Readable report · JSON

Measured on real AWS Lambda

The final profile ran at 05:51 UTC on August 9, 2026, in temporary us-east-1 Lambda functions using the managed Node.js 22 runtime and 512 MiB of memory. It compared x86_64 with ARM64 on the same JavaScript bundle, then explicitly selected the JPEG WASM accelerator for eligible full-resolution decodes. Every cell below is the median of three verified cold environments and three immediately paired warm invocations.

98,517 bytesdeployed code ZIP
21,100 / 5,160 / 4,363WASM bytes · raw / gzip / Brotli
14,160,578 bytesfixture objects staged separately in S3

JavaScript reference: x86_64 versus ARM64

Workflowx86 cold totalARM cold totalx86 warm operationARM warm operationx86 maxARM max
JPEG → 1024px PNG6,677.6 ms6,566.5 ms5,021.5 ms4,721.2 ms168 MiB168 MiB
JPEG → 1024px WebP6,870.4 ms7,095.4 ms5,140.9 ms4,886.2 ms158 MiB162 MiB
PNG → 1024px JPEG7,861.8 ms8,270.0 ms5,421.4 ms5,560.2 ms194 MiB201 MiB
PNG → 1024px WebP7,383.5 ms8,399.7 ms5,346.9 ms5,748.2 ms189 MiB190 MiB

There is no universal architecture winner in this sample. ARM64 warm operation medians were about 5–6% lower on the two JPEG-input workflows and about 3–8% higher on the two PNG-input workflows. Maximum memory was equal or up to 7 MiB higher on ARM64.

Explicit JPEG WASM experiment

This profile resized the 4000 × 3000 JPEG to 3000 × 2250 so the planner requested a full-resolution decode and the accelerator was eligible. The reference JavaScript path remains the default. WASM does not apply to reduced-resolution JPEG decode, other codecs, or unrelated pipeline work.

Architecture and outputJS cold totalWASM cold totalJS warm operationWASM warm operationMax used · JS / WASMCold WASM load
x86_64 · PNG16,589.5 ms15,337.5 ms14,437.7 ms13,201.0 ms171 / 178 MiB118.52 ms
x86_64 · WebP16,416.7 ms14,829.5 ms13,900.4 ms12,601.8 ms182 / 185 MiB119.52 ms
ARM64 · PNG15,722.4 ms13,882.2 ms13,623.6 ms11,461.9 ms198 / 197 MiB37.84 ms
ARM64 · WebP16,065.0 ms10,202.6 ms13,604.4 ms8,160.3 ms198 / 205 MiB57.41 ms

The accepted cold operation lazily instantiated the module exactly once; its paired warm invocation reused that instance. WASM memory was 6.0 MiB in every accelerated row, while maximum AWS-reported memory sometimes increased. The ARM64 WebP median was substantially lower, but its three-sample distributions were variable; the exact values above should not be generalized beyond this fixture and profile.

Correctness and timing boundary. Every accepted output had identical SHA-256 across cold and warm invocations, architectures, and JavaScript/WASM engines where applicable. operationMs includes JS/WASM input and output copies but excludes the S3 fixture read and output metadata validation. Those steps remain inside AWS Duration; cold total is Duration plus Init Duration.

An earlier 04:50 UTC x86-only tier sweep embedded the fixtures in a roughly 5.2 MiB deployment, so its package and cold-start profile is obsolete. Its warm results still show the Lambda CPU-allocation effect: the JPEG → WebP operation took 10,601 ms at 256 MiB, 5,261 ms at 512 MiB, and 2,533 ms at 1024 MiB while maximum use stayed at 121, 122, and 120 MiB. All four measured 12-megapixel workflows completed at 256 MiB with 121–156 MiB maximum use. For latency-sensitive endpoints, start at 1024 MiB rather than sizing only from RSS. Read the tier-sweep report →

01Deploy x86_64 and ARM64 functions with the same 512 MiB configuration and code asset.
02For each target, change a configuration nonce and wait for Lambda before the cold invocation.
03Immediately invoke warm and require the same Lambda log stream and reused WASM instance.
04Reject samples without deterministic format, dimensions, SHA-256 parity, or the required cold/warm lifecycle.

Read the final ARM64 and JPEG WASM report → Inspect the raw samples →

Earlier Jimp baseline

This separately recorded Jimp-only baseline predates the seven-engine profile above. In its 6000 × 4000 JPEG orientation, crop, resize, and encode workflow, PureJsImage used 118.6 MiB peak RSS versus Jimp’s 1,188.3 MiB and completed in 3,242.5 ms versus 3,762.9 ms.

90%lower peak RSS · 24 MP JPEG
14%faster wall time · 24 MP JPEG
87%lower RSS · 100 MP PNG
WorkflowPureJsImageJimpPJI RSSJimp RSS
6000×4000 orient, crop, resize, JPEG3,243 ms3,763 ms119 MiB1,188 MiB
JPEG crop and resize2,554 ms2,868 ms121 MiB1,197 MiB
100-megapixel PNG downscale1,970 ms3,733 ms169 MiB1,274 MiB
4000×3000 PNG resize496 ms944 ms142 MiB301 MiB
4000×3000 BMP to JPEG149 ms719 ms153 MiB262 MiB
Large TIFF to JPEG109 ms639 ms133 MiB319 MiB
Read peak RSS as a process number. It includes the Node.js runtime and is not a codec-only allocation counter. Larger workflows show memory scaling more clearly than small images.

Open the checked-in benchmark reports →

Methodology

A timing is accepted only after its output passes the workflow’s structural or pixel validation. Input preparation, output validation, and exact-area quality measurement remain outside the timed and peak-RSS regions.

01Verify checksum-pinned input and expected dimensions.
02Run each measured sample in an isolated worker process.
03Record wall time, CPU time, output size, and peak RSS.
04Decode and validate output, then measure premultiplied-RGBA PSNR where configured.
  • Competitor profile: PureJsImage, jimp@1.6.0, sharp@0.35.3 with default concurrency and one processing thread, image-js@1.7.0, and jSquash JPEG 1.6.0, PNG 3.1.1, WebP 1.5.0, and resize 2.1.1.
  • Resize workflows use engine defaults. PureJsImage and Sharp use Lanczos 3; Jimp uses bilinear. Cross-kernel timings describe each package's default experience and are not matched-quality comparisons.
  • Quality-enabled JPEG and PNG workflows independently decode input and output, apply exact-area resize and requested alpha flattening, and report premultiplied-RGBA PSNR. Fully transparent RGB does not count as visible error; exact means every compared channel matched.
  • The detailed historical tables below use the separately recorded Jimp 1.6 baseline.
  • Recorded environment: Node.js 24.16.0 on Linux x64, Intel Core i7-10700.
  • Reports use median and p95 wall time across repeated runs.
  • Absolute process peak RSS is the headline memory value.
  • JSON is the authoritative machine-readable result; Markdown is the review summary.

Inputs include real photographs, phone-camera files, standards fixtures, transparent graphics, pathological dimensions, high-entropy data, and deterministic generated stress images. The corpus manifest records source, license, dimensions, and SHA-256.

Codec results

JPEG

Baseline JPEG reconstruction and encoding retain bounded MCU rows instead of a full source-resolution RGB or RGBA bitmap. Progressive decode and encode retain compact 16-bit coefficient planes; final decode reconstruction remains bounded-row.

For deterministic 2048×1536 quality-80 4:2:0 output, isolated cold progressive encoding took 343.01 ms and 100.44 MiB peak RSS versus 230.55 ms and 86.89 MiB for baseline. It retained 9.00 MiB of coefficients and produced 1,384,013 bytes versus 1,492,375 bytes, 7.26% smaller with identical independently decoded PSNR. Progressive output gathers statistics in a second pass and writes five scan-specific DHT markers; it remains a separate runtime and memory class.

WorkflowPureJsImage wallJimp wallPJI RSSJimp RSS
Large JPEG metadata0.2 ms5,285 ms97 MiB1,184 MiB
4000×3000 resize to 1200 px894 ms1,395 ms106 MiB596 MiB
Twilio MMS JPEG to 1024 px853 ms1,364 ms105 MiB601 MiB
EXIF orientation 6388 ms576 ms105 MiB254 MiB

Ordered transforms to JPEG

These workflows use three isolated runs after one warmup and accept timings only when dimensions and pinned output pixels pass. PureJsImage’s arbitrary-angle rotation has a separate absolute baseline because Jimp uses different canvas sizing and sampling semantics.

WorkflowPureJsImage wallJimp wallPJI RSSJimp RSS
Rotate 90°, resize, JPEG1,795 ms2,052 ms175 MiB609 MiB
Resize, crop, resize, JPEG952 ms1,434 ms121 MiB584 MiB
Flip, flop, flatten to JPEG22 ms70 ms101 MiB153 MiB

The 17-degree RGBA rotation and JPEG flattening absolute baseline is 254 ms with 141 MiB peak RSS.

TIFF

The focused TIFF profile used nine measured runs after two warmups. PureJsImage passed all 18 workflows; Jimp passed seven, lacked the eight bounded raw/region workflows, and returned invalid pixels in three decode-to-PNG cases.

WorkflowPureJsImage wallJimp wallPJI RSSJimp RSS
4000×3000 TIFF metadata0.4 ms150.4 ms134.4 MiB289.8 MiB
4000×3000 TIFF → 1000px JPEG144.4 ms663.0 ms214.9 MiB372.1 MiB
7795×3122 LZW TIFF → 1000px PNG1,026.2 ms753.2 ms133.0 MiB284.8 MiB
PNG → Deflate TIFF24.5 ms97.3 ms111.4 MiB152.8 MiB

The LZW workflow is the measured speed exception: PureJsImage was slower, while using 53.3% less peak RSS. PureJsImage report · Jimp report.

WebP

Jimp 1.6 exposes no WebP codec, so these are absolute baselines—not synthetic comparisons.

WorkflowMedian wallPeak RSS
1600×2000 WebP resize to JPEG519 ms167 MiB
Lossy WebP to PNG216 ms140 MiB
JPEG to lossy WebP965 ms112 MiB
PNG to lossless WebP50 ms108 MiB

JPEG 2000

The explicit full-frame JP2 fallback was measured with a public-domain 1920 × 2172 NASA photograph. Metadata peaked at 97 MiB cold and 101 MiB warm. Resize to a 480 × 543 JPEG took 4,422 ms cold and 4,204 ms warm, with absolute process peaks of 192 MiB and 211 MiB. The reproducible gate requires metadata below 128 MiB and resize below 256 MiB. Comparable four-megapixel inputs need a 512 MiB Lambda tier for practical runtime headroom; smaller deployments should lower input pixel and decoded-byte limits.

AVIF and experimental HEIF

The restricted AVIF photo decoder measured 295 ms on a 768 × 512 Kodak image and 783 ms on a 1204 × 800 fox image. Its five-run photo benchmark reached 144 MiB maximum observed RSS. It retains padded full-frame YUV and RGBA state today, so this is a baseline rather than the target architecture.

The explicitly registered 4032 × 3024 iPhone HEIC auto-orient-and-resize-to-1200px-JPEG workflow measured 8,080 ms and 190 MiB cold peak RSS. Jimp exposes no HEIF decoder. Grid tile selection benefits crop workflows, while full decode memory remains an explicit optimization target.

Experimental HEIC scope. HEIF/HEIC is excluded from allCodecs and these measurements use its direct experimental import. HEIC commonly carries HEVC/H.265 content that may be subject to third-party patent rights; PureJsImage's MIT license grants no third-party patent rights. Commercial users and distributors must assess their own licensing obligations.
Comparison scope. Unsupported Jimp workflows are reported as unsupported. Development oracles are not presented as production engines.

Bundle and deployment size

JPEG and PNG form the matched codec set because all five libraries support them. PureJsImage and jSquash can assemble exactly those entries. The normal public imports for Jimp, image-js, and Sharp include the additional codecs shown rather than offering equivalent codec-level tree shaking.

Competitor comparison

ImportVersionCodecs includedMinified JSgzipBrotliInstalledPackages
PureJsImage matched0.9.0JPEG, PNG145.4 KiB47.2 KiB39.6 KiB2.3 MiB1
PureJsImage all0.9.09 codecs668.9 KiB240.7 KiB201.6 KiB2.3 MiB1
Jimp1.6.0JPEG, PNG, TIFF, BMP, GIF577.4 KiB174.6 KiB139.5 KiB29.3 MiB70
image-js1.7.0JPEG, PNG, TIFF, BMP361.5 KiB111.2 KiB94.3 KiB17.0 MiB46
jSquashJPEG 1.6.0; PNG 3.1.1; resize 2.1.1JPEG, PNG52.4 KiB16.0 KiB13.2 KiB1.0 MiB3
Sharp JS wrapper0.35.3JPEG, PNG, TIFF, WebP, GIF, AVIF128.4 KiB38.3 KiB33.5 KiB18.9 MiB6
Native payload. Sharp's JavaScript bundle is only the wrapper. Its installed footprint includes the native addon and the required Linux x64 libvips package, so 128.4 KiB is not its deployment size. Installed footprints vary by operating system and architecture.
WebAssembly payload. jSquash's 52.4 KiB JavaScript bundle is codec and resize glue. Its 1.0 MiB installed footprint includes the selected JPEG, PNG, and resize WebAssembly packages.

All JavaScript rows use the same esbuild settings: Node 22 target, ESM output, tree shaking, minification, no source map, gzip level 9, and Brotli quality 11. Installed size includes each engine's production dependency tree and required platform packages.

PureJsImage entry points

Applications can import only the codec entries they use. The root API itself pulls no codec implementation into the module graph.

EntryMinifiedgzipBrotli
Core API49.8 KiB16.2 KiB14.4 KiB
Core + PNG83.7 KiB28.0 KiB24.4 KiB
Core + JPEG120.2 KiB38.9 KiB33.1 KiB
Core + JPEG 200086.7 KiB28.0 KiB24.5 KiB
Core + WebP113.0 KiB39.1 KiB33.7 KiB
Core + experimental HEIF / HEIC148.0 KiB48.2 KiB41.6 KiB
Core + all codecs668.9 KiB240.7 KiB201.6 KiB

Run npm run size to rebuild PureJsImage and reproduce both tables on the current platform.

Reproduce it

Prepare and verify the corpus before running profiles. Downloaded fixtures are checksum-pinned; generated fixtures are deterministic.

Terminal
npm run fixtures:prepare
npm run fixtures:verify
npm run fixtures:avif
npm run fixtures:heif
npm run fixtures:jpeg

npm run build
npm run bench:competitors
npm run bench:competitors:charts
npm run bench:smoke
npm run bench:bmp
npm run bench:tiff
npm run bench:transforms
npm run bench:transforms:compare
npm run bench:webp
npm run bench:avif:b2
npm run bench:heif:cold

AWS_PROFILE=<profile> AWS_REGION=us-east-1 npm run bench:lambda:deploy
AWS_PROFILE=<profile> AWS_REGION=us-east-1 npm run bench:lambda:run
AWS_PROFILE=<profile> AWS_REGION=us-east-1 npm run bench:lambda:run:arm-wasm
AWS_PROFILE=<profile> AWS_REGION=us-east-1 npm run bench:lambda:destroy

npm run bench:competitors builds PureJsImage and runs all seven engines in isolated workers. The chart command regenerates the three checked-in PNGs from the JSON report. See the benchmark harness guide for profiles and result interpretation.

Interpret results

  • Compare equivalent complete workflows, not isolated kernels with different work.
  • Use absolute peak RSS for Lambda tier decisions. Warmup can leave allocator pages resident and make a delta look deceptively small.
  • Treat output dimensions, validity, pixel tolerances, and lossy quality/size as part of performance.
  • Do not generalize one codec’s bounded path to another. WebP and current AVIF paths have different memory classes from baseline JPEG and sequential PNG.
  • Re-run representative inputs on your hardware and Node version before selecting deployment limits or memory tiers.