Skip to content
animorio
IT
Back to the library

The photo dome you turn

WebGL, 3D, ImagesGSAP, WebGL, CanvasAdvancedCompressed weight: Demo 7.8 KBGSAP 28 KBThree.js 174 KBFreeDownload the zip

A minimal-style WebGL gallery: we stand in the middle of a dome of fashion prints and turn it by dragging or with the wheel. The push dies down slowly with inertia, the prints bow while they run and the ones behind us change photo, so the dome never ends.

The code

A folder that runs on its own: index.html, style.css, script.js and the libraries from a CDN. The React and Vue tabs hold the same demo as a ready component, which switches its animations off when it leaves the page.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Velure</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/full.css">
  <link rel="stylesheet" href="style.css">
  <script type="importmap">
    { "imports": {
      "three": "https://cdn.jsdelivr.net/npm/three@0.178.0/build/three.module.min.js"
    } }
  </script>
</head>
<body>
  <!-- a dome of prints around the camera: drag, swipe or scroll to turn it; it keeps going on its own momentum, the
       prints bow while it runs, and the twelve frames are refilled behind your back so the dome never ends -->
  <main class="pagina" data-pagina>
    <canvas class="tela" aria-label="A dome of photographs, drag to turn it" role="img" data-tela></canvas>

    <header class="testa">
      <p class="testa__voce" data-entra>Autumn archive</p>
      <p class="testa__voce" data-entra data-nota>Drag to turn</p>
    </header>

    <footer class="piede">
      <h1 class="marchio" data-entra data-marchio>Velure</h1>
      <p class="didascalia" data-entra><span class="didascalia__finestra"><span data-didascalia>Linen</span></span></p>
    </footer>
  </main>

  <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
  <script type="module" src="script.js"></script>
</body>
</html>

Playground

Change values, colours and text with the controls and watch the animation respond live. Or write in the code yourself. Then download the folder with your changes.

17 values to change

Or ask Claude Code for it

With the Animorio connector, Claude Code takes it from here, guide included, and fits it into your project. Paste this request:

Take “The photo dome you turn” from Animorio and fit it into my site

What it does

The screen is the inside of a paper-coloured dome and we stand in the middle: all around, hung on the wall and all facing us, are the prints of Velure, an invented fashion archive, at three heights that alternate, so the row rises and falls like a frieze. The prints are not photos: they are fields of paper, stone, clay and ink with a single out-of-focus shape, a disc, a column, a folded cloth, a horizon, with grain and a small caption at the foot; only two are rust. On opening they arrive from the top down, one after the other, rising from below and coming forward as they light up; then the brand appears, VELURE, big in the middle at the bottom, and under it the name of the print in front. Dragging with the mouse or a finger, or with the wheel, turns the dome: the push becomes a speed that dies down slowly, and while it runs the prints bow, their middle lagging behind like a sheet in the air, and the brand gains weight. The prints that end up behind us change photo without being seen, so with twelve frames the gallery never ends; when the print in front changes, its name slides into the caption. When nobody touches it, the dome turns slowly on its own and now and then gives itself a push.

How it works

Twelve frames on a sphere

The Three.js camera sits at the origin and the prints are twelve planes hung on a circle of radius 0.95 times 15.2. The height of each comes from an angle that changes in a round of three, 0.52 π plus or minus 0.24, times the radius and 0.88; those above zero are pulled down by 80 per cent and all go down by 0.9. Each plane calls lookAt towards the centre, so it faces us. It is 8.1 times 0.94 tall and as wide as its format, between 5.6 and 9.9, with 16 by 16 segments so it can bend.

The drawn prints

The images are 2D canvases 600 pixels tall: a flat tone, a soft radial light, a blurred shape chosen from a list, grain from a fixed-seed number generator and a caption in Fraunces. They become CanvasTextures with mipmaps and anisotropy, so prints seen side-on do not shimmer. Three's colour management is off and the output is linear: the tones come out the same as written in the code, and the renderer's background is the warm white of :root.

Inertia

