webdevpuneet.com

GLB Exploded View Assembly Toggle — Free Three.js glTF Snippet

GLB Exploded View Assembly Toggle · Media · Plain HTML, CSS & JS · Live preview

Copy & paste

Source Code

<div class="gxv-wrap">
  <canvas id="gxvCanvas"></canvas>
  <div class="gxv-hint">Drag to orbit · Ctrl/Cmd + scroll to zoom</div>
  <div class="gxv-zoom">
    <span class="gxv-zoom-label">+</span>
    <input type="range" id="gxvZoom" class="gxv-zoom-slider" min="0" max="100" step="1" />
    <span class="gxv-zoom-label">&minus;</span>
  </div>
  <div class="gxv-panel">
    <div class="gxv-panel-title">Assembly</div>
    <input type="range" id="gxvExplode" class="gxv-explode-slider" min="0" max="100" step="1" value="0" />
    <div class="gxv-panel-row">
      <span id="gxvExplodeLabel">Collapsed</span>
      <span id="gxvPartCount">&hellip; parts</span>
    </div>
    <button id="gxvAutoBtn" class="gxv-auto-btn">Animate explode</button>
  </div>
</div>

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

Real glTF binary model
Loaded via THREE.GLTFLoader from an actual .glb file, not a primitive shape.
Automatic per-part explode direction
Computed from each part's position relative to the assembly's own center.
Works on any glTF hierarchy
model.traverse() flattens any nesting depth into a flat, explodable parts list.
Non-accumulating explode logic
Positions always recompute from a captured baseline, never drift over time.
Manual slider plus idle animation
Both control paths converge on one applyExplode() function.
Live real part count
Shows the actual number of mesh nodes found in the loaded model.
Multi-part honest fallback
A three-piece placeholder assembly keeps the explode control functional on failure.
Slider + Ctrl/Cmd-scroll zoom
Zoom is an explicit, opt-in gesture, never a hijacked plain scroll wheel.

About this UI Snippet

GLB Exploded View Assembly Toggle — Any glTF Hierarchy, Pulled Apart Automatically

Screenshot of the GLB Exploded View Assembly Toggle snippet rendered live

Exploded diagrams are everywhere in product manuals and assembly guides, but building one usually means hand-authoring an offset for every single part. This snippet does it generically instead: it loads a real .glb model, flattens every mesh node in its hierarchy into a flat list, and computes each part's own outward "explode direction" automatically from the assembly's bounding-box center — no part needs a hand-authored offset, and the same code works on any glTF file with more than one mesh.

Flattening the hierarchy, not assuming a specific structure

model.traverse() walks every node in the loaded scene graph at any depth and collects every isMesh node into a flat meshNodes array — regardless of how deeply the source .glb nests its parts inside groups. This is what makes the technique reusable: it makes no assumption about a specific model's node names or structure.

Direction from geometry, not authored by hand

For each mesh, its current world-space position is compared against the assembly's own overall bounding-box center (overallBox.getCenter()), and the normalized difference becomes that part's permanent direction vector — parts near the center barely move, parts near the edges of the assembly explode outward the furthest, exactly like a real technical exploded diagram, without a single manually-tuned number.

Recomputed from a stable baseline, never accumulated

Each part also keeps its originalPosition, captured once at load time. applyExplode(amount01) always sets p.node.position back to that original position plus direction * amount01 * MAX_OFFSET — never adds to whatever the position currently is. That means dragging the slider back and forth thousands of times, or restarting the auto-play animation mid-way, can never drift a part's position off from where it should be.

A slider, plus an optional idle animation

The explode amount is driven by a plain range input for direct manual control, and a separate "Animate explode" toggle drives the exact same applyExplode() function from a ping-ponging value inside the render loop — both paths converge on one function, so there's only ever one source of truth for where a part currently sits.

A real, honest fallback with its own parts

If the .glb fails to load, the fallback isn't a single placeholder mesh — it's a small three-piece placeholder group, each piece registered into the same parts array with its own computed direction, so the explode slider and animate button both keep working correctly even without the real model.

Zoom is opt-in, not a hijacked scroll wheel

OrbitControls' built-in wheel-zoom is turned off, with zoom reimplemented as a slider plus Ctrl/Cmd + scroll, so a plain scroll over the card always scrolls the page.

Customizing it

Swap MODEL_URL for any multi-part .glb, tune MAX_OFFSET for a bigger or smaller explosion, or pair this with GLB hotspot annotation viewer to label each part once it's pulled apart.

Step by step

How to Use

  1. 1
    Add all three CDN scriptsthree.min.js, GLTFLoader.js, and OrbitControls.js.
  2. 2
    Paste HTML, CSS, and JSThe camera model loads fully assembled, with its real part count shown.
  3. 3
    Drag the assembly sliderEvery part slides outward from the assembly's own center, proportionally.
  4. 4
    Click "Animate explode"The explosion ping-pongs automatically between collapsed and exploded.
  5. 5
    Drag on the canvasOrbit the camera freely at any explode amount.
  6. 6
    Use the slider or Ctrl/Cmd + scroll to zoomPlain scroll always scrolls the page; zoom is a separate, opt-in gesture.
  7. 7
    Swap the model URLPoint MODEL_URL at any multi-part .glb to explode a different assembly.

Real-world uses

Common Use Cases

Product manuals and assembly guides
Show how a real product fits together, with each mesh part moving outward along a direction computed from the model's own bounding box.
Engineering and CAD portfolios
Demonstrate exploded diagrams from a real glTF file without authoring an offset for each individual part.
What's inside sections
Let shoppers see a product's internals with a single slider, where positions always recompute from a captured baseline rather than accumulating.
glTF hierarchy teaching
Learn how model.traverse() flattens nested nodes of any depth into a simple list of parts to move.
Annotated explode views
Pair with the GLB hotspot annotations viewer to label each part once the assembly has been pulled apart.
Related: Profile Completeness Card — Weighted Progress with Next-Best-Action
See the Profile Completeness Card — Weighted Progress with Next-Best-Action for a related cards pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

For every mesh node found by traversing the loaded model, the snippet compares that mesh's current world position against the bounding-box center of the entire assembly, and normalizes the difference into a direction vector. Parts near the assembly's center barely move outward; parts near its edges move the furthest — the same logic a real technical exploded diagram follows, computed automatically from geometry rather than authored by hand for a specific model.

Each part keeps its original local position captured once at load time. applyExplode() always sets a part's position to that original position plus its direction scaled by the current explode amount — never adds an increment to wherever the part currently sits. That means the slider, the animate-toggle, and repeated back-and-forth dragging can never accumulate drift; the same slider value always produces the exact same part positions.

Any model with more than one mesh node, at any nesting depth. model.traverse() walks the entire loaded hierarchy and collects every mesh it finds into a flat list, with no assumption about node names, groups, or how the source .glb organized its parts — the direction computation only needs each part's world position, which every mesh has regardless of authoring convention.

The loader's error callback logs the real failure and builds a small three-piece placeholder group instead, registering each piece into the same parts array the slider and animate button already read from — so the exploded-view interaction keeps working correctly even without the real model.

Set up the renderer, scene, GLTFLoader call, and OrbitControls inside a mount effect, keeping the parts array, assemblyCenter, and autoPlaying flag in refs so the slider input handler and the render loop can reach current values. Call controls.dispose() and renderer.dispose() in the cleanup function to release the WebGL context and drag listeners on unmount.