# Photo Sphere Gallery

> A grid of photos folds into a spinning 3D sphere you can fling around, then flies into a cube, a heart or back into a grid at the press of a button. Click any photo to lift it out at full size.

Canonical: https://gsapvault.com/effects/photo-sphere-gallery
Live demo: https://gsapvault.com/demos/photo-sphere-gallery/index.html

| Property | Value |
|----------|-------|
| Type | effect |
| Tier | paid |
| Price | £10 |
| Difficulty | advanced |
| Plugins | Core GSAP only |
| Techniques | 3d-transforms, morphing, momentum, spring-physics, stagger, pointer-drag, touch-drag, depth-falloff, lightbox, shared-element, focus-management, load-sequence, ambient, viewport-aware-ticker, keyboard-navigation, progressive-enhancement |
| Uses Lenis | No |

## Lighthouse, as measured

Google Lighthouse on the demo, 8 October 2026. A measurement of the demo as shipped, not a promise for your page.

| Category | Score |
|----------|-------|
| Accessibility | 100 |
| Best practices | 100 |

No performance score is published: it depends on the deployment (server compression, caching, CDN, connection and device) rather than on the code, so measure it where it will live.

## Overview

A wall of photographs that does not stay flat. On load the grid peels apart and folds itself into a globe of cards, which then turns slowly on its own. Grab it and it stops dead like a globe under your hand; drag to turn it and tilt it, and let go mid-swipe to fling it, with the spin easing back down to its idle pace in the direction you threw it.

The same cards rearrange on command. Cube packs them onto six faces, Heart fills a heart outline and puffs it forward so it still reads as an object when it sways, and Grid lays them flat again. Each card takes its own staggered flight to the slot nearest where it started, blooming outwards mid-flight so the cards clear each other, and the shape keeps turning while they travel. Cards on the far side fade into the background, so the depth reads even in a still frame.

Click a photo and the shape turns to bring it to the front, then the card lifts out of the shape and opens to fill the gallery, swapping in a larger file at the photo's own proportions. Close it and it flies back into its slot, and the spin picks up where it left off.

## Features

- Four shapes from one set of cards: sphere, cube, heart and grid, each rebuilt for the stage size
- Drag or swipe to spin and tilt, with a fling that eases back to the idle spin in the direction you threw it
- Catch-to-stop: pressing on a spinning shape halts it under the pointer
- Staggered flights between shapes that bloom outwards mid-air, matched so cards travel to the nearest free slot
- Facing shapes (heart and grid) turn under a drag and spring back with a slight overshoot
- Click to enlarge: the shape turns the photo to the front, it lifts out to full size with a larger file and its real proportions, and flies back into place on close
- Depth shading: cards behind the centre fade towards the background colour
- Opening sequence folds the page's own photo grid into the sphere, so there is no jump when the script takes over
- Works with any number of cards from about 20; every shape lays itself out for the count
- Arrow keys spin and tilt, Enter enlarges the photo at the front and the arrows step through photos in the enlarged view; the shape buttons are native toggle buttons
- Pauses its animation loop off screen and in background tabs
- Reduced motion: no idle spin or fling, and shape changes cross-fade instead of flying
- No JavaScript: the photos remain an ordinary responsive grid

## Use Cases

- Photographer and studio portfolios that want a memorable first screen built from their own work
- Moodboards, reference libraries and archives where browsing a lot of images is the point
- Event, festival and campaign pages showing a wall of attendee or community photos
- Product and collection launches where a heart or cube shape can tie into the campaign

## Vibe-Code Ready Setup

This effect includes `START-HERE-AI.md`, a product-specific copy-paste setup prompt for Cursor, Claude Code, ChatGPT, GitHub Copilot, Windsurf, and other coding assistants. It tells the assistant to inspect the existing stack, integrate the supplied files, preserve the design, scope selectors, retain accessibility and responsive behaviour, add framework-appropriate GSAP cleanup, and report what it tested.

