mrkeyoor.com_
Sat 08 Aug 22:50 UTC
npmUtilsupdated 08 Aug 2026

jimp

Jimp is an image decoder, pixel buffer, encoder, and manipulation toolkit implemented in JavaScript. The default package reads and writes PNG, JPEG, BMP, GIF, and TIFF, then provides resize, crop, cover, contain, rotate, composite, text, blur, color, quantization, hashing, and per-pixel operations in Node, browsers, and workers. Its main attraction is avoiding native addons and system image libraries. That portability comes at a cost: transformations run on the JavaScript thread, and the project warns that its codecs are not optimized and may allocate substantial memory.

Verdict

Jimp is the practical portability pick for modest image jobs when native code is off the table. For production thumbnail pipelines or modern codec work, Sharp is usually faster, leaner in memory, and less likely to turn image processing into event-loop latency.

API stability3/5The project reached 1.x and current methods are strongly typed around consistent options objects, but moving from 0.x was a broad rewrite: named imports replaced the default export, positional parameters became objects, constructors changed, export methods became uniformly async and lost their Async suffix, MIME constants moved, and encoder settings moved to export calls. Pin the major and reject old snippets during review.
Docs4/5The official site has focused guides for setup, browser use, custom builds, plugins, WebP, and v1 migration, plus generated API pages for methods, option types, enums, fonts, and utilities. It honestly warns about performance and memory. Some generated examples remain stale, including positional resize, crop, cover, and flip calls that conflict with current options-object declarations, so the type definitions are sometimes more trustworthy than the example block.
Maintenance4/5npm published 1.6.1 and GitHub recorded a matching push in April 2026. The repository is not archived, current packages require Node 18, and the monorepo maintains separate codecs, plugins, types, browser builds, and documentation. GitHub reports 185 open issues and pull requests, a meaningful backlog for an image library with many codecs and environments, but recent coordinated releases show the project is active.
Ecosystem4/5Jimp recorded 3,202,858 downloads for the measured week and has 14,663 GitHub stars. Its default distribution composes 27 first-party @jimp packages covering codecs, core utilities, and manipulation plugins, with documented custom builds and WASM WebP extension. It runs in Node, browsers, and workers without native installation, although the broader production image ecosystem and modern format support are stronger around Sharp and native tooling.

Use it if

  • You need basic image manipulation in a Node 18+, browser, worker, or constrained deployment where native addons are unavailable
  • Your workload is small or offline, such as generating test fixtures, avatars, social cards, simple thumbnails, or bitmap text
  • You want direct RGBA pixel access and a plugin architecture implemented entirely in JavaScript
  • You need one API across CommonJS and ESM with bundled TypeScript declarations and browser exports
Skip it if

Setup reality

Jimp 1.6.1 requires Node 18 or newer and has no native build, system library, peer dependency, credential, or configuration requirement. npm install jimp gives ESM, CommonJS, browser exports, TypeScript declarations, six default format families, and the full default plugin set, but it also pulls 27 direct @jimp packages. Version 1 is the first surprise: import { Jimp } from 'jimp' is correct because the default export is gone; read and fromBuffer create decoded images; new Jimp receives an object; resize, crop, cover, contain, flip, and print use options objects; and write, getBuffer, and getBase64 are the async export methods without the old Async suffix. Codec options now belong on write or getBuffer, so JPEG quality is not a mutating image method. The API documentation contains some generated examples that still show positional calls even though current declarations validate options such as { w, h }, so follow the v1 migration guide and types when they disagree. Inputs become uncompressed RGBA buffers and codecs may allocate additional working memory. Limit upload bytes and dimensions outside Jimp, cap concurrency, and move expensive work to worker threads or a job process so a resize does not stall request handling. Browser use is supported, but remote reads still face CORS, file uploads must become ArrayBuffer data, and some plugin combinations need a Buffer polyfill. WebP requires @jimp/wasm-webp plus @jimp/core and a custom Jimp assembled with defaultFormats and defaultPlugins; it is not enabled by installing jimp alone. Fonts are bitmap font assets loaded asynchronously, not arbitrary system fonts. Output type comes from the MIME passed to getBuffer or the destination extension passed to write, and metadata preservation should not be assumed without a test for the codec and file in question.

Patterns

Read, resize, and write an imageread-resize-write

import {Jimp} from 'jimp';

const image = await Jimp.read('./input.png');
image.resize({w: 320});
await image.write('./output.png');

Jimp v1 uses a named export and an options object with w and h. Supplying only w preserves the aspect ratio.

Decode a buffer and encode JPEGconvert-buffer-to-jpeg

import {Jimp, JimpMime} from 'jimp';

