Skip to content

About

Combine images side by side, vertically or in a grid with Canvas. Zero dependencies; browser, Web Workers and Node.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

combine-images

Combine images side by side, vertically or in a grid, with optional gaps, padding, background, rounded corners and labels. Zero dependencies. Runs in the browser, in Web Workers (OffscreenCanvas) and in Node with @napi-rs/canvas.

Docs and live demo: photomerge.app/developers/combine-images

Don't need code? The same layouts run as a free, no-upload editor at PhotoMerge.

npm install combine-images

Browser

import { combineImages, canvasToBlob } from 'combine-images';

// Files from <input type="file" multiple>, Blobs, URLs or loaded <img> elements
const canvas = await combineImages(input.files, {
  direction: 'horizontal', // 'horizontal' | 'vertical' | 'grid'
  gap: 16,
  padding: 16,
  background: '#ffffff',
  labels: ['Before', 'After'],
});

const blob = await canvasToBlob(canvas, 'image/jpeg', 0.92);

Without a bundler, load it from a CDN:

<script type="module">
  import { combineImages } from 'https://cdn.jsdelivr.net/npm/combine-images@1/src/index.js';
</script>

Node

import { writeFile } from 'node:fs/promises';
import { createCanvas, loadImage } from '@napi-rs/canvas';
import { combineImages } from 'combine-images';

const canvas = await combineImages(['a.jpg', 'b.jpg', 'c.jpg'], {
  direction: 'grid',
  gap: 12,
  createCanvas,
  loadImage,
});
await writeFile('grid.png', await canvas.encode('png'));

Layouts

direction What it does Size options
horizontal (default) One row. Every image keeps its shape and is scaled to the same height. height (default: the shortest image)
vertical One column. Every image keeps its shape and is scaled to the same width. width (default: the narrowest image)
grid Equal cells; a short last row is centred. columns ('auto' or a number), cellWidth, cellHeight, fit: 'cover' | 'contain'

Options

Option Default
gap 0 Space between images, in output pixels
padding 0 Space around the result
background '#ffffff' Any canvas color; null for transparent (use PNG or WebP)
radius 0 Corner radius of each image
labels [] Text per image, by index
labelPosition 'bottom-left' top-left, top-center, bottom-left, bottom-center
labelColor, labelBackground, labelFont, labelSize white on 60% black, system font, 0.065 labelSize is a share of the shorter cell side
maxSize none Scale the result down so its longest side fits
scale 1 Multiply the output size
canvas / createCanvas OffscreenCanvas or <canvas> Where to draw
loadImage createImageBitmap in browsers How to load sources that are not images yet

Lower-level API

computeLayout(sizes, options) is pure math: give it { width, height } objects and it returns the output size and a rectangle per image, so you can render with WebGL, SVG, sharp or anything else.

import { computeLayout, drawLayout } from 'combine-images';

const layout = computeLayout([{ width: 1200, height: 800 }, { width: 800, height: 1200 }], { gap: 10 });
// { width: 1743, height: 800, cells: [{ index: 0, x: 0, y: 0, width: 1200, height: 800 }, ...] }

drawLayout(canvas, images, layout, { background: '#000', scale: 0.25 }); // e.g. a quick preview

Limits

Browsers cap canvas size (Safari at about 16.7 million pixels, Chrome and Firefox at about 268 million, and 32,767 px per side). Use maxSize when combining many large photos.

License

MIT © PhotoMerge

About

Combine images side by side, vertically or in a grid with Canvas. Zero dependencies; browser, Web Workers and Node.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages