DEV Community

BY L
BY L

Posted on

Designing a Browser-Local Image Merge Pipeline Without Uploads

Combining several images into one canvas looks like a server-side job, but modern browser APIs make a local-first pipeline practical. That choice matters when the source files are screenshots, receipts, product photos, or reference images that users may not want to upload to an unknown processing queue.

Why keep the merge in the browser?

A browser-local workflow has three useful properties:

  • Privacy: the selected files can stay on the user's device.
  • Fast feedback: decoding, layout changes, and previews do not wait for a round trip.
  • Predictable cleanup: closing or reloading the tab releases the in-memory working set instead of leaving a cloud project behind.

Local processing does not remove every risk, but it removes the ordinary image-upload path from the core task.

A practical merge pipeline

The core loop can be small and explicit:

  1. Accept only the image types the interface documents.
  2. Decode each selected file with createImageBitmap() or an Image element.
  3. Normalize orientation and calculate a destination rectangle for every item.
  4. Size one output canvas from the complete layout plan.
  5. Draw backgrounds, gaps, and images in a deterministic order.
  6. Export with canvas.toBlob(), then verify that the blob is non-empty before enabling download.
async function decodeFiles(files) {
  const decoded = [];

  for (const file of files) {
    if (!/^image\/(jpeg|png|webp)$/.test(file.type)) {
      throw new Error('Unsupported image type');
    }

    const bitmap = await createImageBitmap(file);
    decoded.push({ file, bitmap, width: bitmap.width, height: bitmap.height });
  }

  return decoded;
}
Enter fullscreen mode Exit fullscreen mode

The important design decision is to compute the whole layout before drawing. A horizontal merge sums destination widths and uses the tallest destination height. A vertical merge does the inverse. A grid also needs column widths, row heights, gaps, outer padding, and the chosen fit rule. Keeping those values in one plan makes the preview and final export use the same geometry.

Contain, cover, and alignment

Users usually expect one of two fitting modes:

  • Contain preserves the entire source image and may leave empty space.
  • Cover fills the destination cell and crops the excess.

Alignment then decides where the image or crop sits: start, center, or end on each axis. These controls should change only the planned source and destination rectangles; the render loop can remain boring. Boring render code is easier to test.

Guardrails prevent browser crashes

Canvas dimensions and memory usage can grow surprisingly fast. A 12,000 by 8,000 RGBA canvas needs hundreds of megabytes before export. Useful protections include:

  • a documented file-count limit;
  • a per-file byte limit;
  • a maximum output edge and total pixel count;
  • proportional downscaling when the planned output crosses those limits;
  • closing decoded ImageBitmap objects when they are removed;
  • revoking temporary object URLs during cleanup.

These limits are part of the product contract, not just implementation details. Show the user when scaling occurs and keep the originals untouched.

Verification should use real files

A merge tool deserves more than a button-click smoke test. Good fixtures use images with known colors and dimensions so the test can check:

  • the exact output width and height;
  • the pixel color at each placement boundary;
  • gap and background colors;
  • ordering after drag-and-drop;
  • PNG, JPEG, and WebP signatures;
  • behavior when a proposed canvas exceeds the safety limit;
  • the absence of image-upload network requests during the core workflow.

It is also worth testing at a narrow viewport. File cards, reorder controls, export settings, and the preview should remain usable without horizontal overflow.

A working reference

MergeImage is a free browser-local implementation of this approach. It supports horizontal, vertical, and grid merging, plus screenshot stitching, comparison, overlay, image splitting, sprite sheets, and printable contact sheets. The source images are processed in the active browser tab, and the interface exposes the limits before export.

The larger lesson is simple: plan first, render once, measure the actual output, and make privacy claims match the network behavior. Those choices turn a small canvas demo into a tool people can understand and verify.

Top comments (0)