const image = await Jimp.fromBuffer(uploadBuffer);
const jpeg = await image.getBuffer(JimpMime.jpeg, {
  quality: 80,
});

Version 1 puts JPEG quality on the export call. The old quality() and getBufferAsync() examples are obsolete.

Create a solid RGBA canvascreate-empty-image

import {Jimp} from 'jimp';

const canvas = new Jimp({
  width: 1200,
  height: 630,
  color: 0xffffffff,
});
await canvas.write('./card.png');

Packed colors use RGBA byte order, so 0xffffffff is opaque white and 0x00000000 is transparent black.

Crop a rectangular regioncrop-region

const image = await Jimp.read('./photo.jpg');
image.crop({x: 100, y: 60, w: 800, h: 600});
await image.write('./crop.jpg', {quality: 85});

crop mutates the image and current v1 expects x, y, w, and h in one object. Clone first when the original is still needed.

Fill a thumbnail without stretchingmake-cover-thumbnail

const image = await Jimp.read('./photo.jpg');
image.cover({w: 400, h: 300});
await image.write('./thumbnail.jpg', {quality: 82});

cover preserves aspect ratio and clips overflow. Use contain when letterboxing is preferable to losing image edges.

Fit an image inside fixed dimensionscontain-with-letterbox

const image = await Jimp.read('./logo.png');
image.background = 0xffffffff;
image.contain({w: 512, h: 512});
await image.write('./logo-square.png');

contain keeps the full image and fills unused space with the background color. Transparent and JPEG outputs need deliberate background handling.

Place a translucent watermarkcomposite-watermark

const [photo, mark] = await Promise.all([
  Jimp.read('./photo.jpg'),
  Jimp.read('./watermark.png'),
]);

mark.resize({w: 180}).opacity(0.6);
photo.composite(mark, photo.width - mark.width - 24, photo.height - mark.height - 24);
await photo.write('./watermarked.jpg', {quality: 85});

composite mutates the destination. Resize and fade the watermark before calculating its final placement.

Draw text with a bundled bitmap fontprint-bitmap-text

import {Jimp, loadFont} from 'jimp';
import {SANS_32_WHITE} from 'jimp/fonts';

const image = await Jimp.read('./banner.png');
const font = await loadFont(SANS_32_WHITE);
image.print({font, x: 24, y: 24, text: 'Release 1.6'});
await image.write('./labeled.png');

Jimp uses BMFont assets, not CSS or arbitrary installed fonts. Font loading is async, while print mutates synchronously.

Modify RGBA bytes during a scanedit-pixels

const image = await Jimp.read('./input.png');

image.scan((x, y, idx) => {
  image.bitmap.data[idx] = 255 - image.bitmap.data[idx];     // red
  image.bitmap.data[idx + 1] = 255 - image.bitmap.data[idx + 1]; // green
  image.bitmap.data[idx + 2] = 255 - image.bitmap.data[idx + 2]; // blue
});

await image.write('./inverted.png');

Each pixel occupies four bytes in RGBA order and idx points to red. Scanning large images is synchronous CPU work.

Apply synchronous filters before one exportchain-color-filters

const image = await Jimp.read('./input.jpg');
image
  .greyscale()
  .contrast(0.2)
  .brightness(0.1)
  .rotate(90)
  .flip({horizontal: true});

await image.write('./processed.png');

Most transformations mutate and return the same image for chaining. Only reading and exporting need await in this sequence.

Clone before producing multiple variantspreserve-original-branch

const original = await Jimp.read('./source.png');
const small = original.clone().resize({w: 320});
const large = original.clone().resize({w: 1280});

await Promise.all([
  small.write('./small.png'),
  large.write('./large.png'),
]);

Transforms mutate their receiver. clone prevents the first resize from changing the pixels used for later variants.

Build a Jimp class with WebP supportadd-webp-format

import {createJimp} from '@jimp/core';
import {defaultFormats, defaultPlugins} from 'jimp';
import webp from '@jimp/wasm-webp';

const WebpJimp = createJimp({
  formats: [...defaultFormats, webp],
  plugins: defaultPlugins,
});

const image = await WebpJimp.read('./input.webp');
await image.write('./output.webp');

Install @jimp/core and @jimp/wasm-webp explicitly. WebP is not part of the default Jimp format list and WASM bundling may need platform-specific configuration.

Alternatives

PackageRegistryPick it when
sharpnpmChoose it for high-throughput server resizing, streaming, modern codecs, metadata controls, and lower CPU use through libvips
pureimagenpmChoose it for a smaller pure-JavaScript Canvas-like drawing API when Jimp's full plugin collection is unnecessary
canvasnpmChoose it when server code needs the HTML Canvas 2D API, font rendering, paths, and drawing semantics despite native installation concerns