webdevpuneet.com

Three.js Scroll Möbius Ride — Camera on a One-Sided Surface

Three.js Scroll Möbius Strip Ride · Scroll · Plain HTML, CSS & JS · Live preview

Copy & paste

Source Code

<section class="mob-stage" id="mobStage">
  <div class="mob-intro"><p>Scroll ↓ to ride the Möbius strip</p></div>
  <canvas id="mobCanvas"></canvas>
  <div class="mob-hud">LAP <span id="mobLap">0</span>%</div>
</section>
<section class="mob-bottom"><p>One surface. One edge. Two laps to come home.</p></section>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month, 7 days free), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

True Möbius parametrization — the u/2 half-angle term builds the one-sided surface from its equation
Indexed 260×10 BufferGeometry grid with computed normals, rendered DoubleSide
Camera frame derived from finite-difference surface tangents — camera.up follows the local normal, no world up
The ride rolls fully inverted at one lap and recovers at two, experiencing the topology rather than asserting it
Lap-two normal negation keeps the surface underfoot across the parametric seam
Single boundary edge traced over 4π with alternating v-sides — one closed loop, as topology demands
36 runway dashes brightening near the rider with proper 2π angular-distance wrapping
Scrub-honest: pause inverted at the halfway point, reverse to unwind the roll

About this UI Snippet

How to Build a Scroll-Driven Möbius Strip Ride With Three.js and GSAP

Screenshot of the Three.js Scroll Möbius Strip Ride snippet rendered live

The Three.js Scroll Möbius Strip Ride snippet puts the camera on the centerline of a mathematically real Möbius strip and lets scroll drive it around — with the twist that makes the topology famous: after one full ring lap the camera is riding the *underside* of the surface, upside down relative to where it started, and only after a second lap (u = 4π) does it come home. GSAP's ScrollTrigger scrubs the ride parameter; the geometry, the roll, and the two-lap structure all fall out of the parametrization.

Building the strip from its equation

The surface is generated directly from the Möbius parametrization: for ring angle u and width coordinate v, the point is ((R + vW·cos(u/2))·cos u, vW·sin(u/2), (R + vW·cos(u/2))·sin u). The u/2 half-angle is the entire secret — the strip's cross-section rotates half a turn while u makes a full turn, joining the ribbon to itself with a flip. A 260×10 grid of these points becomes an indexed BufferGeometry with computeVertexNormals(), rendered DoubleSide because a one-sided surface, ironically, needs both GL faces drawn.

A camera frame with no world up

Riding a surface that turns you upside down means camera.lookAt with the default world-up would fight the roll and flip violently at the poles of the twist. Instead the snippet constructs the rider's frame from the surface itself: two finite-difference tangents (one step along u, one along v) cross-multiplied into a local surface normal, the camera positioned 1.5 units along that normal, camera.up set to it, and the look-at aimed at a point further along the centerline lifted by the same normal. The camera therefore rolls exactly as the surface rolls — by the end of lap one it is fully inverted, and most viewers only notice when the fog-dimmed far side of the ring appears "above" them.

The 4π edge and the lap-two flip

Two details encode the topology honestly. The glowing boundary line is built by walking u from 0 to 4π while alternating the v = ±1 side each lap — because a Möbius strip has exactly one edge, and that edge closes only after two circuits. And during lap two, the finite-difference normal (which always samples the same parametric side) is negated so the ride surface stays underfoot; without the flip the camera would clip through the strip the moment u crossed 2π. These are the kinds of sign subtleties that make one-sided surfaces a rite of passage.

Speed made visible

A camera gliding over a smooth purple ribbon has no motion cues, so 36 dash markers sit along the centerline, oriented by lookAt toward the next sample. Each dash brightens as the rider approaches — opacity derived from angular distance with proper 2π wrapping — producing a runway-light effect that communicates both speed and direction, similar in role to the recycled particles of the scroll tunnel. The single edge pulses slowly on clock time so the scene idles alive, per this series' convention.

