Scroll Grid Zoom — Free GSAP Tile Expand Snippet

Scroll Grid Zoom · Scroll · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Index-derived scatter
Each tile exits along its own grid direction.
Measured zoom
Scale factor computed from rendered size.
Compositor scaling
transform: scale avoids nine-tile reflow.
Single camera move
Zoom and exits share timeline position 0.
Late caption
Copy fades in once the tile is fullscreen.
Soft exits
Neighbors fade to 15%, not to nothing.
Layered hero
z-index keeps traffic behind the center.
Reversible dive
Scrub back to reassemble the grid.

About this UI Snippet

Scroll Grid Zoom — Expand the Center Tile to Fullscreen on Scroll

Screenshot of the Scroll Grid Zoom snippet rendered live

The scroll grid zoom starts as an ordinary 3×3 image grid, then — as you scroll — the center tile grows until it owns the entire viewport while its eight neighbors accelerate outward and fade, as if the camera dove into the middle cell. It's a cinematic way to promote one item out of a collection. This snippet builds it with GSAP ScrollTrigger (from a CDN), a measured scale factor, and per-tile direction math.

Neighbors fly along their own grid direction

Each outer tile computes a direction vector from its grid index: col = (i % 3) − 1 and row = floor(i / 3) − 1 yield −1/0/+1 offsets that point directly away from the center cell. The corner tiles travel diagonally, edge tiles straight out — multiplied by ~85% of the viewport so everything clears the screen. Deriving direction from index means the scatter pattern is symmetric automatically, with no per-tile configuration.

The zoom factor is measured, not hardcoded

Instead of guessing "scale: 3.4", the script measures the center tile's rendered size with getBoundingClientRect() and computes max(vw/w, vh/h) × 1.05 — the exact scale at which the tile covers the viewport in both dimensions, with 5% overshoot to hide edge slivers. Because the grid is fluid (min(720px, 90vw)), a hardcoded factor would under- or over-shoot at different screen widths; measuring makes the effect resolution-independent.

Scale beats width/height animation here

The center tile grows via transform: scale(), not by animating its width and height. Transforms run on the compositor without reflowing the grid — animating the box size would relayout all nine tiles every frame and shift the grid as the middle cell grew. The visual cost of scaling (its contents grow too) is actually desirable: the tile's emoji becomes the hero image of the fullscreen view.

Everything shares timeline position 0

The eight fly-outs and the center zoom all start at position 0 on one scrubbed timeline, so the dive reads as a single camera move rather than a sequence. The caption is the only late element — it fades in at 70% of the timeline, once the tile has effectively become a fullscreen hero, turning the end state into a legitimate section header.

Fading, not removing, the neighbors

Outer tiles land at opacity: 0.15 rather than 0. During the scrub-back they're already faintly visible mid-viewport, which makes the reverse animation read as tiles returning rather than materializing from nothing. A touch of rotation (col × 6°) adds spin to the exits so the scatter feels thrown, not slid.

The center stays above the traffic

z-index: 2 on the center tile keeps neighbors passing *behind* it as they exit, preserving the illusion that the camera moves toward the middle cell instead of tiles crossing over the hero.

Customizing it

Swap emoji tiles for <img> or background-image cells, tune the exit distance, or zoom a different cell by changing which tile gets measured. Pair it with a scroll zoom hero for a single-image cousin, scroll image mask for a clip-path take, or a scroll reveal grid to rebuild a grid in the next section.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You don't have to reverse-engineer the direction and scale math by hand. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how col and row are derived from a tile's flat array index using modulo and floor division, and why the center tile's scale factor is measured with getBoundingClientRect at runtime instead of hardcoded. The same assistant can help optimize it — asking whether the measured scale factor needs to be recalculated on window resize since it's only computed once at load, or whether animating scale on the center tile is meaningfully cheaper than animating its width and height directly. It's also useful for extending the effect: ask it to make the zoomed tile a clickable link to a detail page, support zooming into whichever tile the user clicks rather than a fixed center cell, or add a subtle parallax to the emoji/image inside each tile as it flies outward. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a "scroll grid zoom" effect in plain HTML, CSS, and JavaScript using GSAP and its ScrollTrigger plugin (load both from a CDN, no build step).

