Installation
image-stitch ships ready for every runtime: native ESM, CommonJS, a tree-shakeable ESM bundle, and a browser-friendly global build. Install it with npm, pnpm, or yarn.
npm install image-stitch
Node.js 18+ is recommended. In browsers, load the prebuilt script tag bundle from https://cdn.jsdelivr.net/npm/image-stitch/dist/browser/image-stitch.min.js or import the module build from https://cdn.jsdelivr.net/npm/image-stitch/dist/bundles/image-stitch.esm.js.
Quick start (Node.js)
import { concatToBuffer } from "image-stitch";
import { readFileSync, writeFileSync } from "node:fs";
const stitched = await concatToBuffer({
inputs: [
readFileSync("./pngsuite/png/basi0g08.png"),
readFileSync("./pngsuite/png/basi2c08.png")
],
layout: { columns: 2 }
});
writeFileSync("spritesheet.png", stitched);
The returned value is a Uint8Array containing the PNG file. Persist it to disk, push it across the network, or convert it into a Blob for browser usage.
Modern browser usage
Use dynamic imports to fetch the ESM bundle from a CDN when prototyping. The library automatically normalizes Blob, File, ArrayBuffer, and HTMLCanvasElement inputs before stitching.
const { concatToBuffer } = await import("https://cdn.jsdelivr.net/npm/image-stitch/dist/bundles/image-stitch.esm.js");
const files = Array.from(document.querySelector("input[type=file]").files ?? []);
const stitched = await concatToBuffer({
inputs: files,
layout: { columns: 3 },
onProgress(current, total) {
console.log(`Decoded ${current}/${total}`);
}
});
const blob = new Blob([stitched], { type: "image/png" });
document.querySelector("img.preview").src = URL.createObjectURL(blob);
When building a production bundle, import directly from image-stitch and let your bundler handle tree-shaking.
Canvas helper
The browser bundle exposes concatCanvases so you can stitch existing canvases without converting to blobs yourself. Choose whether to receive a Blob or render into a canvas.
import { concatCanvases } from "image-stitch/browser";
const stitchedCanvas = await concatCanvases({
canvases: [topLeft, topRight, bottomLeft, bottomRight],
layout: { rows: 2, columns: 2 },
output: "canvas",
onProgress(current, total) {
progressBar.value = current / total;
}
});
document.body.appendChild(stitchedCanvas);
Switch output to "blob" to receive a downloadable Blob (PNG by default, configurable via mimeType).
Streaming & pipelines
concatStreaming(options) (async generator)
import { concatStreaming } from "image-stitch";
for await (const chunk of concatStreaming({
inputs,
layout: { width: 2048 }
})) {
await writeChunkSomewhere(chunk);
}
concatToStream(options) (Node.js Readable)
import { concatToStream } from "image-stitch";
import { pipeline } from "node:stream/promises";
import { createWriteStream } from "node:fs";
await pipeline(
concatToStream({
inputs,
layout: { columns: 4 },
onProgress(current, total) {
console.info(`Completed ${current}/${total}`);
}
}),
createWriteStream("spritesheet.png")
);
The async generator works everywhere (Node, Deno, browsers). The Node.js helper wraps it in a Readable instance for easy piping.
API reference
concatToBuffer(options)
Concatenate input images and return a Promise<Uint8Array>. A deprecated concat alias remains for backwards compatibility.
type IterableInput =
| T[]
| Iterable
| AsyncIterable;
interface ConcatOptions {
inputs: IterableInput<string | Uint8Array | ArrayBuffer | ImageDecoder>;
layout: {
columns?: number;
rows?: number;
width?: number;
height?: number;
wrapBehavior?: "wrap" | "truncate";
};
decoderOptions?: DecoderOptions;
decoders?: DecoderPlugin[];
outputFormat?: "png";
onProgress?: (completed: number, total: number) => void;
}
- inputs โ Accepts arrays, iterables, or async iterables of Node-friendly sources. In browsers, the
BrowserConcatOptionswrapper also acceptsBlobandHTMLCanvasElementvalues. - layout โ Choose fixed rows/columns or constrain the output width/height. Use
wrapBehaviorto control overflow when hitting a limit. - onProgress โ Callback receiving
(current, total)whenever an input tile finishes streaming.
The supporting typings (ImageDecoder, DecoderOptions, DecoderPlugin, and BrowserConcatOptions) are exported from the package for projects that want full TypeScript coverage.
concatStreaming(options)
Returns an async generator of Uint8Array chunks. Works in both Node.js and browsers.
concatToStream(options)
Node.js convenience wrapper that exposes the streaming concatenator as a Readable stream.
concatCanvases(options) (browser bundle)
interface ConcatCanvasesOptions extends Omit<BrowserConcatOptions, "inputs"> {
canvases: IterableInput<HTMLCanvasElement>;
output?: "blob" | "canvas";
mimeType?: string; // Blob mode only
targetCanvas?: HTMLCanvasElement; // Canvas mode only
}
Returns a Promise<Blob | HTMLCanvasElement> depending on output. When rendering to a canvas, you can provide targetCanvas to reuse an existing element.
Best practices
- Use inputs with matching bit depth and color type to avoid implicit conversions.
- Progress callbacks fire per decoded tileโpipe them into loaders or telemetry for large grids.
- Tree-shake optional formats by providing explicit decoder plugins when bundling for browsers.
- Prefer
concatCanvaseswhen working with<canvas>content; it saves extra blob allocations.