Combine Images: 5 Methods for Any Workflow
Combining images is one of those tasks that sounds simple until you actually need to do it. You have two screenshots that belong in one comparison. A set of product photos that need to form a grid. Before-and-after shots for a case study. Documentation images that make more sense as a single figure than four separate ones.
The challenge is that "combine images" means different things in different contexts. Side-by-side comparison? Vertical stack? A 3x3 grid? An overlay with transparency? Each layout calls for a different approach, and the right tool depends on whether you need a one-off result or a repeatable pipeline.
This guide covers five methods — from quick CLI commands to programmatic approaches — so you can pick the one that fits your workflow and move on.
One thing all methods share: your source images need consistent dimensions before combining. Mismatched sizes produce ugly results with uneven edges and stretched content. Pixotter's resize tool handles this in your browser — drop your images, set matching dimensions, and download them ready to combine. No upload, no account.
Prep your images first
Resize and crop images to matching dimensions before combining. Free, instant, no upload required.
Methods Comparison
Not every tool handles every layout. Here is what each method supports and where it runs:
| Method | Side-by-Side | Vertical Stack | Grid | Overlay | Platform | Cost |
|---|---|---|---|---|---|---|
| ImageMagick 7.1 | Yes | Yes | Yes | Yes | Windows, macOS, Linux | Free |
| FFmpeg 7.0 | Yes | Yes | Yes | Yes | Windows, macOS, Linux | Free |
| Python Pillow 10.4 | Yes | Yes | Yes | Yes | Any (Python) | Free |
| HTML Canvas | Yes | Yes | Yes | Yes | Any browser | Free |
| GIMP 2.10.36 | Yes | Yes | Yes | Yes | Windows, macOS, Linux | Free |
All five handle every layout type, but the trade-offs are in speed, automation potential, and learning curve. ImageMagick gives you the most flexibility in a single command. Pillow scales to thousands of images with a script. HTML Canvas works without installing anything. GIMP is best when you need visual precision on a single composition. FFmpeg is overkill for still images but useful if you already have it in your pipeline.
Licenses: ImageMagick is Apache 2.0 (free, open source). FFmpeg is LGPL 2.1 for core libraries and GPL 2+ when compiled with certain codecs — check your build. Pillow is HPND (open source, MIT-like). GIMP is GPLv3+. HTML Canvas is a browser-native API, no license applies.
Try it yourself
Reduce file size without visible quality loss — free, instant, no signup. Your images never leave your browser.
Method 1: ImageMagick (CLI — Recommended)
ImageMagick 7.1 is the Swiss Army knife of image manipulation from the command line. If you need to combine images quickly and repeatably, start here.
Install it from imagemagick.org or via your package manager:
# macOS
brew install imagemagick
# Ubuntu/Debian
sudo apt install imagemagick
# Windows (winget)
winget install ImageMagick.ImageMagick
Side by side (horizontal)
The +append operator places images left to right:
magick input1.jpg input2.jpg +append side-by-side.jpg
This works with any number of images. Three images:
magick left.jpg center.jpg right.jpg +append panorama.jpg
Vertical stack
The -append operator stacks images top to bottom:
magick top.jpg bottom.jpg -append stacked.jpg
Useful for combining screenshots into a single scrollable view, or stacking before/after comparisons vertically.
Grid layout with montage
The montage command arranges images into rows and columns:
magick montage img1.jpg img2.jpg img3.jpg img4.jpg \
-geometry 400x400+10+10 \
-tile 2x2 \
grid.jpg
Breaking that down:
-geometry 400x400+10+10— resize each tile to fit within 400x400 pixels, with 10px horizontal and 10px vertical gaps between tiles-tile 2x2— arrange in a 2-column, 2-row grid- Swap
2x2for3x3,4x2, or any arrangement you need
Adding gaps and borders
For a clean gap between side-by-side images, use -splice to insert spacing:
magick input1.jpg \
\( -size 20x1 xc:white \) \
input2.jpg \
+append \
with-gap.jpg
This inserts a 20px-wide white strip between the two images. Change xc:white to xc:black or any hex color (xc:#f0f0f0) for different backgrounds.
For a border around each image before combining:
magick input1.jpg -border 5 -bordercolor "#333333" \
input2.jpg -border 5 -bordercolor "#333333" \
+append bordered.jpg
Overlay (composite)
To place one image on top of another — a watermark, a logo, or a picture-in-picture layout:
magick background.jpg overlay.png \
-gravity southeast \
-geometry +20+20 \
-composite \
result.jpg
The -gravity southeast positions the overlay at the bottom-right corner, offset 20px from each edge. The overlay image should have transparency (PNG with alpha channel) for clean compositing.
Method 2: Python Pillow (Programmatic)
When you need to combine images as part of a script — processing hundreds of product photo pairs, generating comparison grids for a report, or building a CI pipeline that stitches screenshots — Pillow 10.4 is the right choice.
Install it:
pip install Pillow==10.4.0
Side by side
from PIL import Image
def combine_side_by_side(paths, output_path, gap=0, bg_color=(255, 255, 255)):
"""Combine images horizontally with optional gap."""
images = [Image.open(p) for p in paths]
# Use the tallest image as the canvas height
max_height = max(img.height for img in images)
total_width = sum(img.width for img in images) + gap * (len(images) - 1)
canvas = Image.new("RGB", (total_width, max_height), bg_color)
x_offset = 0
for img in images:
# Center vertically if heights differ
y_offset = (max_height - img.height) // 2
canvas.paste(img, (x_offset, y_offset))
x_offset += img.width + gap
canvas.save(output_path, quality=90)
print(f"Saved: {output_path} ({total_width}x{max_height})")
# Usage
combine_side_by_side(
["photo1.jpg", "photo2.jpg", "photo3.jpg"],
"comparison.jpg",
gap=20
)
Grid layout
from PIL import Image
import math
def combine_grid(paths, output_path, cols=3, tile_size=(400, 400), gap=10, bg_color=(255, 255, 255)):
"""Arrange images in a grid with uniform tile size."""
images = [Image.open(p).resize(tile_size, Image.LANCZOS) for p in paths]
rows = math.ceil(len(images) / cols)
canvas_w = cols * tile_size[0] + (cols - 1) * gap
canvas_h = rows * tile_size[1] + (rows - 1) * gap
canvas = Image.new("RGB", (canvas_w, canvas_h), bg_color)
for i, img in enumerate(images):
row, col = divmod(i, cols)
x = col * (tile_size[0] + gap)
y = row * (tile_size[1] + gap)
canvas.paste(img, (x, y))
canvas.save(output_path, quality=90)
print(f"Saved: {output_path} ({canvas_w}x{canvas_h})")
# Usage: 3x2 grid of product photos
combine_grid(
["prod1.jpg", "prod2.jpg", "prod3.jpg",
"prod4.jpg", "prod5.jpg", "prod6.jpg"],
"product-grid.jpg",
cols=3,
tile_size=(600, 600),
gap=15
)
The tile_size parameter forces every image to the same dimensions, which is critical for a clean grid. If your source images are different sizes, resize them to matching dimensions first — Pixotter's batch resize handles this without installing anything.
Method 3: HTML Canvas (Browser-Side)
If you need to combine images inside a web application — no backend, no installs — the HTML Canvas API does the job. This is the same approach Pixotter uses for client-side image processing.
<canvas id="output" style="display:none;"></canvas>
<script>
async function combineImages(urls, direction = "horizontal", gap = 0) {
const images = await Promise.all(
urls.map(url => new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = reject;
img.src = url;
}))
);
const canvas = document.getElementById("output");
const ctx = canvas.getContext("2d");
if (direction === "horizontal") {
canvas.width = images.reduce((sum, img) => sum + img.width, 0) + gap * (images.length - 1);
canvas.height = Math.max(...images.map(img => img.height));
let x = 0;
for (const img of images) {
ctx.drawImage(img, x, (canvas.height - img.height) / 2);
x += img.width + gap;
}
} else {
canvas.width = Math.max(...images.map(img => img.width));
canvas.height = images.reduce((sum, img) => sum + img.height, 0) + gap * (images.length - 1);
let y = 0;
for (const img of images) {
ctx.drawImage(img, (canvas.width - img.width) / 2, y);
y += img.height + gap;
}
}
// Export as blob
canvas.toBlob(blob => {
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "combined.jpg";
a.click();
URL.revokeObjectURL(url);
}, "image/jpeg", 0.9);
}
</script>
This runs entirely in the browser. No server roundtrip, no file uploads. The main limitation is memory — combining many large images (20+ at 4000px each) can hit browser memory limits. For those cases, resize your source images down first.
Method 4: FFmpeg (CLI)
FFmpeg 7.0 is primarily a video tool, but its filter system handles image combining through the hstack, vstack, and xstack filters.
Side by side
ffmpeg -i left.jpg -i right.jpg \
-filter_complex hstack=inputs=2 \
side-by-side.jpg
Vertical stack
ffmpeg -i top.jpg -i bottom.jpg \
-filter_complex vstack=inputs=2 \
stacked.jpg
2x2 grid
ffmpeg -i img1.jpg -i img2.jpg -i img3.jpg -i img4.jpg \
-filter_complex "[0][1]hstack=inputs=2[top];[2][3]hstack=inputs=2[bottom];[top][bottom]vstack=inputs=2" \
grid.jpg
FFmpeg works well if it is already in your toolchain. For image-only workflows, ImageMagick is more intuitive. FFmpeg shines when you are combining images into video frames or need to apply the same filters you use for video processing.
Method 5: GIMP (GUI)
GIMP 2.10.36 is the manual-precision option. Use it when you need pixel-perfect placement, blending modes, or visual adjustments that CLI tools cannot easily express.
- Open GIMP. Go to File > Open as Layers and select all images you want to combine.
- Each image loads as a separate layer. Go to Image > Canvas Size and increase the canvas to fit your desired layout (e.g., double the width for side-by-side).
- Use the Move tool (M) to position each layer where you want it.
- For precise alignment: Image > Guides > New Guide (By Percent) at 50% to mark the center.
- When satisfied, go to Image > Flatten Image to merge all layers.
- Export via File > Export As and choose your output format.
GIMP also supports Script-Fu and Python-Fu for batch operations, but if you are scripting, Pillow or ImageMagick are simpler choices.
Preparing Images Before Combining
The single biggest factor in a clean combined image is matching dimensions. When images have different sizes, you get one of three bad outcomes:
- Uneven edges — side-by-side images with mismatched heights create a ragged bottom edge
- Stretched content — forcing images to the same size without preserving aspect ratio distorts them
- Wasted space — padding smaller images to match larger ones adds empty areas
Fix this before combining:
Decide your target dimensions. For side-by-side comparisons, all images should have the same height. For grids, all images should be the same width and height. For vertical stacks, match the width.
Resize to matching dimensions. Pixotter's resize tool handles this in a few seconds — drop your images, set the target dimensions, toggle "maintain aspect ratio" if you want proportional scaling. Everything processes in your browser.
Crop to matching aspect ratios. If your images have different aspect ratios and you need exact matching, crop them first. A 16:9 image next to a 4:3 image will look off even if heights match.
Convert to the same format. Mixing PNG and JPEG sources can cause color space mismatches. Pick one format for your source images. Pixotter's converter handles batch format conversion without quality loss.
This preparation step takes 30 seconds and makes the difference between a professional result and an obviously cobbled-together image.
Optimizing the Combined Image
Combined images are large. Stitching two 2MB photos side by side does not produce a 4MB result — it is often larger because the combined image has more pixels and the compression algorithm has more data to encode. A 2x3 grid of product photos can easily hit 8-10MB.
That is too large for the web, email, or most upload forms. Compress after combining:
JPEG for photographs. Use quality 80-85. At this setting, a 10MB combined photo typically drops to 1-2MB with no visible quality loss. JPEG is ideal for product comparisons, before-and-after shots, and photo collages.
PNG for screenshots and text-heavy images. If your combined image contains UI screenshots, code editor captures, or diagrams with text, PNG preserves those sharp edges that JPEG would blur. The files will be larger — optimize with Pixotter's PNG compressor to strip metadata and apply lossless optimization.
WebP for the best balance. If your target platform supports WebP (most do in 2026 — 97% browser coverage), use it. WebP produces 25-50% smaller files than equivalent JPEG at the same visual quality. Convert your combined output to WebP using Pixotter's format converter.
Run your combined image through Pixotter's compressor before publishing. It strips metadata, optimizes encoding, and reduces file size — all in your browser, no upload required. A 6MB combined JPEG typically compresses to under 2MB at quality 80 with no visible difference.
Common Use Cases
Before-and-after comparisons
Side-by-side is the standard layout. Resize both images to identical dimensions, combine horizontally with a thin separator:
magick before.jpg \( -size 4x1 xc:#cccccc \) after.jpg +append comparison.jpg
The 4px gray line between images makes the boundary clear without being distracting.
Social media grids
Instagram carousels and Pinterest collages use specific aspect ratios. For a 2x2 Instagram grid (1080x1080 total):
magick montage img1.jpg img2.jpg img3.jpg img4.jpg \
-geometry 530x530+10+10 \
-tile 2x2 \
-background white \
instagram-grid.jpg
Each tile is 530px with 10px gaps, fitting neatly into a 1080x1080 square. Resize your source images to square aspect ratios first using Pixotter's crop tool.
Documentation and tutorials
Vertical stacking works well for step-by-step screenshots:
magick step1.png step2.png step3.png -append tutorial-steps.png
This produces a single tall image showing the progression. Save as PNG to preserve text sharpness in the screenshots.
Product photo collages
E-commerce listings often show multiple angles in a single image. A 3-column layout works well:
magick montage front.jpg side.jpg back.jpg detail1.jpg detail2.jpg detail3.jpg \
-geometry 600x600+8+8 \
-tile 3x \
-background white \
product-collage.jpg
The -tile 3x flag means 3 columns, as many rows as needed. Batch resize your product photos to square dimensions before running this command for consistent results.
FAQ
How do I combine two images side by side?
The fastest method is ImageMagick's +append operator: magick image1.jpg image2.jpg +append output.jpg. This places images left to right. For vertical stacking, use -append instead. Both images should have matching heights (for horizontal) or matching widths (for vertical) to avoid uneven edges. Resize them to matching dimensions first if needed.
Can I combine images without installing software?
Yes. The HTML Canvas API in any modern browser can combine images with JavaScript — no installs, no uploads, no server. The code example in this article runs entirely client-side. For preparing images before combining (resizing, cropping, format conversion), Pixotter does all of that in-browser too.
How do I merge images into a grid?
ImageMagick's montage command is the most flexible option: magick montage *.jpg -geometry 400x400+10+10 -tile 3x3 grid.jpg. This creates a 3x3 grid with 400px tiles and 10px gaps. Python Pillow's approach (shown above) gives you more control when scripting. For consistent results, resize all images to identical dimensions before creating the grid.
What is the best format for combined images?
It depends on the content. JPEG at quality 80-85 for photographs and photo collages. PNG for screenshots, diagrams, or any image with text. WebP for the best compression-to-quality ratio when your platform supports it (97% of browsers do). Avoid combining into BMP or TIFF for web use — the files will be enormous.
How do I add space between combined images?
In ImageMagick, insert a colored spacer between images: magick img1.jpg \( -size 20x1 xc:white \) img2.jpg +append output.jpg. The 20x1 creates a 20px-wide white gap. In Python Pillow, use the gap parameter in the combine_side_by_side function from this article. For montage grids, the +10+10 in -geometry controls horizontal and vertical spacing.
How do I combine images of different sizes?
First, decide what "different sizes" means for your layout. If heights differ for a side-by-side layout, the result will have uneven edges. The clean approach: resize all images to matching dimensions before combining. If you cannot resize, ImageMagick's append will use the largest dimension and leave whitespace. Pillow's example above centers smaller images vertically. The best results always come from consistent source dimensions.
Try it yourself
Resize to exact dimensions for any platform — free, instant, no signup. Your images never leave your browser.