webdevpuneet.com

GLB Wireframe / Solid Toggle Viewer — Free Three.js glTF Cross-Fade Snippet

GLB Wireframe / Solid Toggle Viewer · Media · Plain HTML, CSS & JS · Live preview

Copy & paste

Source Code

<div class="gwf-wrap">
  <canvas id="gwfCanvas"></canvas>
  <div class="gwf-hint">Drag to orbit · Ctrl/Cmd + scroll to zoom</div>
  <div class="gwf-zoom">
    <span class="gwf-zoom-label">+</span>
    <input type="range" id="gwfZoom" class="gwf-zoom-slider" min="0" max="100" step="1" />
    <span class="gwf-zoom-label">&minus;</span>
  </div>
  <div class="gwf-panel">
    <div class="gwf-panel-title">Render mode</div>
    <input type="range" id="gwfMix" class="gwf-mix-slider" min="0" max="100" step="1" value="0" />
    <div class="gwf-panel-row"><span>Solid</span><span id="gwfMixLabel">Solid</span><span>Wireframe</span></div>
  </div>
</div>

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

Real glTF binary model
Loaded via THREE.GLTFLoader from an actual .glb file, not a primitive shape.
Continuous solid-to-wireframe blend
A single slider cross-fades opacity across the full 0-100% range, not a binary toggle.
Shared geometry, dual materials
Solid and wireframe clones reference the same geometry object, doubling no memory.
Baked world-matrix flattening
Each clone's transform is copied directly from the source, no hierarchy to maintain.
Auto-fit before flattening
Scale and ground alignment happen on the source model before it's cloned.
Always-complementary opacity
Solid and wireframe opacity always sum to 1, so the model never disappears mid-slide.
Honest load-failure fallback
A logged error swaps in a placeholder run through the same dual-material pipeline.
Slider + Ctrl/Cmd-scroll zoom
Zoom is an explicit, opt-in gesture, never a hijacked plain scroll wheel.

About this UI Snippet

GLB Wireframe / Solid Toggle Viewer — A Continuous Blend, Not a Binary Flip

Screenshot of the GLB Wireframe / Solid Toggle Viewer snippet rendered live

The obvious way to toggle wireframe mode on a Three.js model is flipping material.wireframe = true. That gives a hard, instant snap between two looks. This snippet does something more useful for actually studying a model's geometry: it builds two full mesh clones of every part — one solid-shaded, one wireframe — layered exactly on top of each other, and cross-fades their opacity continuously from a single slider, so a visitor can rest anywhere between "fully solid" and "fully wireframe," not just at the two extremes.

Two meshes per part, sharing one geometry

For every mesh found by traversing the loaded model, buildDualMaterialModel() creates two new THREE.Mesh instances that both reference the *same* node.geometry object — no geometry is duplicated in memory, only the materials differ. One gets a MeshStandardMaterial for solid shading, the other a MeshBasicMaterial with wireframe: true. Both start transparent: true so their opacity can be driven independently.

Baking world transforms instead of reparenting

Rather than trying to preserve and reparent the original model's node hierarchy (position, rotation, and scale at every level), each cloned mesh has matrixAutoUpdate turned off and its .matrix set directly from the source node's already-computed matrixWorld — after the auto-fit scale and ground-alignment shift have both already been applied and the world matrices recomputed with updateMatrixWorld(true). That one .matrix copy is all that's needed to place each flattened clone exactly where its original part was, with no hierarchy to maintain afterward.

The slider drives one function, both groups

applyMix(t01) sets every solid clone's opacity to 1 - t01 and every wireframe clone's opacity to t01 in the same call, so the two groups are always perfectly complementary — there's no moment where both are fully opaque or both are fully transparent, and the model never visually disappears at any point along the slider's range.

Auto-fit before flattening, not after

The bounding-box measurement, scale, and ground-position shift all happen on the *original* loaded model, with model.updateMatrixWorld(true) called before flattening into the dual-material clones — so every clone's baked world matrix already reflects the correctly-sized, correctly-grounded final position.

A named, honest fallback if the model fails

If the .glb can't load, a simple icosahedron is run through the exact same buildDualMaterialModel() function, so the solid/wireframe slider keeps 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 other .glb with clean, readable edges, or pair this with GLB lighting studio viewer to study how lighting and geometry inspection combine.

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 solid-shaded.
  3. 3
    Drag the render-mode sliderThe model continuously cross-fades from solid toward pure wireframe.
  4. 4
    Drag on the canvasOrbit freely at any blend amount to inspect edges from any angle.
  5. 5
    Use the slider or Ctrl/Cmd + scroll to zoomPlain scroll always scrolls the page; zoom is a separate, opt-in gesture.
  6. 6
    Swap the model URLPoint MODEL_URL at any other .glb to inspect a different model's geometry.

Real-world uses

Common Use Cases

3D topology review
Inspect a model's edge flow by blending between solid and wireframe with one slider, rather than a hard on and off snap.
Engineering portfolios
Demonstrate technical depth with a model that cross-fades opacity across two layered clones sharing the same geometry.
Game asset QA
Check a character or prop's silhouette and mesh density, with each clone's transform flattened from the source world matrix.
Architecture and product previews
Blend between a finished look and the structural skeleton, helping clients understand how an object is built.
Lighting and geometry together
Pair with the GLB lighting studio viewer to evaluate both mood and mesh quality on the same real model.
Related: ResizeObserver Live Card
See the ResizeObserver Live Card for a related cards pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

Flipping a single material's wireframe boolean only produces a hard, instant snap between two looks. Building a solid-shaded clone and a wireframe clone that share the same underlying geometry, both marked transparent, lets their opacity be cross-faded continuously from a slider — so a visitor can rest anywhere between fully solid and fully wireframe, which a boolean toggle simply cannot represent.

No. Both the solid and wireframe THREE.Mesh instances for a given part reference the exact same geometry object — only two new Mesh wrapper objects and two new materials are created per part, not two copies of the vertex, normal, and UV data. Geometry, which is typically the largest chunk of memory a model uses, is never duplicated.

Preserving a full node hierarchy (with nested position/rotation/scale at every level) for two full clones would be significantly more bookkeeping than needed. Instead, each source node's already-computed matrixWorld — after the auto-fit scale and ground-alignment shift are both applied — is copied directly onto each flattened clone's own .matrix, with matrixAutoUpdate turned off. One matrix copy per clone places it correctly with zero hierarchy left to maintain.

The loader's error callback logs the real failure and builds a simple icosahedron group, which is passed through the exact same buildDualMaterialModel() function the real model uses — so the render-mode slider keeps working correctly, cross-fading a placeholder shape instead of silently doing nothing.

Set up the renderer, scene, GLTFLoader call, OrbitControls, and the solidGroup/wireGroup pair inside a mount effect, keeping them in refs so the slider's input handler can reach current values. Call controls.dispose() and renderer.dispose() in the cleanup function to release the WebGL context and drag listeners on unmount.