Three.js Scroll Planet Approach — GSAP Orbit Arrival

Three.js Scroll Planet Approach · Scroll · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Procedural vertex-colored planet — continents, oceans, and polar ice from trig noise, zero texture downloads
Budget fresnel atmosphere: oversized BackSide sphere whose opacity scales with scroll progress
Additive-blended cloud shell rotating faster than the surface for continuous secondary motion
Braking-burn distance easing via 1 − (1 − p)^2.2 so arrival flattens instead of stopping abruptly
Corkscrew approach vector swings 2.1 radians around the planet so the terminator sweeps into view
Inclined moon orbit advanced by both clock time and scroll progress for constant parallax
Live distance HUD in kilometers derived from the same scrubbed camera distance
Fully reversible — every visual state is a pure function of one ScrollTrigger-scrubbed value

About this UI Snippet

How to Build a Scroll-Driven Planet Approach With Three.js and GSAP

Screenshot of the Three.js Scroll Planet Approach snippet rendered live

The Three.js Scroll Planet Approach snippet flies the camera from deep space into a close orbit around a procedurally colored planet, with GSAP's ScrollTrigger scrubbing a single 0–1 progress value that every other quantity — camera distance, swing angle, atmosphere glow, and a live distance HUD — is derived from each frame. It is the "arrival" counterpart to the starfield warp: instead of racing past stars forever, the scroll ends in a stable orbit around a destination.

A textureless planet painted per-vertex

The planet needs continents, oceans, and polar ice, but downloading an equirectangular texture would make the snippet depend on an external image. Instead, the sphere's vertices are colored directly: each vertex normal is fed into a cheap trigonometric noise expression (sin/cos products at different frequencies), and the result thresholds into land green or ocean blue, with anything above 0.86 on the Y axis painted ice white. Because MeshLambertMaterial supports vertexColors, the GPU interpolates smoothly between vertices and the 96-segment sphere reads as soft-edged continents with zero network requests.

Atmosphere and clouds without shaders

The atmosphere rim is the classic budget fresnel trick: a second sphere 7% larger than the planet rendered with side: THREE.BackSide, so only its far hemisphere's inside faces draw, producing a halo that hugs the limb. Its opacity is tied to scroll progress — barely visible from deep space, glowing at 0.38 opacity once you arrive — which sells the sensation of entering the atmosphere's optical thickness. Clouds are a third shell using the same vertex-color trick but with AdditiveBlending and depthWrite: false, rotating slightly faster than the surface so the planet feels alive even when the user stops scrolling.

Distance easing shaped for a braking burn

Mapping scroll linearly to distance would make the final moments feel abrupt — most of the planet's apparent growth happens in the last few units of approach. The snippet instead applies 1 - Math.pow(1 - p, 2.2) to the scrubbed progress, so distance closes quickly while the planet is small and flattens out near arrival, mimicking a deceleration burn. This is the same derive-from-one-value philosophy used in the portal gate sequence: ScrollTrigger scrubs one number, and the frame loop shapes it.

A swinging approach vector, not a straight line

Flying straight at a sphere is visually static — the silhouette just grows. The camera's position is computed on a circle whose angle advances 2.1 radians across the scroll, so the approach corkscrews a third of the way around the planet and the day/night terminator sweeps across the visible face during descent. Combined with an inclined moon whose orbital angle also advances with progress, there is always parallax between foreground and background to communicate motion.

Scroll-scrubbed but never frozen

Only the approach itself is scroll-driven. Planet rotation, cloud drift, the moon's base orbit, and a subtle sinusoidal camera bob all run off THREE.Clock elapsed time, so the scene keeps breathing when scrolling stops — the same split between scrubbed primary motion and clock-driven secondary motion used in the scroll camera path snippet. Scrolling back up reverses the entire arrival exactly, because nothing is fired one-shot; every visual state is a pure function of the current progress value and the clock.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You do not need to reverse-engineer how a planet gets continents without textures or why the approach feels like a braking burn. Paste this snippet's HTML, CSS, and JS into an AI assistant like Claude and ask it to explain the vertex-color noise thresholding, the BackSide atmosphere trick, or why the distance easing is applied in the frame loop rather than on the tween. The same assistant can extend the scene for you — adding a ring system, city lights on the night side by brightening vertices facing away from the sun, a second flyby moon, or an entry-glow flash when progress crosses 0.9. It can also restyle the planet to match a brand palette by swapping the three THREE.Color values and the noise thresholds. 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:

