npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

image-stitch

v1.1.53

Published

Stitch images together efficiently with multi-format support (PNG, JPEG, HEIC), streaming, for node.js and web

Downloads

113

Readme

image-stitch

image-stitch combines images into a single output without relying on Canvas APIs. It works in Node.js and modern browsers, including streaming scenarios for large images.

Features:

  • Multi-format input: PNG, JPEG, HEIC with automatic format detection
  • Multi-format output: PNG (lossless) or JPEG (lossy with quality control)
  • Streaming processing: Minimal memory usage for large images
  • No canvas dependency: Works in Node.js and browsers
  • Flexible layouts: Grid, columns, rows, or positioned images with overlapping
  • Alpha blending: Proper compositing for overlapping positioned images

Install

npm install image-stitch

Pick the right build

  • ESM (default)import { concatToBuffer } from 'image-stitch' resolves to dist/esm/index.js with rich type definitions.
  • CommonJS – legacy bundlers or Node.js require load dist/cjs/index.cjs automatically.
  • Tree-shakeable bundleimport 'image-stitch/bundle' for a single-file ESM artifact that keeps dependencies external. The browser bundle ships with PNG support out of the box; add JPEG/HEIC decoders only when you need them.
  • Browser global – drop <script src="https://cdn.jsdelivr.net/npm/image-stitch/dist/browser/image-stitch.min.js"></script> into any page and use window.ImageStitch.
  • Node.js specificimport ... from 'image-stitch/node' ensures you get the full Node.js build (useful for JSDOM testing).
  • Denoimport { concatToBuffer } from 'npm:image-stitch/deno/mod.ts' targets the ESM build with Node compatibility.

All bundles ship with source maps and .d.ts files so editors stay fully typed.

Basic usage

import { concatToBuffer } from 'image-stitch';
import { readFileSync, writeFileSync } from 'fs';

const result = await concatToBuffer({
  inputs: [
    readFileSync('one.png'),
    readFileSync('two.png')
  ],
  layout: { columns: 2 }
});

writeFileSync('stitched.png', result);

JPEG output

By default, image-stitch outputs PNG (lossless). You can also output JPEG (lossy) with configurable quality:

import { concatToBuffer } from 'image-stitch';

// Output as JPEG with custom quality
const result = await concatToBuffer({
  inputs: ['photo1.jpg', 'photo2.jpg', 'image.png'],
  layout: { columns: 3 },
  outputFormat: 'jpeg',  // 'png' (default) or 'jpeg'
  jpegQuality: 90        // 1-100, default: 85
});

writeFileSync('stitched.jpg', result);

JPEG output features:

  • Lossy compression with configurable quality (1-100)
  • Smaller file sizes compared to PNG for photos
  • 8-bit RGBA format (automatically converts higher bit depths)
  • Streaming support via concatToStream() and concatStreaming()
  • Mixed inputs - combine PNG, JPEG, HEIC inputs into JPEG output

Positioned images (flexible layout)

Place images at arbitrary x,y coordinates with support for overlapping and alpha blending:

import { concatToBuffer, type PositionedImage } from 'image-stitch';

const inputs: PositionedImage[] = [
  { x: 0, y: 0, source: 'background.png' },
  { x: 50, y: 50, zIndex: 10, source: 'overlay.png' },      // Overlaps background with explicit zIndex
  { x: 200, y: 100, source: imageBuffer }
];

const result = await concatToBuffer({
  inputs,
  layout: {
    width: 500,    // Optional: canvas width (auto-calculated if omitted)
    height: 400    // Optional: canvas height (auto-calculated if omitted)
  },
  enableAlphaBlending: true  // Default: true (blend overlapping images)
});

Positioned image features:

  • Arbitrary placement - position images anywhere on the canvas using x,y coordinates
  • Overlapping support - images can overlap with proper alpha blending (z-order = input order or custom zIndex)
  • Auto canvas sizing - canvas dimensions calculated from image bounds if not specified
  • Automatic clipping - images outside canvas bounds are clipped with console warnings
  • Alpha blending - optional blending for overlapping images (disable for faster compositing)
  • Streaming - maintains O(canvas_width) memory usage, not O(total_pixels)
// Example: Create a composite with watermark
const composite = await concatToBuffer({
  inputs: [
    { x: 0, y: 0, source: 'photo.jpg' },
    { x: 420, y: 380, source: 'watermark.png' }  // Bottom-right corner
  ],
  layout: {},  // Canvas auto-sized to fit all images
  outputFormat: 'jpeg',
  jpegQuality: 90
});

Track progress

Pass an onProgress callback to receive a counter whenever an input tile finishes streaming. The callback receives the number of completed inputs and the total inputs in the operation, making it easy to update loading indicators while large grids process.

await concatToBuffer({
  inputs: tiles,
  layout: { rows: 4, columns: 4 },
  onProgress(current, total) {
    console.log(`Stitched ${current}/${total}`);
  }
});

👉 Read the full guides, API docs, and interactive demos at image-stitch GitHub Pages.

Browser bundle & optional decoders

Modern browsers provide native HEIC/JPEG decoding primitives, so the browser-focused bundle keeps only the PNG decoder by default. Opt in to extra formats with lightweight plugins:

import { concatToBuffer } from 'image-stitch/bundle';
import { jpegDecoder } from 'image-stitch/decoders/jpeg';
import { heicDecoder } from 'image-stitch/decoders/heic';

await concatToBuffer({
  inputs: [jpegBytes, heicBytes],
  layout: { columns: 2 },
  decoders: [jpegDecoder, heicDecoder]
});

Node.js imports (import { concatToBuffer } from 'image-stitch') continue to register PNG, JPEG, and HEIC decoders automatically.

Environment Support

image-stitch supports Node.js, modern browsers, and hybrid environments like JSDOM.

Node.js

Standard usage works out of the box with npm install image-stitch. Node environments automatically use efficient native libraries (like sharp if installed) or pure JS fallbacks (jpeg-js, pako).

JSDOM & Testing

For JSDOM environments (e.g. Jest/Vitest tests), you can ensure robust behavior by using the Node-specific export or configuring custom constructors.

Option 1: Force Node Logic (Recommended)

Import from image-stitch/node to bypass browser bundles and use Node.js capabilities (file system, streams, buffers) even if JSDOM mocks browser globals.

import { concatToBuffer } from 'image-stitch/node';
// Uses Node.js logic and dependencies (pako, jpeg-js, etc.)

Option 2: Dependency Injection

If you prefer to use browser-like APIs (Canvas) in your tests, you can inject compatible constructors (like napi-rs/canvas or canvas).

import { concatToBuffer } from 'image-stitch';
import { Image, Canvas } from '@napi-rs/canvas';

await concatToBuffer({
  inputs: [...],
  layout: { columns: 2 },
  decoderOptions: {
    customConstructors: { Image, Canvas } // injected constructors
  }
});