Why scrub suits this topology

The Möbius property is an experiential claim — "go around once and you are flipped" — that a fired animation asserts but a scrub *proves*: users can stop at u = 2π, look around while inverted, and back up to re-watch the roll accumulate. The gear train scrubs an angle and the pendulum wave scrubs time; this scrubs a coordinate on a surface, completing the series' tour of what a single scrubbed number can be. For the straight-line cousin of this ride, see the staircase climb.

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 view sits on the purple strip's centerline, runway dashes ahead, the single glowing edge pulsing at both sides.
  3. 3
    Scroll to rideThe camera glides along the ribbon while rolling with the surface — the horizon tilts continuously as the half-twist accumulates.
  4. 4
    Pause at 50%At one full ring lap you are riding the underside, fully inverted — look up at where you started.
  5. 5
    Complete lap twoOnly at 100% (u = 4π) does the camera return to its starting orientation — the Möbius property, proven by scroll.
  6. 6
    Reshape the ribbonChange R and W for ring radius and strip width, or replace u/2 with 3u/2 in mobiusPoint for a three-half-twist strip — the frame math adapts unchanged.

Real-world uses

Common Use Cases

Math education and topology pages
The strongest possible demo of one-sidedness: students ride the flip, pause inverted, and reverse it — pair with a pendulum wave for a physics-and-math duo.
Infinity-themed brand loops
Logistics, recycling, and subscription brands get a literal endless-loop hero where the journey visibly never hits an edge.
Award-site signature interactions
A camera that rolls upside down mid-scroll is a memorable centerpiece, more disorienting than a scroll tunnel flythrough.
Album, film, and title sequences
Use the inversion moment as a narrative beat — drop a title card exactly at u = 2π when the world is upside down.
Puzzle and perspective game promos
Monument-Valley-adjacent games can let visitors experience impossible geometry before seeing gameplay.
Generative and kinetic art portfolios
Present the strip as sculpture with the ride as the exhibition path, alongside pieces like the wave ribbon.

Got questions?

Frequently Asked Questions

That is the defining property of a Möbius strip. The u/2 term rotates the strip's cross-section half a turn per ring lap, so after u = 2π the rider is on the same surface but locally inverted — the "other side" that does not exist as a separate side. Only after 4π of travel has the accumulated roll returned to identity. The scrub lets users verify this by pausing at 50% and looking around while upside down.

lookAt orthonormalizes against camera.up; with a fixed world up, the computed right vector degenerates as the surface rolls past 90° and the camera snaps or spins wildly. Setting camera.up to the local surface normal each frame — computed from cross(tangent-along-u, tangent-along-v) — lets the camera roll smoothly and continuously through full inversion, which is the entire point of the ride.

The finite-difference normal always samples the same parametric v-direction, but after u = 2π the rider is physically on the opposite face of the ribbon, where that sampled normal points into the surface. Negating it during lap two keeps the camera hovering 1.5 units above the face underfoot instead of clipping through the strip at the seam. It is a one-line fix for a sign error that is invisible until it teleports the camera.

A Möbius strip has exactly one edge: follow the v = +1 border around the ring and you arrive at v = −1 of your starting point, needing a second lap to close the loop. The edge line walks u through 4π while alternating which v-extreme it samples per lap, producing the single closed loop. Drawing two separate v = ±1 circles would render the topology of a cylinder — visibly wrong once you know what to look for.

Yes. Export via the JSX, Vue, Angular, or Tailwind buttons. Build the parametric geometry, dashes, and ScrollTrigger in a mount effect against a canvas ref, reusing the module-scope scratch vectors as effect-scope consts. On cleanup kill the ScrollTrigger, dispose the strip and edge geometries, dash geometries/materials, and call renderer.dispose(). The ride value lives in a plain object — never React state.