Auto Layout Engine

Photos and words, automatically arranged.

Two Algorithms

Grid

Gallery-wall style. Images and text align in precise rows with uniform gaps. A genetic algorithm searches thousands of arrangements to find the best fit.

Text-aware rowsPreciseBalanced

Phyllo

Named after phyllotaxis — the golden-angle spiral in sunflower seeds. Photos and text bloom outward from center. A constraint solver guarantees zero overlap.

Text-aware spiralOrganicNatural hierarchy

How text joins the layout

Text isn’t glued on top — it enters the same layout search as your photos. Watch what changes as we add a greeting, a paragraph, and a title/subtitle pair to the same three photos. The engine is Phyllo; each newly-added scrap is ringed in accent.

  1. 01

    Photos only

    baseline · photos only

    The starting point. Three photos arranged by Phyllo on a 16:9 canvas. Every step below adds text to this same set — same photos, same seed, same canvas.

  2. 02

    Add a short greeting

    short · ratio ≈ 4.0 · maxArea ceiling

    “Good morning!” fits in one row at the reference size, so it’s classified as short: a wide tile with a max-area cap. The engine slots it alongside the photos without letting it swallow the canvas.

  3. 03

    Add a long paragraph

    long · flexible ratio · minArea floor

    A wrapping paragraph needs floor area so the font size stays readable. Phyllo raises the text to meet minArea and proportionally shrinks the photos — images can drop to 30% of their original area before the floor kicks in.

  4. 04

    Add a title + subtitle

    paired · one box, two lines

    A paired scrap is one layout item with one ratio — the renderer stacks the title over the subtitle and scales both together, so they never drift apart when the box shrinks.

All four layouts use the same seed and same three photos. The only input that changes is the text — so any difference you see between frames is the engine responding to text, not to a different random draw.

Pipeline

Follow the same photos and text scraps through every stage. Photos are tinted photos and text text; together they make up the list the engines work on.

  1. 01

    Collect items

    Photos and text scraps become a single flat list of items. From here on, the engines don’t care which is which — they only see an id, a ratio target, and (for text) a min or max area budget.

    Photos (intrinsic ratio)

    4:3 landscape1.333:4 portrait0.751:1 square1.00

    Text scraps (ratio + area computed)

    Good morning!Wishing you a day full of warmth, …
  2. 02

    Measure text

    For every text scrap the engine decides one thing first: does it fit on one row at the reference size? That answer drives both the preferred aspect ratio and whether the box has a min-area floor (long text) or max-area ceiling (short text).

    Good morning!

    SHORTratio ∈ [3.60, 5.63]maxArea 2.6% of canvas

    Wishing you a day full of warmth, laughter, and small moments of joy.

    LONGratio ∈ [1.26, 2.27]minArea 1.3% of canvas

    Photos skip this step — their ratio is whatever the image already is, and they don’t carry an area budget.

  3. 03

    Search in parallel

    Two engines try different arrangements of the same list. Text participates in both — the engines can resample a text scrap’s ratio from its allowed range in search of a better score.

    Grid GAbinary tree · 50 × 40
    • Start from 2 balanced trees + random mutations.
    • Per generation (× 40): flip cut, swap leaves, restructure subtree.
    • Text scraps have a 50% chance to resample their ratio from [lo, hi].
    • Keep top 30% each generation; refill by mutating survivors.
    Phyllo trialsspiral · 30 trials
    • Allocate area with sizeVar hierarchy, then raise any text below minArea.
    • Deficit is scaled out of non-text items (floor 0.3× of their original area).
    • Seed on a golden-angle ellipse; run a 300-iteration constraint solver.
    • 30 trials with fresh ratio samples; keep the best-scoring one.
  4. 04

    Score + retry

    Every candidate is multiplied through a handful of factors that each stay in [0, 1]. The text factor (tB for Grid, ts for Phyllo) can drag the whole score down if any text renders below the fs floor or overflows its area budget. If the best score is below the user’s minScore, the seed increments and the search replays — up to maxRetries.

    Grid scoreexponent
    • fl (fill)0.15
    • am (aspect match)0.15
    • rcOK (row count)0.13
    • rwS (row width)0.13
    • gs (gap uniformity)0.13
    • aOK (area balance)0.09
    • co (compactness)0.05
    • tB (text block)0.17
    Phyllo scoreexponent
    • co (compactness)0.30
    • gh2 (gap harmony)0.17
    • cov (coverage)0.15
    • am (aspect match)0.10
    • axisFill0.08
    • ts (text signal)0.20
    retry loopwhile score < minScore && tries < maxRetries → seed += 1
  5. 05

    Post-process

    Once the engines converge, two optional passes reshape the winning frames. Neither runs a search — they’re purely geometric transforms, applied to Grid and Phyllo frames identically.

    Scrap scalescrapScalePct

    Inflate every box outward by a percentage of the canvas’s short edge. Makes individual items larger; neighbors can overlap.

    TightnesstightnessPct

    Pull every frame toward the canvas centre, then rescale the bbox back to full size. Compresses gaps without cropping content.

  6. 06

    Render

    The engine’s job ends in normalized units. The renderer maps each frame to a percentage of the live canvas width, so the same layout looks identical on 400px mobile and 1400px desktop.

    • Map frames

      Every frame’s (x, y, w, h) is converted to % of NW/NH on the fly — no pixel constants leak from the engine.

    • Fit the text

      Each text scrap runs a 25-iteration shrink loop in a useLayoutEffect: start at the estimator’s fs, measure the rendered height, multiply fs by 0.9 until it fits, and stop at a floor of 3 display px.

    • Animate

      Position/size changes animate over 600ms with a cubic-bezier ease. The animation is CSS-only; React just updates the percentages.

Playground

Adjust any parameter — both engines re-run together. Text scraps participate in the search the same way images do.

Grid
Coverage44%
Gap23–23u
Score79%
Overlaps0
Phyllo
Coverage77%
Gap14–19u
Score72%
Overlaps0
Seed 42
Canvas & Content
Canvas ratio
Background
Image set
Images3
Text scraps (2/3)
SINGLE
SINGLE
Layout
Gap4%
Padding6.5%
Border0px
Text Border Opacity30%
Shadow0%
Post-process
Scrap Scale0%
Tightness0%
Text Ratio
Mode
GA Search
Text Box Size1.10×
Min FS0u
Max FS60u
Advanced
Font
Italic
Line Height1.40
Grid sizeVar50%
Phyllo sizeVar50%
Rotation100%
Density55%
Trials30
Auto Retry
Min Score70%
Max Retries60

Try With Your Photos

📷

Drop photos here

or click to browse · Supports JPG, PNG, WebP

Try with sample photos

122.5