webdevpuneet.com

Three.js Scroll Asteroid Belt — InstancedMesh Dodge Run

Three.js Scroll Asteroid Belt Run · Scroll · Plain HTML, CSS & JS · Live preview

Copy & paste

Source Code

<section class="ast-stage" id="astStage">
  <div class="ast-intro"><p>Scroll ↓ to thread the belt</p></div>
  <canvas id="astCanvas"></canvas>
  <div class="ast-hud">NEAR MISSES <span id="astMiss">0</span></div>
</section>
<section class="ast-bottom"><p>Clear of the field.</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

320 asteroids in ONE InstancedMesh draw call, matrices recomposed per frame via a shared dummy Object3D
DynamicDrawUsage hint on the instance buffer for per-frame matrix updates
Seeded per-vertex jitter on one DodecahedronGeometry makes every instance read as unique debris
Analytic weaving path from incommensurate sines — flyable corridor guaranteed at construction, zero collision tests
Gate asteroids at reduced clearance (3.4 vs 6.5) for parameterized, not random, danger
Banking from the path's numerical X-derivative — the camera leans into turns like a cockpit
Near-miss counting with re-arm logic that survives scrub oscillation and reverse runs
Per-instance hot-flash via setColorAt, still within the single draw call; FogExp2 density illusion

About this UI Snippet

How to Build a Scroll-Driven Asteroid Belt Run With InstancedMesh and GSAP

Screenshot of the Three.js Scroll Asteroid Belt Run snippet rendered live

The Three.js Scroll Asteroid Belt Run snippet flies the camera on a weaving, banking path through 320 tumbling asteroids — every rock a single InstancedMesh instance, so the whole field costs one draw call — with "gate" asteroids seeded deliberately close to the flight path that flash hot and increment a NEAR MISSES counter as the camera threads them. GSAP's ScrollTrigger scrubs the camera's Z; the corridor, the banking, and the drama are all construction-time guarantees.

One draw call for the whole field

Rendering 320 individual Mesh objects means 320 draw calls and 320 scene-graph nodes. InstancedMesh submits one geometry, one material, and a buffer of per-instance matrices — the GPU stamps the rock 320 times in a single call. Each frame the loop recomposes every matrix through a shared Object3D dummy (position from placement, rotation from clock-time tumble, scale from seed) and flags instanceMatrix.needsUpdate. With DynamicDrawUsage hinting the driver that this buffer changes every frame, the field animates as cheaply as a particle system while remaining fully lit, shadowed, solid geometry. The base rock itself is a DodecahedronGeometry with seeded per-vertex radial jitter, so instances read as debris rather than dice.

A corridor guaranteed at construction

Dodge-field scenes fail when a rock happens to spawn on the flight path. Here the path is two analytic functions — pathX(z) and pathY(z), sums of incommensurate sines that weave without repeating — and every asteroid is placed *relative to the path at its own Z*: a random angle around the centerline at a radius of at least 6.5 units of clearance. The corridor is therefore flyable by construction, no collision testing ever needed. The 12% of rocks flagged as gates get clearance 3.4 instead — close enough to feel dangerous, still outside the camera's envelope. Threat placement is a parameter, not luck.

Banking sells the steering

A camera translated along a curve looks like it is being dragged; a camera that *banks* looks like it is flying. The roll angle is the numerical X-derivative of the path — (pathX(z−1) − pathX(z+1)) × 0.09 — so the camera leans into every turn exactly as hard as the turn demands, with look-at aimed 14 units down-path. One derivative turns a slide into a cockpit, the first-person sibling of the bridge crossing's footstep bob.

Near-misses with scrub-safe counting

Gate rocks within 6 units of the camera tint hot orange via setColorAt — per-instance color, still one draw call — and increment the counter once per pass. The counted flag re-arms only when the camera retreats fully above the gate, so oscillating mid-pass cannot double-count, and scrolling back up genuinely re-arms the gates for another run. It is the honest-counter philosophy of the portal gate sequence adapted to stateful counting on a scrubbed axis.