[How AI-assisted setup works](https://gsapvault.com/vibe-coding)

## How It Works

Each photo is a card positioned in real CSS 3D space inside two nested layers: the outer one tilts, the inner one spins. Every shape is just a list of positions and orientations computed for the current stage size: latitude rings for the sphere, a 3 × 3 grid on each cube face, a filled heart outline with a domed depth, and a best-fit flat grid. When you choose a shape, each card is matched to the free slot nearest its place on the sphere, and GSAP tweens its position, rotation and scale there with a small per-card delay and a mid-flight bloom.

The spin, the fling and the spring back to facing are a small physics step on the GSAP ticker, which also writes every card's transform and its depth shade. The loop sleeps whenever nothing is moving, the stage is off screen or the tab is hidden. A change in motion preference rebuilds the gallery in place, and one teardown call returns the markup to the plain grid.

Enlarging tweens the spin so the chosen card faces the viewer, measures where it lands on screen, and hands over to a flat copy laid over the gallery, which GSAP grows to the largest box of the photo's proportions that fits. Closing measures the card again and flies the copy back to it.

## Documentation

How this effect works and how it goes into a page. The reference you use once you own the files (worked examples, events, the programmatic API, the class list) ships with the download.

### Quick Start

**1. Add to your HTML `<head>`:**

_Code snippet omitted: it ships with the download._

**2. Add the gallery to your `<body>`:**

_Code snippet omitted: it ships with the download._

**3. Add before the closing `</body>` tag:**

_Code snippet omitted: it ships with the download._

The script finds every `.photo-sphere` on the page. It measures where each card sits in the plain grid, switches the gallery into 3D, and flies the cards from those cells into the starting shape.

### Using It With Your Own Design

**What the effect needs from your markup:** the four nested hooks `.photo-sphere` > `.photo-sphere__stage` > `.photo-sphere__world` > `.photo-sphere__cards`, with one `.photo-sphere__card` per photo. The stage is the drag surface and holds the perspective; the world layer tilts and the cards layer spins, so keep them nested in that order. Shape buttons are any element with `data-formation-to` inside `.photo-sphere`; the script keeps their `aria-pressed` in step. A card can hold anything square-ish: an `<img>`, a `<picture>`, or an `<a>` wrapping an image (a drag that ends on a link does not follow it).

**Enlarging:** a click on a card that is not a drag enlarges its `<img>`. Give each image a `data-full` with a larger file (the demo uses 1100px wide); it starts loading on the click and is shown at its own proportions, so a square card can open into a landscape or portrait photo. Without `data-full` the card's own file is shown, at no more than twice its size. A card holding a link stays a link and never enlarges. The script builds the enlarged view (`.photo-sphere__viewer`, holding `.photo-sphere__backdrop`, `.photo-sphere__photo` and `.photo-sphere__close`) inside `.photo-sphere`, so it covers the gallery rather than the whole page. Style those four classes however you like; `.photo-sphere` needs `position: relative` for the view to sit over it.

**What is only the demo's styling:** the dark ground and its soft radial glow, Mona Sans, the pill-shaped control bar and the cue line. Card corner radius is `--psg-radius`. Restyle or replace all of it. The script never reads a class from the control bar or the cue.

**CSS the effect depends on:**

- `.photo-sphere.is-3d` rules: the world and cards layers are `position: absolute` at the stage centre with `transform-style: preserve-3d`, and each card is `position: absolute` and sized by `--psg-card`. Don't put `overflow`, `opacity`, `filter` or `clip-path` on the world or cards layers: each one flattens the 3D.
- `.photo-sphere.is-3d .photo-sphere__stage` uses `perspective: var(--psg-perspective)`. The script sets both custom properties from the stage size.
- `.has-js .photo-sphere` gives the gallery a fixed height (`100svh` in the demo). Size it however you like, but give the stage a real height: every shape is fitted to it.
- `touch-action: pan-y` on the stage lets horizontal swipes spin the shape while vertical swipes still scroll the page.
- `.photo-sphere__card::after` is the depth shade. It reads `--psg-shade` and paints `--psg-shade-colour`, which should match whatever is behind the stage.
- Without the `is-3d` class, `.photo-sphere__cards` is a CSS grid. That is the no-JavaScript layout and the opening frame.

### Options

| Attribute | Values | Default | Description |
|-----------|--------|---------|-------------|
| `data-formation` | `sphere`, `cube`, `heart`, `grid` | `sphere` | The shape the cards fly into first |
| `data-auto-rotate` | `true`, `false` | `true` | Idle spin for the sphere and cube, gentle sway for the heart |
| `data-spin-speed` | degrees per second | `9` | Idle spin speed |
| `data-intro` | `true`, `false` | `true` | Fold out of the grid on load. `false` starts in the shape |
| `data-enlarge` | `true`, `false` | `true` | Click a photo to enlarge it. `false` leaves the cards inert |

On each card's `<img>`, `data-full` is the larger file shown when it is enlarged.

The shape buttons take a `data-formation-to` with one of the four shape names.

| Custom property | Set by | Description |
|-----------------|--------|-------------|
| `--psg-shade-colour` | you | Colour the far side fades towards (the demo uses the page ground) |
| `--psg-radius` | you | Card corner radius |
| `--psg-card` | script | Base card size for the current stage |
| `--psg-perspective` | script | Stage perspective for the current stage |

### Accessibility

- **Keyboard**: the stage is focusable. Left and Right arrows spin the shape (each press adds a nudge), Up and Down tilt it. While the stage has keyboard focus the idle spin waits and the photo nearest the front is ringed (`.is-front`); Enter or Space enlarges it. The enlarged view is a modal dialog labelled with the photo's alt text: focus sits on its close button, Left and Right step through the described photos while the shape turns each one to the front behind it, Escape or any click closes it, and focus returns to the stage. The photos are not individual buttons, because cards seen edge-on at the side of a sphere are too thin to be usable touch targets. The shape buttons are native `<button>` toggles with `aria-pressed`.
- **Reduced motion**: there is no idle spin or fling, and dragging moves the shape directly without inertia. Shape changes cross-fade in place instead of flying, the enlarged photo fades in and out instead of lifting, and the opening sequence is skipped. If the preference changes while the page is open, the gallery rebuilds itself.
- **No JavaScript**: the cards stay an ordinary responsive grid and the shape buttons are hidden, since they would do nothing.
- **Alt text**: give each distinct photo a description. If you repeat photos to fill the shape, mark the repeats `alt=""` so screen readers don't read them twice; they still enlarge on click, and the arrow keys in the enlarged view skip them.
- The animation loop stops while the gallery is off screen, while the tab is hidden, and whenever nothing is moving.

### Dependencies

**Required:**
- GSAP 3.12+ (the demo is built against 3.15.0)

No GSAP plugins and no Lenis: it is a pointer effect and never scrolls the page.

## What You Get

- `index.html`: working demo page
- `assets/script.js`: commented, readable source
- `assets/style.css`: effect styles
- `README.md`: full documentation with examples and framework integration notes
- `START-HERE-AI.md`: product-specific copy-paste prompt for AI-assisted setup
- `LICENSE.txt`: standard license terms
- Lifetime updates: re-download anytime from your library

## Get the Code

This is a premium effect. The standard license costs £10 one-time and covers unlimited personal and commercial projects with no attribution required for our code. Bundled third-party assets retain their own licences and attribution requirements. The only restrictions: no redistribution of the code itself and no competing effect libraries.

- [Buy Photo Sphere Gallery](https://gsapvault.com/effects/photo-sphere-gallery)
- [The Vault (£99 one-time, best value): every collection in the Vault library, plus future items added to those collections](https://gsapvault.com/effects)

## Judge the Code Quality First

These related effects are free with complete source published, written to the same production standard (cleanup functions, reduced-motion support, framework-agnostic):

- [Image Clip Reveal](https://gsapvault.com/effects/image-clip-reveal.md): A cinematic image reveal where a directional polygon aperture opens as the photograph settles from a restrained Ken Burns scale and its caption lands.

---

From [GSAP Vault](https://gsapvault.com): production-ready GSAP animation effects. Full catalog for agents: https://gsapvault.com/llms-full.txt
