# merge-images: Browser and Node.js Image Composition

> merge-images provides a small Promise-based API for layering images, setting coordinates, changing opacity, and selecting output dimensions. It suits applications that need a straightforward client-side or Node.js composition step without writing repetitive canvas setup code.

**lukechilds/merge-images** — Easily compose images together without messing around with canvas

- Repository: https://github.com/lukechilds/merge-images
- Stars: 1,709 · Forks: 164
- Language: JavaScript
- License: MIT
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/lukechilds-merge-images

## A focused API for layered image composition

merge-images solves a narrow problem that otherwise requires a sequence of canvas operations: load several images, place them on one output surface, apply optional transparency, and convert the result into a data URI. Its target users are JavaScript developers building image composition in a browser page or a Node.js process. The README presents the project as a way to avoid repetitive canvas setup, and the public operation is a single `mergeImages(images, [options])` call.

The result is deliberately narrower than a general image editor. It supports ordered image sources, `x` and `y` positions, per-image opacity, output width and height, output format, and quality for formats such as JPEG and WebP. It does not document resizing an individual image, rotation, cropping, filters, masks, text, blending modes, or a separate background color. That makes the API easy to inspect for a simple face composition or overlay workflow, but it also means the library is not a replacement for a full graphics engine.

The function returns a Promise that resolves to a base64 data URI. That output can be assigned directly to an `img` element in browser code, or handled by a Node.js caller. The project has no runtime dependencies listed in `package.json`, and its browser-facing behavior relies on the environment providing image and canvas implementations.

## How ordered layers become a data URI

The documented data flow starts with the `images` argument. It can be an array of valid sources for `new Image()`, or an array of objects containing `src`, optional `x`, optional `y`, and optional `opacity`. The order matters because the README describes images as being overlaid on top of one another. In the first example, `/body.png`, `/eyes.png`, and `/mouth.png` are passed in that order and the resulting image receives the later layers.

When coordinates are omitted, the README does not state a separate per-image placement rule, so callers should not assume capabilities beyond the documented examples. When coordinates are supplied, each object can define an exact pixel position. The output canvas normally takes its width from the widest source image and its height from the tallest source image. `options.width` and `options.height` override those measurements. `options.format` defaults to `'image/png'`, while `options.quality` defaults to `0.92` and applies when the requested format is `image/jpeg` or `image/webp`.

The browser example uses the package as an ES module. The Node.js example passes `Canvas` and `Image` through the options object because `merge-images` does not create those environment-specific objects itself. The exact conversion mechanism is not documented in detail, but the documented result is a Promise resolving to a base64 data URI. For a browser application, that URI can be placed into an image element. For server-side use, the caller receives the same documented result shape.

## Installing merge-images and composing a first image

Install the package with npm when it will be used as a project dependency. The README gives the following command:

```bash
npm install --save merge-images
```

For a quick browser test without a package installation step, the README also provides a script tag:

```html
<script src="https://unpkg.com/merge-images"></script>
```

The first browser example imports the package and combines three image sources. After the Promise resolves, the base64 value is assigned to the `src` property of the selected `img` element.

```js
import mergeImages from 'merge-images';

mergeImages(['/body.png', '/eyes.png', '/mouth.png'])
  .then(b64 => document.querySelector('img').src = b64);
```

The reader should see a composed face image in the targeted `img` element. The returned value is a data URI, beginning with a format marker such as `data:image/png;base64,`. The README does not document error handling for this first example, so callers should add their own rejection handling appropriate to the surrounding application.

The package metadata identifies `dist/index.umd.js` as the CommonJS entry and `dist/index.es2015.js` as the module entry. The build script uses Rollup, while the test and lint scripts run the project’s own build before their respective commands. Those scripts are repository maintenance details rather than part of the runtime API.

## Positions, opacity, and output size controls

For more control, pass an object for each source. The following example places three images at explicit pixel coordinates. The source paths are written as shown in the README, and the Promise receives the resulting data URI.

```js
mergeImages([
  { src: 'body.png', x: 0, y: 0 },
  { src: 'eyes.png', x: 32, y: 0 },
  { src: 'mouth.png', x: 16, y: 0 }
])
  .then(b64 => ...);
```

Opacity is attached to an individual image object. The README uses `0.7` and `0.3` for the eye and mouth layers, leaving the body layer without an explicit opacity value.

```js
mergeImages([
  { src: 'body.png' },
  { src: 'eyes.png', opacity: 0.7 },
  { src: 'mouth.png', opacity: 0.3 }
])
  .then(b64 => ...);
```

Output dimensions are supplied once in the second argument. This example fixes the result at 128 by 128 pixels.

```js
mergeImages(['/body.png', '/eyes.png', '/mouth.png'], {
  width: 128,
  height: 128
})
  .then(b64 => ...);
```

These controls are useful for predictable page assets, but the README does not describe how source images are scaled into a smaller or larger canvas. It only documents that the rendered image receives the requested dimensions. A project that requires per-layer scaling should verify the visual result before depending on this package.

