Open-source project
nodeca/pica avatar
nodeca/pica

nodeca/pica: browser-side image resize that picks its own backend

Resize image in browser with high quality and high speed

4,160 stars256 forksTypeScriptMIT

At a glance

What is it?
pica resizes canvas and image data in the browser, choosing between Web Workers, WebAssembly, createImageBitmap and plain JavaScript. It is a good fit for shrinking uploads before they leave the client, and a poor fit for EXIF-aware file handling or professional-grade output.
Who is it for?
Adopt pica when you want to shrink an image inside the browser before upload and you already hold a Canvas or ImageBitmap. Do not adopt it for File or Blob handling from a file input: the README points at image-blob-reduce for orientation and EXIF metadata, and it explicitly does not recommend pica for resizing professional quality images.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 91 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • nodeca/pica4,160 ★ · +6
Star history
Repository2026-09-232026-11-14
nodeca/pica4.2k4.2k

6 Stars in the last 52 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

What pica solves, and the workload it assumes

The README frames the goal plainly: resize images in the browser without pixelation and reasonably fast. The stated use cases are reducing upload size for large images, saving server resources on image processing, and generating thumbnails client-side. All three share a shape: an image already decoded in the page, a smaller target, and a desire to avoid a round trip to a server.

That framing matters because it tells you what pica is not. It is not a file pipeline. The README includes an explicit note: if you need File or Blob resize from a form's file input, consider image-blob-reduce, which it says has additional machinery to process orientation and keep EXIF metadata. So the intended user is someone who has a Canvas, an Image or an ImageBitmap in hand and wants a smaller one. If your entry point is an <input type="file">, pica is one layer below where you actually start, and the README says so rather than pretending otherwise.

Feature autoselection: js, wasm, cib and ww

pica does not commit to a single resize implementation. The config key features takes a list, defaulting to ['js', 'wasm', 'ww'], and the README says the alternative is ['js', 'wasm', 'cib', 'ww'] or ['all']. The four names map to plain JavaScript, WebAssembly, createImageBitmap and web workers. The library picks the best available technology from that list at runtime.

The createImageBitmap entry deserves attention because it is the one the project actively discourages. The README states that cib is buggy in Chrome and does not support the default mks2013 filter, that its resize feature is blocked in the default config, and that enabling it is done at your own risk with results that depend on the browser. Without cib, the README calls the result predictable and good. createImageBitmap is still used for non-blocking image decode when available, just not for downscaling. That split is the interesting design decision here: take the async decode, refuse the resize.

Two more config keys shape memory and latency. tile defaults to 1024 and the README explains that images are processed by regions to restrict peak memory use. idle defaults to 2000 ms and exists because creating web workers is not fast, so instances reuse them. concurrency defaults to an autodetected CPU count capped at 4.

Install and a first resize

The README gives a single install step: npm install pica. The package ships a CommonJS entry at dist/pica.js and an ESM entry at dist/pica.mjs, with a browser export pointing at the minified builds.

bash
npm install pica

The default export changed shape in v10. The migration notes say the default export is now a factory function only, so new (require('pica'))() no longer applies; you either call the factory or import the named Pica class.

js
import pica from 'pica';
const resizer = pica();

resizer.resize(from, to)
  .then(result => console.log('resize done!'));

Here from is a Canvas, Image or ImageBitmap and to is a destination canvas whose size is supposed to be non-zero. Sizes come from the two objects, not from arguments. The promise resolves with to on success, so the console line fires after the destination canvas has been written.

If you also want an encoded result, the README chains toBlob, which matters because it says some ancient browsers do not support canvas.toBlob() and that pica.toBlob() includes the required shim.

js
resizer.resize(from, to)
  .then(result => resizer.toBlob(result, 'image/jpeg', 0.90))
  .then(blob => console.log('resized to canvas & created blob!'));

One packaging choice affects your bundle and your Content Security Policy. By default the main entry inlines the web worker code. The split build, pica/dist/pica_main.mjs plus pica/dist/pica_worker.js, keeps the worker separate and then requires workerURL, passed as a URL object.

Quality limits the README admits to

The quality section is unusually candid. JavaScript canvas does not expose gamma correction information, and bitmaps carry 8 bits per channel. The README states that with gamma correction precision could be 12 bits per channel, so some quality is lost. It then draws the line itself: precision loss will not be noticeable for ordinary images, and the project does not recommend the library for resizing professional quality images.

That is a real boundary, not a hedge. If your pipeline ends in print or in a colour-managed review step, the missing gamma information is not something a filter choice fixes. The filter option defaults to mks2013, described as doing both resize and sharpening and as optimal, with a note that changing it is not recommended. The deprecated positional quality argument from v9 maps onto filter: pica.resize(from, to, 3) becomes pica.resize(from, to, { filter: 'lanczos3' }), and the object form { quality: 3 } still works but is deprecated.

Sharpening is available separately through unsharpAmount, unsharpRadius and unsharpThreshold. unsharpAmount defaults to 0, off, with 100 to 200 described as usually good. The README notes mks2013 already does optimal sharpening, which makes the unsharp options a second pass rather than a default correction. Radius values below 0.5 turn the mask off, and large values are clamped to 2.0.

Where pica breaks: CORS, iOS memory and rotated JPEGs

The README's prior-to-use section lists failure modes that have nothing to do with resize math. Because of JavaScript security restrictions, you can process images from the same domain or local files only; loading from a remote domain requires the proper Access-Control-Allow-Origin header. That is a browser rule, but it decides whether pica works at all in a given deployment.

iOS has memory limits for canvas elements, which the README says may cause problems in some cases and links to a wiki page for details. Tile size is the knob that interacts with this: the default 1024 exists to restrict peak memory, so a larger tile trades memory for fewer region boundaries.

The third failure mode is the one most likely to bite silently. If your source data is a jpeg image, it can be rotated, and the README points at image-blob-reduce rather than offering an internal fix. The same advice appears for saving: for jpeg source it is a good idea to keep exif data, again via image-blob-reduce. So a photo straight off a phone can come out with the wrong orientation and without its metadata, and pica will not warn you.

Finally, cancellation. resize accepts a cancelToken, a Promise instance, and the README says the current operation is terminated on rejection. That is the mechanism for abandoning work when the user navigates away or picks a different file.

How pica differs from a general image manipulation library

The related searches around this project mix pica with Compressor.js, general JavaScript image processing libraries, and Node.js image work. The distinction is worth making concrete, because picking the wrong layer wastes time.

A library aimed at compressing an uploaded file starts from a Blob and owns decoding, orientation, metadata and encoding as one operation. pica starts from a decoded Canvas, Image or ImageBitmap and owns one step: producing a smaller raster. That is why the README redirects file-input work to image-blob-reduce instead of extending pica's scope. The trade-off is deliberate: a narrower surface means fewer assumptions about where your bytes came from, but it also means you assemble the surrounding pipeline yourself.

The same split applies to server-side resizing. pica is a browser library; the package's browser export points at minified builds and the feature list is built around web workers, WebAssembly and createImageBitmap, none of which exist in Node. If your resize happens after upload, pica is the wrong tool even though the search terms overlap.

Within its own scope, the differentiator is the feature list. A plain canvas drawImage call is one line and always available, but the README's whole premise is that the result is not good enough and not fast enough, which is why pica carries four backends and a region-based tiling scheme.

Maintenance, migration cost and the MIT licence

The repository is not archived, and the last push was on 2026-08-15. That is recent enough to treat the project as live, and the version in package.json is 10.0.3. The repository has no published GitHub releases, so the changelog file at the repository root is the place to confirm what shipped when.

The upgrade cost is concentrated in one migration. The README's v9 to v10 notes list five breaking changes: legacy browsers including IE are dropped, the default export becomes a factory function only, createCanvas is removed from options in favour of exposing a custom OffscreenCanvas on the global scope, the positional quality argument is replaced by filter, and implicit sharing of a worker pool between multiple Pica instances is gone, so you create one instance and reuse it. That last one is the change most likely to surface as a performance regression rather than an error, because per-instance worker pools still work, they just cost more.

The licence is MIT, so the practical obligations are the usual ones: keep the copyright and permission notice with distributions. The package.json also lists GitHub Sponsors entries for the maintainer and the organisation, which is a funding arrangement rather than a licensing term. Nothing here restricts commercial use, but if your product ships a modified dist build, confirm you are preserving the notice file.

Editorial conclusion

Adopt pica when you want to shrink an image inside the browser before upload and you already hold a Canvas or ImageBitmap. Do not adopt it for File or Blob handling from a file input: the README points at image-blob-reduce for orientation and EXIF metadata, and it explicitly does not recommend pica for resizing professional quality images. Before committing, verify two things in your own target browsers: whether the default feature set resolves to wasm or falls back to js, and whether your source images are JPEGs that carry orientation data, since pica will not correct that for you.

Frequently asked questions

How do I resize a JPEG without losing quality with pica?

You cannot avoid all loss: the README states that canvas bitmaps are 8 bits per channel and gamma correction information is unavailable, where precision could otherwise be 12 bits. The default mks2013 filter is described as optimal and changing it is not recommended. The README also notes that if your source is a JPEG it can be rotated, and points at image-blob-reduce for orientation and EXIF handling.

Is pica free to use?

pica is published under the MIT licence, and the package.json also lists GitHub Sponsors entries for the maintainer and the nodeca organisation. The README does not describe any paid tier or commercial restriction.

How do I install pica?

The README gives one command, npm install pica. The package exposes a CommonJS entry at dist/pica.js and an ESM entry at dist/pica.mjs, plus a browser export pointing at the minified builds.

Does pica work with File or Blob input from a file picker?

Not directly. The README says that if you need File or Blob resize from a form's file input, you should consider image-blob-reduce, which has additional machinery for orientation and EXIF metadata. pica's resize takes a Canvas, Image or ImageBitmap as its source.

Why is createImageBitmap resize disabled by default in pica?

The README states that cib is buggy in Chrome and does not support the default mks2013 filter, and that results with cib enabled depend on the browser. It is used only for non-blocking image decode when available, without downscale.

Can I cancel a resize that is already running in pica?

Yes. The resize options include cancelToken, a Promise instance, and the README says the current operation is terminated on rejection.

Official sources

  1. Issues
  2. License: MIT
  3. nodeca/pica on GitHub
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/nodeca-pica.svg)](https://hysenlabs.com/projects/nodeca-pica)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/nodeca-pica.svg)](https://hysenlabs.com/projects/nodeca-pica)

Was this useful?