Requirements:
- A CSS grid of nine tiles (3 columns by 3 rows), with one specific tile marked as the center/hero tile containing extra caption content that starts invisible.
- For every tile except the center one, derive a direction vector purely from its position in the flat array: column offset as (index modulo 3) minus 1, and row offset as floor(index / 3) minus 1, producing values of -1, 0, or 1 that point away from the center in every direction with no manual per-tile configuration.
- Animate each outer tile's x and y by that direction vector multiplied by a large fraction of the viewport width/height (so it fully clears the screen), add a small rotation scaled by the column offset, and fade it down to a low but nonzero opacity (not fully to 0) so it stays faintly visible for the reverse animation.
- For the center tile, measure its actual rendered width and height with getBoundingClientRect, then compute a scale factor as the larger of (viewport width / tile width) and (viewport height / tile height), with a small overshoot multiplier (like 1.05) to hide edge slivers — do not hardcode a scale number.
- Animate the center tile's scale (a transform), not its width/height, up to that measured factor, and fade its caption content in later in the timeline (e.g. at 70% progress) once it visually fills the viewport.
- Every outer-tile exit tween and the center zoom tween must start at the same timeline position (0) so the whole sequence reads as one continuous camera move, all wired to a single pinned, scrubbed ScrollTrigger that reverses cleanly on scroll-up.
- Give the center tile a higher z-index than the others so exiting neighbor tiles visually pass behind it rather than in front.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Add the GSAP CDNsInclude gsap and ScrollTrigger from the CDN panel.
  2. 2
    Paste HTML, CSS, and JSA 3×3 tile grid renders with a marked center cell.
  3. 3
    Scroll into the stageNeighbors fly outward as the center starts growing.
  4. 4
    Reach fullscreenThe tile covers the viewport and its caption fades in.
  5. 5
    Scroll back upThe tile shrinks into its cell; neighbors return.
  6. 6
    Swap in imagesReplace the emoji tiles with img or background cells.

Real-world uses

Common Use Cases

Portfolio highlights
Dive into a featured project from the grid; browse the rest with a photo gallery.
Product heroes
Promote one product to fullscreen, then continue with sticky scroll features.
Travel and editorial
Zoom into a destination tile inside a scroll pin story.
Category landings
Enter a category cinematically; a scroll gallery pin can carry the detail.
Campaign reveals
Make the announcement the center cell, echoing a scroll zoom hero.
App screenshots
Expand the key screen from a feature grid like scroll reveal grid.

Got questions?

Frequently Asked Questions

Direction comes from each tile's grid index: col = (i % 3) − 1 and row = floor(i / 3) − 1 produce −1/0/+1 vectors pointing straight away from the center cell. Corners travel diagonally and edges straight out, multiplied by ~85% of the viewport so they fully clear the screen. No per-tile configuration is needed.

The script measures the tile's rendered size with getBoundingClientRect, then computes max(innerWidth/width, innerHeight/height) × 1.05 — the exact factor at which it covers the viewport both ways, with 5% overshoot to hide edge slivers. Because the grid is fluid, a hardcoded scale would misfire at other screen widths.

Scaling is a transform, so it composites on the GPU without touching layout. Animating the box's width/height would reflow the entire 3×3 grid every frame and physically shift the other cells while they're mid-exit. The side effect of scale — the tile's contents grow too — is exactly what a zoom should look like.

Yes — point the center variable at any tile and skip it in the exit loop; the measured scale factor still works. For a corner tile, add x/y compensation in the same tween (moving its center toward the viewport center as it scales), since scaling alone expands around the tile's own position, not the screen's middle.

Render tiles from an array and build the timeline in a mount effect (useEffect, onMounted, or ngAfterViewInit) within gsap.context, reverting on cleanup so the pin unregisters. Measure the center ref after layout (the effect runs post-render, so getBoundingClientRect is safe), and re-create the trigger on resize if your grid is fluid. The grid maps to Tailwind's grid utilities directly.