## Node.js requires node-canvas compatibility

Node.js use has one explicit requirement: the caller must provide a Canvas implementation and an Image implementation through the options object. The README names `node-canvas` and shows `Canvas` and `Image` being imported from `canvas`.

```js
const mergeImages = require('merge-images');
const { Canvas, Image } = require('canvas');

mergeImages(['./body.png', './eyes.png', './mouth.png'], {
  Canvas: Canvas,
  Image: Image
})
  .then(b64 => ...);
```

The source paths in this example are filesystem paths beginning with `./`. The README specifically warns that the `Image` supplied for node-canvas must accept a valid image source, and that a relative URL used in browser examples may not be appropriate for the Node.js example. It directs readers to the node-canvas documentation for the accepted source forms.

This arrangement keeps the package independent of a particular server-side image implementation. It also moves compatibility work to the application. The package metadata lists `canvas` under `devDependencies`, not runtime `dependencies`, so a consuming Node.js application is responsible for supplying the compatible implementation. The README does not document supported Node.js versions, installation instructions for `canvas`, or behavior when the supplied classes cannot load an image. Those are concrete points to check in a server deployment.

## Browser loading, CORS, formats, and operational limits

In a browser, the package can be imported as a module or loaded from unpkg for quick testing. Images must be valid sources for `new Image()`. When combining images from another origin, the `crossOrigin` option can set the attribute used by the `Image` instances. The README gives `Anonymous` as an example value for CORS-enabled images, but it does not specify a particular server policy or browser matrix.

The default output format is `'image/png'`. `options.quality` is documented as a number from 0 to 1 and is relevant to `image/jpeg` and `image/webp`. The README does not say how browser support for those output formats is detected, nor does it promise a particular compression result. The default width and height are based on the widest and tallest source images, which can make output size depend on input assets.

The API also has a boundary around image size and resource use, although the README does not provide numerical limits. Large source images can produce a large data URI, and the package does not document streaming output, binary buffers, or server-side file writing. The documented interface is therefore most suitable when a caller wants a composited data URI in memory. Teams needing metadata extraction, color management, animated image handling, or advanced compositing should treat those capabilities as absent rather than infer them from the canvas topic labels.

## Licensing, maintenance, and a practical alternative

The repository identifies the project as MIT licensed and the README states `MIT © Luke Childs`. That information indicates the stated licensing model, but it is not legal advice and does not answer questions about dependencies or use in a particular product. The package metadata contains no runtime dependencies, while development and test tooling includes packages such as Rollup, AVA, nyc, and canvas.

The repository was not archived, and its last push was on 2026-05-17. There are no GitHub releases, so release history and a stable upgrade sequence cannot be established from the available repository information. The package version is `2.0.0` in `package.json`. An upgrade therefore requires checking the project’s published package state and its compatibility with the browser or Node.js environment used by the application.

A direct alternative is to write the canvas composition code with the platform’s Canvas API. That approach exposes lower-level operations and gives the application ownership of image loading, sizing, drawing order, export, and error handling. merge-images reduces that setup to a function call, which is a meaningful difference for simple overlays. The trade-off is a smaller documented feature set and a dependency on supplied `Canvas` and `Image` implementations outside the browser. A full image-processing library is another approach when the task needs filters, masks, transformations, or format-specific workflows that the documented API does not expose.

## Conclusion

Adopt merge-images for a small, declarative composition task in a browser or Node.js script, especially when the required operations are layering, positioning, opacity, and fixed output dimensions. Do not choose it for advanced graphics editing, complex masking, or transformations that the documented API does not cover. Before adoption, verify that your image sources work with the chosen environment, particularly because Node.js requires compatible node-canvas classes and valid file paths.

## FAQ

### How do I install merge-images?

Run `npm install --save merge-images` as documented in the README. For a quick browser test, the README also shows loading the package from `https://unpkg.com/merge-images` with a script tag.

### Does merge-images work in Node.js?

Yes, according to the README, Node.js usage requires a compatible canvas implementation and Image implementation passed through the options object. The example imports `Canvas` and `Image` from `canvas` and uses filesystem paths such as `./body.png`.

### What does mergeImages return?

The function returns a Promise that resolves to a base64 data URI. The README demonstrates assigning that value to the `src` property of an `img` element.

### Can I change image positions and opacity?

Yes. Each image can be supplied as an object with `src`, `x`, `y`, and `opacity` fields. Output dimensions can be controlled separately with `options.width` and `options.height`.

### What is the default output size and format?

The default format is `'image/png'`. Unless specified, the output width is the width of the widest source image and the output height is the height of the tallest source image.

## Sources

- [Issues](https://github.com/lukechilds/merge-images/issues)
- [License: MIT](https://github.com/lukechilds/merge-images/blob/master/LICENSE)
- [lukechilds/merge-images on GitHub](https://github.com/lukechilds/merge-images)
- [README](https://github.com/lukechilds/merge-images/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/lukechilds-merge-images
