webdevpuneet.com

Three.js Wave Ribbon — Flowing 3D TubeGeometry Animation

Three.js Wave Ribbon · Animations · Plain HTML, CSS & JS · Live preview

CategoryAnimations

Copy & paste

Source Code

Requires
<canvas id="ribbonCanvas"></canvas>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), 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

Animated CatmullRomCurve3: a smooth curve through a handful of control points defines the ribbon's path
TubeGeometry extrusion: a circular cross-section swept along the curve produces the visible mesh
One-line ribbon flattening: geometry.scale() squashes a round tube into a flat ribbon shape
Per-frame geometry rebuild: TubeGeometry is recreated (not mutated) since its shape depends on curve tangents
Proper GPU cleanup: the old geometry is disposed before each replacement to avoid memory buildup
Phase-per-control-point wave: a staggered sine/cosine offset makes the undulation visibly travel along the ribbon
Dual-color point lighting: two differently colored lights give the glossy surface dimensional highlights
Loaded entirely from a CDN: no npm install, bundler, or build step required

About this UI Snippet

How to Build a Flowing Three.js Ribbon With Animated TubeGeometry

Screenshot of the Three.js Wave Ribbon snippet rendered live

The Three.js Wave Ribbon snippet renders a flattened, glossy ribbon that undulates continuously along its length — the wave visibly traveling from one end to the other rather than the whole shape bobbing in place — by animating the control points of a CatmullRomCurve3 and rebuilding a TubeGeometry from it every frame, using core Three.js loaded from a CDN.

A curve, not a mesh, is the thing being animated

Rather than displacing vertices on a flat plane mesh directly (the technique used in the particle wave and synthwave terrain snippets), this ribbon's actual shape comes from a THREE.CatmullRomCurve3 — a smooth curve interpolated through a small number of control points. TubeGeometry then extrudes a circular cross-section along that curve's entire length, producing the tube-like mesh you actually see. Animating the curve's handful of control points, rather than hundreds of individual mesh vertices, is a far simpler mental model for anything shaped like a rope, cable, or ribbon.

Flattening a round tube into a ribbon with one line

TubeGeometry only supports a circular cross-section natively — there's no built-in "flat ribbon" option. This snippet sidesteps that limitation with one extra line: geometry.scale(1, 0.12, 1), squashing the finished tube geometry down on one axis after it's built. A circular tube squashed to roughly an eighth of its height reads convincingly as a flat ribbon rather than a rounded cable, without needing any custom cross-sectional geometry.

Why the geometry is rebuilt, not mutated in place

Every other snippet in this collection that animates a shape (the particle wave, the morphing blob) mutates an existing BufferGeometry's attributes directly rather than recreating it. TubeGeometry is the exception, and for a specific reason: its triangulated surface depends on the curve's *tangent direction* at every sampled point along its length, which changes in a complex way whenever any control point moves. Recomputing that correctly requires re-running TubeGeometry's own construction logic — which is exactly what calling new THREE.TubeGeometry(curve, ...) again does, followed by disposing the old geometry to free its GPU memory before replacing it.

Phase-per-index is what makes the wave travel

Every control point's Y and Z position is driven by a sine and cosine wave, but each point's phase offset is index * 0.55 — so point 0 and point 10 are always at different points in the wave cycle at any given moment. This staggered phase, growing steadily along the ribbon's points, is what makes the undulation visibly *travel* from one end toward the other, rather than every point moving up and down in unison, which would look like the whole ribbon breathing rather than flowing.

Two lights, two colors, for depth

A blue point light and a pink point light, positioned on opposite sides of the scene, give the glossy MeshStandardMaterial two distinct highlight colors to catch as the ribbon twists — a detail that reads as more dimensional than a single light source would, especially combined with side: THREE.DoubleSide so the ribbon's underside is visible and lit correctly whenever the curve twists it into view.

Where this technique applies

Animated-curve-plus-TubeGeometry is the standard approach for any rope, cable, snake, or flowing-fabric effect in Three.js. Pair this ribbon with a scroll camera path flythrough for a decorative flowing element woven through a scroll sequence, or contrast its smooth, continuous flow against the discrete, segmented look of the DNA helix.

Step by step

How to Use

  1. 1
    Load the Three.js CDNAdd three.min.js from the CDN panel — TubeGeometry and CatmullRomCurve3 are part of core Three.js.
  2. 2
    Paste HTML, CSS, and JSA flowing ribbon appears immediately, undulating continuously along its length.
  3. 3
    Watch the wave travelThe undulation visibly moves from one end of the ribbon toward the other, driven by per-point phase offsets.
  4. 4
    Tune the wave shapeAdjust the phase multiplier and sine/cosine amplitudes for a tighter, looser, or more twisted flow.
  5. 5
    Adjust the ribbon's flatnessChange the 0.12 scale factor for a flatter ribbon or a rounder, more tube-like shape.
  6. 6
    Resize the windowRenderer size and camera aspect ratio update automatically.

Real-world uses

Common Use Cases

Hero section decorative elements
A flowing ribbon adds organic motion behind or alongside a headline, distinct from particle or grid effects.
Brand and identity animations
A smoothly flowing ribbon in brand colors works as a signature animated logo element or section divider.
Teaching curve-based geometry
A focused, minimal example of CatmullRomCurve3 and TubeGeometry — the standard technique for rope-like 3D shapes.
Portfolio and agency showpieces
A fluid, continuously flowing 3D element demonstrates curve-based modeling skill beyond static meshes.
Loading and transition animations
A calm, flowing ribbon gives a loading screen ambient motion without competing with foreground content.
Fantasy and magic-themed visuals
Reuse the flowing-curve technique for magical energy trails, spell effects, or ribbon-like game UI flourishes.

Got questions?

Frequently Asked Questions

The ribbon's visible shape comes from TubeGeometry extruding a cross-section along a CatmullRomCurve3. Animating the curve's small number of control points is a much simpler mental model for a rope-like or ribbon-like shape than trying to directly displace hundreds of individual mesh vertices while keeping the surface smooth and continuous.

TubeGeometry only natively supports a circular cross-section. After building the tube geometry, a single call to geometry.scale(1, 0.12, 1) squashes it down on one axis, flattening the once-round cross-section into something that reads as a ribbon rather than a rounded cable.

TubeGeometry's triangulated surface depends on the curve's tangent direction at every sampled point along its length, which changes in a complex way whenever any control point moves. Correctly recalculating that requires re-running TubeGeometry's own construction logic, which is why the snippet creates a new geometry each frame (disposing the old one) rather than mutating an existing BufferAttribute in place.

Every control point's position is driven by a sine and cosine wave, but each point's phase offset within that wave grows with its index along the ribbon. Because point 0 and point 20 are always at different phases of the same wave cycle, the undulation visibly propagates from one end toward the other rather than the whole ribbon bobbing up and down in unison.

No, as long as the previous geometry is explicitly disposed before being replaced, which this snippet does with ribbon.geometry.dispose() immediately before assigning the newly built geometry. Skipping that disposal call would leak GPU memory over time, since Three.js does not automatically garbage-collect a geometry object simply because it is no longer referenced by a mesh.

Yes. Click JSX for a React component, Vue for a Vue 3 SFC, Angular for a standalone component, or Tailwind for a React + Tailwind CSS utility-class version. Keep the control points array and curve object in a ref so they persist across renders, rebuild and dispose the geometry inside the same animation loop, and call renderer.dispose() on cleanup.