Photo Merge — TypeScript source
Combine photos side by side, stacked, or in a grid — uniform cells, adjustable gap, one PNG out. Runs entirely in your browser.
This is the TypeScript implementation — the same logic the interactive tool runs, in a shareable, citable form.
// Photo merge — display snippet. The live tool adds stacked/grid layouts and
// gap control; canvas-bound, so no Go twin.
export async function mergeSideBySide(files: File[], gap = 12): Promise<Blob> {
const bitmaps = await Promise.all(files.map((f) => createImageBitmap(f)));
const cellH = Math.max(...bitmaps.map((b) => b.height));
const cellW = Math.max(...bitmaps.map((b) => b.width));
const canvas = document.createElement('canvas');
canvas.width = bitmaps.length * cellW + (bitmaps.length + 1) * gap;
canvas.height = cellH + 2 * gap;
const ctx = canvas.getContext('2d')!;
ctx.fillStyle = '#0f172a';
ctx.fillRect(0, 0, canvas.width, canvas.height);
bitmaps.forEach((bmp, i) => {
const scale = Math.min(cellW / bmp.width, cellH / bmp.height);
const w = Math.round(bmp.width * scale);
const h = Math.round(bmp.height * scale);
ctx.drawImage(
bmp,
gap + i * (cellW + gap) + Math.round((cellW - w) / 2),
gap + Math.round((cellH - h) / 2),
w,
h,
);
});
return new Promise<Blob>((resolve, reject) =>
canvas.toBlob((b) => (b ? resolve(b) : reject(new Error('encode failed'))), 'image/png'),
);
}
Also available in 8 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →