Guides

Putting several devices in one image

How to fit 3, 6, 9, or 12 phones into a single image, what the count and gap controls do, and why the margin between devices holds at 4 percent.

Updated 26 August 2026

Sometimes several screens have to sit side by side in one image. A store screenshot set, the top of a landing page, the cover of a proposal: places where the app has to be taken in at a glance.

The repeating grid format is built for that. Pick how many devices you want, move the gap, and the layout is rebuilt. As the count goes up the gap and the device size are adjusted together so the devices never overlap, which means there is no layout to nudge into place by hand.

  • Open the Repeating grid format in the editor.
  • Pick a count of 3, 6, 9, or 12.
  • Use the gap slider to widen or tighten the space between devices.
  • Every change to the count or the gap rebuilds the layout.

What follows is the rule behind that adjustment and where its limits are. Every number on this page comes from the layout calculation code. Values worked out by substituting the constants are marked (calculated), and the evidence table at the bottom names the file and the symbol behind each sentence.

Does a higher count make them overlap?

It does not. It was not always that way, though. In the repeating grid format, raising the count to 9 used to make the phones in adjacent rows overlap. The reported figures left in the code comments are a phone height of 404 pixels against a vertical gap of 374 pixels. Roughly 30 pixels of vertical bite.

The cause was in how the gap was decided. The gap was written purely as a ratio of the canvas size, and how many pixels the device in that spot would actually be drawn at never entered the calculation. With a device larger than the gap, overlapping was the natural outcome of the structure.

Tighten the gap and they still will not touch

Push the gap slider all the way down and the devices still keep their distance. The gap actually used is the larger of the requested gap and the device size. A minimum margin is added on the device size side, so the gap never lands exactly on the device size and leaves the devices touching.

Grid layout constants
ConstantValueRole
Minimum margin0.04The margin always left between devices (as a ratio of the canvas)
Edge margin0.03The margin left on both sides so the whole grid fits inside the canvas
Fit floor0.2However high the count, the device is not made smaller than this
Search iterations24The number of binary search steps used to find the fit scale

The requested gap is multiplied by the fit scale as well. If the whole layout shrinks while the gap stays at its original value, a high count would not fit the canvas at any scale.

Raise the count and the devices shrink on their own

Holding the gap floor means the grid overflows the canvas as the count rises. So the device and the gap are reduced by the same ratio, and only when it overflows. That is what you are seeing when the devices get smaller at a count of 12, and binary search is how the ratio is found.

Occupancy rises with the scale, which is what makes the search valid. If a scale of 1 already fits, it is left alone. If it overflows, the space between the floor and 1 is halved 24 times, narrowing in on the boundary where it fits. Nothing depends on randomness or on the clock, so the same settings always produce the same layout.

Width and height are checked together. Both directions have to fall inside the area left after the edge margin for a scale to pass.

What happens to the gap when you switch phone kind?

Setting the gap means knowing how many pixels the device is drawn at on screen. The natural pixel size of the frame image is multiplied by the slot scale and divided by the canvas size. The scale is defined against the natural pixels of the frame, so it has to be converted into a canvas ratio before it can be compared with the gap in the same unit.

The order of the calculation matters too. Switching the phone kind between iPhone and Android changes the natural pixel size of the frame, so the kind substitution is applied first and the device size is calculated after. That way the gap is set against the frame that will actually be drawn.

How far the layout goes as the count rises

The premises are the real values of the repeating grid format. The canvas is 1200 × 1200, the natural size of the default phone frame is 726 × 1444, and the slot scale is 0.28. Columns are fixed at 3, the gap slider default of 0.24 becomes the horizontal gap, and the vertical gap is 1.3 times that, 0.312.

Converting the device size with those values gives roughly 0.169 of the canvas across and roughly 0.337 down. Measured vertically, the device is larger than the requested gap of 0.312. That is where the overlap seen earlier comes from. A vertical gap of 0.312 times a canvas of 1200 is 374 pixels, and a device height of 0.337 times 1200 is 404 pixels. The same figures the code comment reported.

(calculated) Layout result per count
CountRows × columnsFit scalePhone heightVertical gapVertical occupancy
31 × 31.00404pxn/a404px
62 × 31.00404px452px856px
93 × 30.85344px392px1128px
124 × 30.61246px294px1128px

Two things can be read from the table. First, in no row is the vertical gap smaller than the phone height. That is what not overlapping actually means here. Second, the gap minus the height is 48 pixels in every row. That is the minimum margin of 0.04 times a canvas of 1200, and it holds even when the fit scale kicks in.

