11# ImageScript benchmark
22
3- How [ ImageScript] ( https://github.com/imagescript/ImageScript ) compares to other image libraries for Node.js: speed, file
4- sizes and what installing each takes. ImageScript is plain JavaScript with no dependencies, so it runs wherever
5- JavaScript does, like Jimp, image-js and PureJsImage; sharp and @napi-rs/canvas wrap native libraries (libvips, Skia),
6- Photon is WebAssembly, and so is jsquash (Squoosh's libavif and libjxl, for AVIF and JPEG XL).
3+ This benchmark compares processing speed, output-file size and install size. Lower times and smaller files are better,
4+ but compare lossy files alongside their quality scores. Results depend on the machine, so compare libraries within a
5+ single run. The Method section explains the test inputs and measurements.
76
87<img src =" charts/overview-portable.svg " alt =" Time per operation of the libraries that run everywhere, colored by the distance to the fastest " >
98
@@ -53,7 +52,8 @@ Time per operation (less is faster), measured 2026-11-08 with Node.js v24.22.2 o
5352| render SVG | ** 172 ms** | – | – | – | – | 43 ms | – | 19 ms |
5453| thumbnail, keeping HDR | ** 180 ms** | – | – | – | – | 167 ms | – | – |
5554
56- Encoded sizes:
55+ Encoded sizes. For lossy files, the dB score estimates how closely the decoded image matches the source; higher is
56+ closer.
5757
5858| | ** ImageScript** | ** Jimp** | ** image-js** | ** PureJsImage** | ** Photon** | ** sharp** | ** jsquash** | ** @napi-rs/canvas ** |
5959| ---| --:| --:| --:| --:| --:| --:| --:| --:|
@@ -87,10 +87,11 @@ Encoded sizes:
8787 when the benchmark starts, PNG, lossy WebP, AVIF by sharp (libaom) and JPEG XL by jsquash (libjxl), at the
8888 qualities below), a 1434×1434 graphic with transparency (PNG), a 445×445 diagram with transparency placed on
8989 the photo (PNG), a 300×300 GIF with 101 frames (and made from it, an animated WebP by sharp at quality 80).
90- - Lossy WebP, AVIF and JPEG XL are encoded at the same nominal quality (80, 60 and 90); as the libraries'
91- scales differ, the file sizes come with their PSNR (of the color over black, decoded by libwebp, libavif and
92- libjxl). jsquash's libjxl runs at the effort ImageScript's default matches (cjxl's 5 lossy, 3 lossless; its own
93- default is 7). PureJsImage's AVIF
90+ - Lossy WebP, AVIF and JPEG XL are encoded at the same nominal quality (80, 60 and 90). Because each library uses a
91+ different quality scale, file sizes are shown with PSNR (peak signal-to-noise ratio), a measure of how closely the
92+ decoded colors match the original; higher is closer. Colors are compared over black after decoding with libwebp,
93+ libavif and libjxl. jsquash's JPEG XL encoder uses effort settings chosen to match ImageScript's defaults.
94+ PureJsImage's AVIF
9495 encoder has no quality option, and its JPEG XL decoder doesn't support everything libjxl's files use, which shows as
9596 failing. jsquash only encodes and decodes AVIF and JPEG XL (it gets the other inputs as pixels); @napi-rs/canvas
9697 decodes both with Skia's codecs, but doesn't encode them.
@@ -101,17 +102,18 @@ Encoded sizes:
101102 clone first, sharp runs a pipeline from raw pixels back to raw pixels, PureJsImage (which has no decoded images) one
102103 from a BMP back to a BMP, @napi-rs/canvas draws into a new canvas. PureJsImage has no blur, composite, rendering or
103104 animated GIFs.
104- - Where the library lets you choose, resizing is bilinear (or bicubic, in its own row), the thumbnail covers 400×400
105- (cropping the sides), blurring is gaussian with a radius or sigma of 5, sharpening an unsharp mask of radius or
106- sigma 1 (Photon: its 3×3 sharpening kernel), rotating by 30° grows the image to fit, brightness and contrast go up
105+ - Where the library lets you choose, resizing is bilinear (smooth) or bicubic (a sharper smoothing filter, in its own
106+ row), the thumbnail covers 400×400 (cropping the sides), blurring is gaussian (softening) with a radius or sigma of
107+ 5, sharpening uses an unsharp mask (edge enhancement) of radius or sigma 1 (Photon: its 3×3 sharpening kernel),
108+ rotating by 30° grows the image to fit, brightness and contrast go up
107109 20% (Jimp's and Photon's scales converted). image-js's and sharp's greyscale make one grey channel (and alpha). Each library uses its own defaults
108110 otherwise, e.g. its PNG compression level.
109111- Threads: ImageScript uses worker threads where they pay off (the processors available, at most 8), and sharp gets
110112 as many (` sharp.concurrency() ` ; its default on glibc Linux without jemalloc, like on CI, is one). The other
111113 libraries run on one thread (jsquash turns its threaded builds off in Node).
112114- [ tinybench] ( https://github.com/tinylibs/tinybench ) , at least 1.5 seconds and 3 runs per measurement, every library
113115 in its own process.
114- - The release workflow runs the benchmark for every release and commits the results, on a GitHub Actions runner;
116+ - The release workflow runs the benchmark for every regular release and commits the results, on a GitHub Actions runner;
115117 runners vary, so compare libraries within one run rather than between runs.
116118- Install sizes: ` npm install <library> ` in an empty project, the apparent size of ` node_modules ` .
117119
0 commit comments