Three.js Scroll Gear Train — GSAP Mechanical Ratios
Three.js Scroll Gear Train Mechanism · Scroll · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
How to Build a Scroll-Driven Gear Train With Three.js and GSAP

The Three.js Scroll Gear Train Mechanism snippet builds five interlocking brass gears procedurally — disc, teeth, hub, and spoke cutouts — and lets scroll act as the crank: GSAP's ScrollTrigger scrubs the driver gear's angle, and every downstream gear derives its rotation through the real tooth-count ratios, counter-rotating at each mesh point exactly as physical gears would. The scene reads as a machine because the math underneath is the machine's math.
Procedural gears from primitives
Each gear is a Group: a CylinderGeometry disc rotated flat, N tooth boxes placed around the rim at (cos a, sin a) × (r + 0.1r) with each tooth rotated a + π/2 to point radially outward, a smaller dark hub cylinder, and four dark wedge boxes that fake spoke cutouts on the face. Building gears in code rather than loading a model means tooth count is a real parameter — and tooth count is exactly what the animation math needs. Tooth width derives from circumference, (2πr / teeth) × 0.45, so any radius/teeth combination produces a plausible gear with correct-looking pitch.
Real gear ratios, not decorative spinning
Most gear animations on the web rotate everything at arbitrary speeds and hope nobody looks closely. Here each gear's angle is computed by chaining angle × (teethPrev / teethCurrent) down the train while flipping direction at every mesh — the actual kinematics of spur gears. The 24-tooth driver turning twice (4π across the scroll) spins the 10-tooth fourth gear nearly five times in the opposite direction. Because ratios are exact and both gears in each pair carry a half-tooth phase offset (π / teeth on alternating gears), teeth visibly interleave through the mesh point instead of clipping through each other — the detail that sells the whole illusion.
Scroll as the crank handle
The single scrubbed value here is not an abstract progress fraction but the driver's angle itself, tweened from 0 to 4π with ease: 'none'. That framing matters: scrolling *is* cranking, at a fixed mechanical advantage, and scrolling backwards runs the machine in reverse — which is free, since every gear's angle is a pure function of the driver's. The HUD converts the same value to degrees, so users see DRIVE 720° at full scroll. This is the purest expression of the derive-everything pattern used across this series, from the portal gate sequence to the Rubik's cube assembly.
A dolly that follows power through the train
The camera starts on the big slow driver and dollies rightward along the zigzagging train as the drive angle accrues, ending close on the smallest, fastest gear — following the power flow, like a documentary camera would. Position and look-at both derive from normalized drive progress, with a small clock-time bob so the shot never feels locked off. Warm directional and amber point lights against high-metalness, mid-roughness MeshStandardMaterial give the brass look; fog matched to the dark background swallows the train's far end.
Where to take it
Because ratios are parameter-driven, the train doubles as a live mechanism diagram: change any teeth value and both the geometry and the motion update coherently. Add a chain of number counters geared to each wheel for an odometer effect, or pair it with the clockwork feel of scroll timeline dots for process storytelling. For a more organic machine aesthetic, the fabric ripple snippet shows the same scrub philosophy applied to soft surfaces instead of rigid bodies.
Build with AI
Build, Understand, Optimize, and Extend It With AI
You do not need to derive spur-gear kinematics yourself. Paste this snippet's HTML, CSS, and JS into an AI assistant like Claude and ask it to explain the ratio chain, the half-tooth phase offset that prevents clipping, or why the scrubbed value is the driver angle rather than a progress fraction. The same assistant can extend the machine — adding a belt-driven flywheel, a piston converting the last gear's rotation to linear motion with proper crank math, gear teeth counts pulled from your product's actual numbers (team size, years, releases) with the HUD explaining the ratio, or an idler gear inserted mid-train to demonstrate direction changes. It can also convert the tooth boxes to an ExtrudeGeometry involute profile if you want machinist-accurate teeth. Treat the code as a starting point to interrogate and reshape, not a finished artifact.
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:
Build a "scroll-cranked gear train" in plain HTML, CSS, and JavaScript using Three.js and GSAP's ScrollTrigger plugin, all loaded from a CDN (no bundler, no build step).
Requirements:
- A pinned full-viewport section with a canvas, WebGLRenderer, PerspectiveCamera (resized with aspect on window resize), warm ambient + directional key light, an amber PointLight, and Fog matched to a dark background.
- A makeGear(radius, teeth, thickness, color) factory returning a THREE.Group: a flat CylinderGeometry disc, N BoxGeometry teeth around the rim (width = 2πr/teeth × 0.45, each at position (cos a, sin a) × 1.1r with rotation a + π/2), a narrow dark hub cylinder, and four dark wedge boxes faking spoke cutouts. High metalness (~0.85), mid roughness for a brass look.
- A train of five gears from a specs array (radius, teeth, color), positioned left to right with spacing = sum of neighboring pitch radii, zigzagging vertically. Alternating gears get an initial rotation.z of π/teeth (half-tooth phase) so teeth interleave at mesh points.
- One GSAP tween (ease "none") scrubbing the DRIVER ANGLE itself from 0 to 4π on a ScrollTrigger with pin: true and end ~+=400%, scrub ~0.4.
- Each frame, chain angles down the train: angle = prevAngle × (prevTeeth / thisTeeth), flipping rotation direction at every mesh, and apply baseZ + angle × dir per gear — exact spur-gear kinematics, never decorative speeds.
- A camera that dollies along the train as drive progress accrues, ending near the smallest/fastest gear, with a slight clock-driven bob and lookAt tracking the dolly.
- A HUD showing the driver angle in degrees, and an intro overlay fading once the angle passes ~0.05.
- Confirm scrolling backwards runs the entire machine in reverse with teeth still interleaving cleanly.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
- 1Load the three CDN scriptsAdd three.min.js, gsap.min.js, and ScrollTrigger.min.js in that order before the snippet JS.
- 2Paste HTML, CSS, and JSA pinned zigzag train of five brass gears sits motionless under warm light with a DRIVE 0° HUD.
- 3Scroll to crankThe big driver turns with your scroll; each downstream gear counter-rotates at its exact tooth ratio — the small 10-tooth gear visibly races.
- 4Watch the mesh pointsHalf-tooth phase offsets keep teeth interleaving cleanly through each contact point rather than clipping.
- 5Scroll back upThe machine runs in reverse — every gear angle is a pure function of the driver angle, so backwards is free.
- 6Re-gear the trainEdit the specs array: radius, teeth, and color per gear. Ratios, spacing, and phase offsets all recompute from teeth counts automatically.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
The frame loop walks the train carrying an angle: for each gear after the driver it multiplies by teethPrev / teethCurrent and flips sign, which is the physical law of spur gears (equal tooth velocity at the contact point). Because every angle derives from the single scrubbed driver angle, the gears can never drift out of mesh no matter how fast or how far the user scrolls in either direction.
Alternating gears start with a rotation offset of π / teeth — half a tooth pitch. With that phase shift, one gear's tooth aligns with its neighbor's gap at the contact point, and since the ratio math advances both at matched surface speed, the interleave is preserved through the whole rotation. Without the offset the ratios would still be correct, but teeth would sit tip-to-tip and pass through each other.
It removes one layer of indirection: the tween IS the crank. Tweening angle from 0 to 4π means the HUD is a unit conversion, gear angles are ratio multiplications, and camera progress is a normalization — every consumer derives what it needs. A 0–1 value would work identically but every use site would multiply by 4π first.
makeGear() assembles a Group from primitives: a flat CylinderGeometry disc, N BoxGeometry teeth placed around the rim at angle a with rotation a + π/2 so they point radially, a narrow hub cylinder, and four dark wedges suggesting spoke cutouts. Tooth width is (2πr / teeth) × 0.45, derived from circumference, so pitch looks right for any parameter combination — and one Group rotation.z turns the entire assembly.
Yes. Use the JSX, Vue, Angular, or Tailwind export buttons. Build the gears and ScrollTrigger inside a mount effect against a canvas ref, update the HUD through a ref rather than state, and on cleanup kill the ScrollTrigger, traverse each gear group disposing geometries and materials, and call renderer.dispose() to release the WebGL context and unpin the section.