text
Build a "scroll-scrubbed planet approach" 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, and PerspectiveCamera resized (including aspect) on window resize.
- A planet built from SphereGeometry (~96 segments) colored per-vertex via a color BufferAttribute: feed each vertex normal into a sum of sin/cos products, threshold the result into land/ocean colors, and paint vertices with |y| above ~0.86 as polar ice. Use MeshLambertMaterial with vertexColors: true and a DirectionalLight as the sun.
- An atmosphere rim from a sphere ~7% larger rendered with side: THREE.BackSide and a transparent MeshBasicMaterial whose opacity rises with scroll progress.
- A cloud shell: a third sphere slightly above the surface, vertex-colored white/black by a different noise expression, with AdditiveBlending and depthWrite: false, rotating slightly faster than the planet.
- A small moon on an inclined orbit whose angle advances from both clock time and scroll progress, plus a ~1200-point starfield distributed on a spherical shell.
- One GSAP tween with ease "none" scrubbing a plain progress value 0→1 on a ScrollTrigger (pin: true, start top top, end around +=450%, scrub ~0.6).
- In requestAnimationFrame: derive camera distance from progress through a braking curve like 140 − 122 × (1 − (1 − p)^2.2), advance the camera's orbital angle ~2.1 radians across the scroll so the terminator sweeps into view, add a small clock-driven vertical bob, and call camera.lookAt(planet).
- Derive a live "DISTANCE n KM" HUD from the same distance value, and fade out an intro overlay once progress passes ~0.03.
- Confirm scrolling back up reverses the entire arrival exactly — every visual state must be a pure function of the scrubbed progress and the clock, nothing fired one-shot.

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
    Load the three CDN scriptsAdd three.min.js, gsap.min.js, and ScrollTrigger.min.js in that order before the snippet JS.
  2. 2
    Paste HTML, CSS, and JSA pinned deep-space view appears with the planet as a small disc, a drifting moon, and a live DISTANCE HUD in the corner.
  3. 3
    Scroll downThe camera closes in along a corkscrew path — the planet grows, the terminator sweeps across its face, and the atmosphere rim brightens.
  4. 4
    Watch the final approachThe power-curve easing flattens the last stretch so arrival into orbit feels like a braking burn rather than a sudden stop.
  5. 5
    Scroll back upThe whole approach reverses exactly — distance, glow, and HUD all derive from one scrubbed value, so nothing is one-shot.
  6. 6
    Recolor the planetChange the ocean, land, and ice THREE.Color values or the noise threshold (0.35) to redraw the continents instantly — no texture needed.

Real-world uses

Common Use Cases

Space and aerospace landing pages
Open a satellite, launch-provider, or space-data product page with an arrival sequence that ends exactly where your content begins.
Sci-fi game and metaverse promos
Use the approach as a "destination reveal" before showing gameplay, pairing naturally with a starfield warp intro above it.
Story-driven scrollytelling
Anchor chapter one of a scroll narrative on arrival at a world, then hand off to a scroll camera path for the surface tour.
Teaching procedural vertex coloring
A compact demonstration of painting geometry with BufferAttribute colors instead of textures, plus the BackSide atmosphere trick.
Portfolio hero with a destination metaphor
Frame your work as "arriving somewhere" — the orbit-established endpoint makes a natural transition into a project grid.
Ambient generative-art pages
Let the clock-driven rotation and cloud drift carry the scene between scrolls, similar to the idle motion in galaxy formation.

Got questions?

Frequently Asked Questions

Each vertex of the SphereGeometry is colored directly through a color BufferAttribute. The vertex normal is fed into a sum of sin/cos products at different frequencies, and the result is thresholded: above 0.35 paints land green, below paints ocean blue, and any vertex with |y| > 0.86 becomes polar ice. MeshLambertMaterial with vertexColors: true interpolates between vertices, so the 96-segment sphere reads as soft continents with zero network requests.

A second sphere 7% larger than the planet is rendered with side: THREE.BackSide, so only the inside of its far hemisphere draws — visually a rim halo hugging the planet's limb. Its opacity is tied to the scrubbed progress value (0.08 in deep space up to ~0.38 in orbit), which imitates entering the atmosphere's optical thickness without writing GLSL.

The tween stays ease: "none" so one linear 0–1 value is shared by every derived quantity. Applying 1 − (1 − p)^2.2 only to the distance lets the camera brake late while the swing angle and moon advance stay linear — different easings per property from a single scrub, which one eased tween could not provide.

A straight-line approach at a sphere only changes silhouette size, which reads as a zoom rather than travel. Advancing the camera's orbital angle 2.1 radians during the approach sweeps the day/night terminator across the visible face and generates parallax against the moon and starfield, so the motion reads as flight through space.

Yes. Click JSX, Vue, Angular, or Tailwind to export a component. Build the scene inside a mount effect (useEffect / onMounted / ngAfterViewInit) against a canvas ref, and in the cleanup kill the ScrollTrigger instance, dispose the sphere geometries and materials, and call renderer.dispose() so the WebGL context and scroll pin are released on unmount.