Fog as density illusion

FogExp2 matched to the background makes rocks materialize gradually out of the dark ahead — the belt feels endless although only 320 rocks exist across 480 units, the same budget-infinity trick as the scroll tunnel. Star dust points scattered through the corridor depth supply parallax between rock encounters. For the physics-flavored cousin where the field reacts to you, see magnetic particles; for the warp-speed version with no obstacles, starfield warp.

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 JSYou hover at the edge of a fog-shrouded asteroid field, rocks tumbling ahead — NEAR MISSES 0 in the HUD.
  3. 3
    Scroll to flyThe camera weaves and banks along the corridor as asteroids materialize from the fog and slide past on both sides.
  4. 4
    Thread the gatesOccasional rocks sit deliberately close — they flash hot orange as you pass and the near-miss counter ticks up.
  5. 5
    Scroll backThe run reverses; gates re-arm as you retreat above them, ready to count again on the next pass.
  6. 6
    Tune the dangerRaise the gate probability (0.88 threshold), lower gate clearance (3.4), or add rocks — the corridor stays flyable by construction.

Real-world uses

Common Use Cases

Space shooter and racer promos
The dodge-run is the genre's core fantasy, scrubbed — hand off to a product viewer for the ship itself.
"Navigating complexity" B2B narratives
Compliance, security, and logistics products get threats threaded in first person, with near-misses as the risk counter.
Teaching InstancedMesh fundamentals
The complete pattern in one scene: shared dummy, matrix recomposition, DynamicDrawUsage, and per-instance color — the single most important Three.js scaling technique.
High-energy portfolio openers
A banking flythrough outpaces static heroes; the fog reveal paces long scrolls like the scroll tunnel.
Risk and anomaly storytelling
Map gates to real incidents — each flash a logged event, the counter a running total, annotated via scroll pin steps.
Ambient space scenes
Zero out the gates and slow the path for a drifting debris-field meditation, kin to the comet trail.

Got questions?

Frequently Asked Questions

Each Mesh costs a draw call plus scene-graph overhead; 320 of them would bottleneck most devices. InstancedMesh submits geometry and material once with a buffer of per-instance matrices — one draw call stamps all 320 rocks. The frame loop recomposes matrices through a shared Object3D dummy and sets instanceMatrix.needsUpdate, with DynamicDrawUsage hinting the GPU driver that the buffer updates every frame. Scaling to 2,000 rocks changes one constant.

The path is analytic — pathX(z) and pathY(z) are sums of sines — and every asteroid is placed RELATIVE to the path evaluated at its own Z: random angle, radius of at least 6.5 units (3.4 for gates). Since placement starts from the centerline outward, no rock can occupy the corridor. It is a construction-time guarantee, so the scene needs no collision detection at runtime, ever.

Banking: camera.rotation.z is set to the numerical derivative of the lateral path — (pathX(z−1) − pathX(z+1)) × 0.09 — so it rolls into each turn proportionally to how sharp the turn is, while lookAt aims 14 units down-path. Translation alone reads as being carried; the derivative-driven roll reads as steering, for one subtraction per frame.

Each gate has a counted flag: it counts when the camera first comes within 6 units approaching from the front (dz > −1), and only re-arms once the camera retreats more than 6 units back above it. Small oscillations around the gate cannot re-trigger it, but a genuine reverse run re-arms every gate — deliberate state on top of a scrubbed axis, designed so both directions behave sensibly.

Yes. Export via the JSX, Vue, Angular, or Tailwind buttons. Build the InstancedMesh, asteroid parameter array, and ScrollTrigger in a mount effect against a canvas ref; the misses counter and dummy Object3D live in effect scope, with the HUD updated through a ref. On cleanup kill the ScrollTrigger, dispose the rock geometry/material (one of each — that is the instancing win), the star system, and call renderer.dispose().