Wheel, drag and arrow keys do not move the dome: they add speed, held between minus 180 and 180. On every frame the speed is multiplied by 0.92 to the power of the number of 60-per-second frames that have passed, and the displacement is the exact sum of that series, 0.92 times v times one minus the remainder, divided by 0.08: so the inertia is the same at 30, 60 or 120 frames per second. The camera angle grows by the displacement times 0.005, times 0.5, times 0.253, and the camera looks at a point 0.9 radii away, four units lower.

The bend in the vertex shader

One ShaderMaterial per print, with the bend uniforms shared. In the vertex shader the point goes down by the sine of uv.x times π times the speed: the edges stay put and the middle moves the most, like a sheet on the run. The z moves away by the height distance from the centre to the power of 1.25, so the top and bottom prints follow the curved wall. PIEGA multiplies the site's bend: at 1 it barely shows, at 6 it shows well.

The dome that never ends

On every frame the twelve places are ranked by the dot product between their position and the direction the camera looks: the front eight are visible. A place that drops out of the eight is behind us, and there it receives the next print in the list, with its geometry and its texture. The first of the ranking gives its name to the caption, which slides out and back in with back.out in the direction the dome is turning.

The direction

Everything runs on GSAP's ticker, with no setTimeout. The entrance has the site's timings: the places, sorted from the top, start 1.25 lower, 0.45 further back and at scale 0.92, and arrive in 0.78 times 0.89 seconds with power3.out, one every 0.045 seconds, lighting up in 0.48 times 0.89. Then a repeating timeline gives a push every OGNI seconds, but only if nobody has touched the dome for four seconds. An IntersectionObserver stops drawing when the page is not visible.

Values to change

NameFileDefaultWhat it changes
RAGGIOscript.js15.2The radius of the dome, as on the site.
ALTEZZAscript.js8.1The height of a print.
PIEGAscript.js6How much the prints bow while the dome runs (1 as on the site).
CURVAscript.js0.05How much the top and bottom prints follow the curved wall.
ATTRITOscript.js0.92How much speed is left each frame: closer to 1, the longer it spins.
SPINTAscript.js110The push the dome gives itself now and then.
OGNIscript.js6.5How many seconds between those pushes.
DERIVAscript.js0.035How fast it turns on its own, slowly, in radians per second.
CAMPOscript.js60The camera's vertical angle, in degrees.

Try them in the Playground

When to use it

As the home page of a photographer, a fashion studio or an image archive; as the gallery of a collection in a clothing shop; as the page of a photography exhibition or festival; anywhere you want to show many images together and let the viewer turn them.

On phones

The same dome: a finger drags horizontally or vertically (the axis locks after six pixels) and the push carries on by itself. On a narrow screen the camera opens its angle a little, so more than one print shows at the sides, and the brand is sized on the height too.

With reduced motion

The prints fade in already in place; the dome does not turn on its own, give itself pushes or bend. Dragging and the wheel stay, with their inertia, because the motion is the viewer's choice.

Pitfalls

  • With Three's colour management on, the warm white background passed in from :root came out darker than the page: Color.set converts it to linear and the linear output does not convert it back. With ColorManagement off and linear output, canvas and page have the same colour.
  • Inertia worked out as v times 0.92 per frame spins twice as far at 120 Hz: we use the power of the frames that have passed and the exact sum of the series, as the site does.
  • The bend's z shift must be done in the plane's own coordinates, after lookAt: there z faces the centre, and subtracting it moves the prints away from the camera instead of sideways.
  • Textures drawn before the font has loaded keep the fallback font: we wait for document.fonts.load, but no longer than 1.2 seconds.
  • Without mipmaps the prints seen at an angle shimmer while the dome turns: CanvasTexture with LinearMipmapLinearFilter and anisotropy 8.

Libraries

Three.js 0.178.0, GSAP 3.15.0, Fraunces (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 7.8 KB; the libraries: GSAP 28 KB, Three.js 174 KB.

Testing

Tested on 22 October 2026

  • Desktop: Done
  • Phone: Done
  • Reduced motion: Done
  • Safari on iPhone: To do

License

Use it in your own projects and in your clients’ projects. Do not resell it or redistribute it as is. GSAP stays under its own Standard license. Full license.

All animations