2.19 · Animation

3D Rotate on Scroll.

A real 3D cube spins fully on both axes as you scroll. Pure CSS preserve-3d — no library required. Different from 2.18's frame scrub: this is live geometry.

What it is

Where the image-sequence (2.18) plays back pre-rendered frames, this technique uses CSS's actual 3D rendering engine to rotate a real object — a cube, a card stack, anything you can compose with preserve-3d — tied directly to scroll position. Cheaper bandwidth (no asset loading) and infinitely smooth at any frame rate. Used by product showcases where the geometry can be CSS-built.

How it works

Parent container has perspective: 1400px. Inside, a "world" element with transform-style: preserve-3d contains 6 face elements (for a cube), each position: absolute; inset: 0 and pre-rotated to their wall via rotateX/Y(...)deg translateZ(120px). The world element's own transform is rotateX(calc(var(--p) * 360deg)) rotateY(calc(var(--p) * 720deg)) — driven by a --p custom property that's updated by a scroll listener. As --p ticks from 0 to 1, the cube rotates a full revolution on X and two on Y.

perspective on parentControls depth feel — 1400px = camera distance from object
transform-style: preserve-3dLets children render in real 3D space (not flattened)
face positioningEach cube face uses rotateX/Y + translateZ(half-cube-size)
scroll → --p → rotateOne progress value drives the whole rotation calc()

Live demo

Scroll inside. The cube does one full X rotation and two full Y rotations across the scroll distance.

Live · scroll inside
↓ scroll to spin

Six faces, one cube.

— scroll-tied 3D —

01
Front
02
Right
03
Back
04
Left
05
Top
06
Bottom
— full rotation complete —

Copy this prompt

Prompt · 2.19 3D Rotate on Scroll
Build a CSS-only 3D cube that rotates on scroll. Outer scroll container overflow-y:scroll, contains a tall 2000px section. Inside that section put a sticky pin (position:sticky top:0 height:100vh display:grid place-items:center perspective:1400px). Inside the pin place a 240×240 cube element with transform-style:preserve-3d and transform:rotateX(calc(var(--p, 0) * 360deg)) rotateY(calc(var(--p, 0) * 720deg)). The cube contains 6 face divs each position:absolute inset:0, gradient background per face (violet front, pink right, cyan back, amber left, teal top, charcoal bottom), border-radius:16px, with their own transforms pre-positioning them to a cube wall: f1 translateZ(120px); f2 rotateY(90deg) translateZ(120px); f3 rotateY(180deg) translateZ(120px); f4 rotateY(-90deg) translateZ(120px); f5 rotateX(90deg) translateZ(120px); f6 rotateX(-90deg) translateZ(120px). Each face has a giant display-serif number (01-06) and a small uppercase label. Scroll listener: calculate the tall section's scroll progress 0→1 and set --p as a CSS custom property on the cube. Optional progress bar at the bottom of the pin showing --p. As you scroll past the section the cube does one full X revolution and two full Y revolutions.

Example sites to study