Vertical occupancy stopping at 1128 pixels is not a coincidence either. Taking the edge margin of 0.03 off both sides leaves 0.94 of usable area, and 0.94 times a canvas of 1200 is exactly 1128 pixels. As the count rises, the layout fills up to that line and then moves to shrinking the devices.

What you can pick on screen

Adjustable items in the repeating grid format
ControlValueNote
Count3, 6, 9, 12Pick one of four
Gap0.08 to 0.5, step 0.01The default is 0.24
ColumnsFixed at 3There is no control on screen
Vertical gap1.3 times the horizontal gapNot adjusted separately

Changing the count or the gap rebuilds the layout. However far you narrow the gap, the floor catches it first, so it never drops to devices touching or overlapping. Widen the gap and the requested value passes the floor, so that value becomes the basis, and in a layout where the fit scale applies it is multiplied by that scale.

How the other formats are laid out

Everything up to here applies only to formats whose layout is expanded by calculation. The repeating grid is also the only format where the count and gap controls appear.

Formats with coordinates written out one by one, such as Diagonal stagger, Diagonal cascade, and Phone trio, do not go down this path. Their layout is already decided, and the coordinates were set so that nothing overlaps. Flat-lay uses another path again. It scatters rotation and position slightly around a reference device, so it allows overlap on purpose.

In short

  • The gap is the larger of the requested value and the device size plus the minimum margin.
  • If the grid still overflows the canvas, the device and the gap shrink by the same ratio.
  • That ratio is found by 24 binary search steps, and the same settings always give the same result.
  • The margin between devices holds at 4 percent of the canvas.
  • The fit floor of 0.2 and the case where frame information cannot be obtained are the exceptions where the guard does not apply.

Worth reading next

What this article is based on

What this article is based on
StatementWhere it comes from
A reported overlap in a 3×3 repeating grid, a phone height of 404px against a vertical gap of 374pxthe LayoutMetrics comment in presets/expandLayout.ts and the gridMetrics comment in presets/applyPreset.ts (owner report 2026-08-19)
The gap used = max(requested gap × scale, device size × scale + minimum margin)effectiveGap in presets/expandLayout.ts
Minimum margin 0.04, edge margin 0.03, fit floor 0.2, 24 search iterationsGRID_MIN_MARGIN, GRID_EDGE_MARGIN, MIN_FIT_SCALE, and FIT_ITERATIONS in presets/expandLayout.ts
Why the requested gap is multiplied by the fit scale as wellthe effectiveGap comment in presets/expandLayout.ts
The horizontal and vertical occupancy formulaoccupancy in presets/expandLayout.ts
A scale of 1 that fits is kept, an overflow runs a binary search between the floor and 1fitScale in presets/expandLayout.ts (the early return on fits(1), the lo and hi binary search)
Usable area = 1 − edge margin × 2avail in presets/expandLayout.ts
Device size = frame natural pixels × slot scale ÷ canvas sizegridMetrics in presets/applyPreset.ts
The phone kind substitution is applied before the device size is calculatedthe baseSlots(swapPhoneSlot) → gridMetrics(baseSlots[0]) call order in presets/applyPreset.ts
Falls back to the previous behavior when the frame information or the canvas size is missingthe undefined return from gridMetrics in presets/applyPreset.ts, fit = 1 when metrics is absent in presets/expandLayout.ts
The repeating grid canvas of 1200×1200, slot scale 0.28, default count 6, 3 columnsthe repeating-grid preset in presets/presets.data.ts
The default phone frame natural size of 726 × 1444naturalWidth and naturalHeight of phone-iphone in frames/frames.data.ts
Columns fixed at 3, vertical gap = horizontal gap × 1.3the grid branch of applyFormat in state/editorStore.ts (cols: 3, gapY: opts.gap * 1.3)
Counts of 3, 6, 9, and 12, gap 0.08 to 0.5 in steps of 0.01, default 0.24COUNTS and the SliderRow attributes in panels/FormatPanel.tsx, the useState initial value
The layout is rebuilt when the count or the gap changesapplyGrid calling applyFormat again in panels/FormatPanel.tsx
The count and gap controls appear in the repeating grid onlyGRID_PRESETS = new Set(['repeating-grid']) in panels/FormatPanel.tsx
Formats with coordinates written out do not pass through the expansion calculationthe flat-perspective, perspective-grid, and trio-hero slots in presets/presets.data.ts (no layout given), the early return when layout is absent in presets/expandLayout.ts
Flat-lay is a separate path using rotation and position scatterthe flatlay layout kind 'cluster' in presets/presets.data.ts, the cluster branch in presets/expandLayout.ts
(calculated): the fit scale, phone height, vertical gap, and vertical occupancy per countworked out by substituting the constants and preset values into effectiveGap and fitScale (the table itself is not in the code)

Related guides