Arrange photos in one of twelve templates whose every cell rectangle, gap, margin and radius is stated in the output's own pixels before anything is drawn.
Initializing in your browser…
Turn an image into a data URL whose payload is decoded again and compared byte for byte with the file, with the media type taken from the file's own signature rather than its extension and the size cost of embedding stated in full.
Compare two images with a perceptual engine (pixelmatch) that ignores anti-aliasing, or an exact byte engine. Groups changes into regions with bounding boxes, reports SSIM, and handles size mismatches with an explicit policy.
Whole-pixel crops with the rectangle typed or dragged, checked by decoding the output and comparing it with the pixels that went in. Quarter turns and mirrors are exact permutations, so nothing is clipped.
Four solid colours in a 2 by 2 grid, so the output’s colour map IS the layout and every rectangle can be measured off the file.
Input
4 pictures · 2 by 2 grid · gap 24 px · radius 20 px · 1200 px wide
What the file really has
asked before after gap at 1200 px 24 288 24 gap at 800 px 24 192 24 gap at 2400 px 24 576 24 corner radius 20 123 20 six pictures, four slots drawn 4 4 said so no yes PNG asked after a JPEG JPEG PNG
The Gap slider said pixels and the export computed `(gap / 100) * min(width, height)`, so the number was a percentage of the short side: twelve times the label at the default output size and twenty-four times at 2400 px. The radius had the same fault. Every length is now a pixel of the output, and the page prints the plan before it draws it.
Arrange photos in one of twelve templates whose every cell rectangle is stated in the output’s own pixels before anything is drawn. The gap, margin, corner radius and border are real pixels, the crop each cell makes is reported as a percentage, and the preview is the file you download.
Combine several photographs into one picture at exactly the size and shape a network wants.
Put two pictures side by side in a diptych, with the crop each one takes stated rather than guessed.
Show several work samples in a grid whose spacing is the same whatever resolution you export at.
Four pictures, a 24 px gap and a 12 px margin at 1200 px wide gives four 582 by 582 cells at (12, 12), (606, 12), (12, 606) and (606, 606), which is what the file has. At 800 and at 2400 px the gap is still 24 px.
Under Fill the page says 50.0 percent of the picture is shown and 50.0 percent cropped, because the cell is 1.000 to 1 and the picture is 2.000 to 1. Under Fit the whole picture is drawn at half the cell’s height with the background above and below.
A collage is a claim about rectangles: this template puts that picture in that part of the canvas, at that size, cropped by that much. Every one of those is arithmetic, so this version states the whole plan BEFORE it draws anything, prints it on the page, and can be checked against the file afterwards. Feeding it solid colours makes the check exact, because the output’s colour map is the layout: the test suite reads every cell rectangle off the produced file and compares it with the plan, and they agree to the pixel.
Three lengths used to mean something other than what they said. **The Gap slider read "8px" and put 96 px in the file.** The export computed `(gap / 100) * min(width, height)`, so the number on the slider was a percentage of the canvas’s short side while the label and the live preview both said pixels: twelve times the stated gap at the 1200 px default, and twenty-four times at 2400 px, which meant the same setting produced a different collage at every output size. **The Corner Radius had the same fault**, producing about 51 px for a slider reading 8. Gap, margin, corner radius and border are now pixels of the output, all four are separate settings (the old one had no margin control at all and simply made the outer margin half the inner gap), and the page says so beside them.
**Pictures past the template’s slot count were dropped in silence.** Six pictures in a four-slot template produced a file with four of them in it and nothing anywhere said which two were missing. The plan now counts them, names them in the panel, and counts empty slots the other way. The templates themselves were checked the same way: coverage is computed exactly by coordinate compression, and the old Focus Center covered 68 percent of its canvas with 4 percent covered twice, so nearly a third of it was background by accident. All twelve templates now tile their canvas exactly, which the test suite asserts.
How a picture fills its cell is a real choice with a measured consequence. Fill crops the picture to the cell’s shape through the same `planGeometry` the resizer uses, and the page reports the share that survives: a 2:1 picture in a square cell shows exactly half of itself. Fit shows the whole picture and lets the background take the rest. Stretch squeezes it and names both shapes so you can see what it cost. Zoom tightens the crop about its centre (200 percent shows a quarter of the area), and dragging inside a cell on the preview pans it, converting the pointer through the preview’s bounding rectangle so the picture moves by the amount the pointer did on the output.
The preview IS the exported file: the same blob is shown and saved, so there is no second renderer to drift. Everything that changes the file regenerates it, including the output size, the format and the quality, which the previous version left out of its effect entirely, so **asking for PNG after a JPEG handed over the JPEG with a `.png` name**. The format is now read back off the produced bytes, the extension comes from what the file IS, and the page reports the size, the type and the byte count. Filters are Filter Effects Module 1 strings, shown on the page as the `ctx.filter` value they become, so the thumbnail and the file agree by construction. Undo and redo are two stacks and record every committed change; before, `saveHistory` was called from exactly two places and almost nothing was undoable.
Yes, and it is asserted in the test suite by measuring the file. The previous version computed the gap as a percentage of the canvas’s short side while calling it pixels: a slider reading 8 put 96 px in a 1200 px collage and 192 px in a 2400 px one.
The panel says how many have no slot and suggests a template with more. The previous version drew the first N and said nothing.
The page tells you. Under Fill the picture is cropped to the cell’s shape and the plan reports the share that survives; a 2:1 photograph in a square cell shows exactly half of itself. Under Fit the whole picture is shown with background around it.
It is the same file. The preview is the encoded blob, so what you are looking at is the bytes you will save, and every setting that changes those bytes regenerates it.
Yes, and it is read back off the produced bytes rather than assumed. The previous version did not regenerate when the format changed, so asking for PNG after a JPEG saved the JPEG with a .png name.
No. Everything is decoded, drawn and encoded locally.
Images are decoded, edited, and exported entirely inside this browser tab. No originals, exports, or metadata are uploaded.