webdevpuneet.com

GLB AR-Style Pedestal Viewer — Free Three.js glTF "Placed Object" Snippet

GLB AR-Style Pedestal Viewer · Media · Plain HTML, CSS & JS · Live preview

Copy & paste

Source Code

<div class="gap-wrap">
  <canvas id="gapCanvas"></canvas>
  <div class="gap-badge">
    <span class="gap-badge-dot"></span> Placed on pedestal
  </div>
  <div class="gap-hint">Drag to orbit · Ctrl/Cmd + scroll to zoom</div>
  <div class="gap-zoom">
    <span class="gap-zoom-label">+</span>
    <input type="range" id="gapZoom" class="gap-zoom-slider" min="0" max="100" step="1" />
    <span class="gap-zoom-label">&minus;</span>
  </div>
  <button id="gapRecenter" class="gap-recenter">Recenter</button>
</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.
Canvas-drawn contact shadow
A radial-gradient texture reads as a soft shadow with zero shadow-map overhead.
Animated AR-style placement ring
A slowly spinning ring beneath the model signals "anchored here."
Capped camera angle
maxPolarAngle keeps every orbit angle looking like a believable product shot.
Auto-fit onto the pedestal
Bounding-box scaling and positioning lines the model up with the pedestal top exactly.
One-click recenter
Instantly resets the camera back to its original placed-object framing.
Honest load-failure fallback
A logged error swaps in a placeholder mesh, still correctly placed on the pedestal.
Slider + Ctrl/Cmd-scroll zoom
Zoom is an explicit, opt-in gesture, never a hijacked plain scroll wheel.

About this UI Snippet

GLB AR-Style Pedestal Viewer — A "Placed Object" Presentation, Without WebXR

Screenshot of the GLB AR-Style Pedestal Viewer snippet rendered live

Real mobile AR product viewers use a specific visual vocabulary to say "this object is anchored in your space": a soft contact shadow, a subtle ring or reticle under the object, and a small status indicator confirming placement. This snippet borrows that exact language for a plain desktop/web 3D viewer — no camera permission, no WebXR session, no device motion required — by presenting a real loaded .glb model resting on a pedestal with all three of those cues built from ordinary Three.js primitives.

A canvas-drawn radial gradient instead of a real shadow map

Real-time shadow mapping is overkill for a simple "this sits here" cue. Instead, a small offscreen <canvas> is drawn once with a radial gradient — dark near the center, fully transparent at the edge — turned into a THREE.CanvasTexture, and mapped onto a flat circle positioned just above the pedestal's top surface. It reads immediately as a soft contact shadow without a single shadow-casting light or render pass.

A placement ring borrowed directly from real AR UI

A thin RingGeometry sits beneath the model, given a slow, continuous rotation inside the render loop (ring.rotation.z = t * 0.00025) — the same subtle "anchored and alive" animation real AR product placement UIs use to confirm an object is actively tracked, adapted here to a pedestal instead of a real-world floor plane.

A capped polar angle keeps the camera above the pedestal

controls.maxPolarAngle is set just under Math.PI / 2, which prevents OrbitControls from ever dragging the camera down below the pedestal's top surface — a small constraint that keeps every possible camera angle looking like a believable "product on a stand" shot instead of an underside view that breaks the illusion.

Auto-fit onto the pedestal's own top surface

Rather than a hardcoded scale and height, the model is measured with THREE.Box3 after loading, scaled to a fixed target size, then shifted vertically so its lowest point sits exactly at PEDESTAL_TOP — the same known Y coordinate the contact shadow and ring are both already positioned at, so the three pieces always line up regardless of the loaded model's native proportions.

A one-click recenter, not a stuck camera

A "Recenter" button resets the camera back to HOME_POSITION and the orbit target back to the pedestal, so a visitor who's dragged the view somewhere disorienting always has an obvious way back — a small but real usability detail real AR viewers also include.

A named, honest fallback if the model fails

If the .glb can't load, the real error is logged and a simple icosahedron takes the model's exact placed position on the pedestal, so the "placed object" illusion holds even without the real asset.

Customizing it

Swap MODEL_URL for any other product-scaled .glb, adjust PEDESTAL_RADIUS to match a differently-proportioned object, or pair this with GLB product color configurator for a "place, then customize" flow.

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 BoomBox loads resting on the pedestal with a soft contact shadow.
  3. 3
    Drag on the canvasOrbit around the placed object; the camera never dips below the pedestal top.
  4. 4
    Use the slider or Ctrl/Cmd + scroll to zoomPlain scroll always scrolls the page; zoom is a separate, opt-in gesture.
  5. 5
    Click "Recenter"Instantly returns the camera to its original placed-object framing.
  6. 6
    Swap the model URLPoint MODEL_URL at any other .glb to "place" a different object.

Real-world uses

Common Use Cases

AR-style product previews
Show a real product placed on a pedestal with a soft contact shadow and a spinning placement ring, as mobile AR viewers do.
Trophy and collectible showcases
Present an award or figurine as if it were anchored in the viewer's space, with a status badge confirming placement.
Product launch landing pages
Embed a polished interactive object that needs no AR app, with maxPolarAngle keeping every orbit angle looking believable.
Museum-style artefact displays
Display a scanned object on a plinth with a canvas-drawn radial-gradient shadow giving it weight on the surface.
Lighting companion
Pair with the GLB lighting studio viewer to compare how the same model looks on a pedestal and under different light moods.
Related: Product Bundle Builder
See the Product Bundle Builder for a related cards pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

No — it never requests camera access, device motion, or a WebXR session. It borrows the visual language real AR product viewers use (a soft contact shadow, an animated placement ring, a status badge) and applies it to a normal Three.js scene with a pedestal, so it works in any browser with WebGL, no device permissions required.

A small offscreen HTML canvas is drawn once with a radial gradient (dark in the center, fully transparent at the edge), converted into a THREE.CanvasTexture, and mapped onto a flat circle positioned just above the pedestal's top surface. It reads as a soft, grounding shadow without any shadow-casting light, shadow map, or extra render pass.

After loading, the model's bounding box is measured and scaled to a fixed target size, then the model is shifted vertically so its lowest point lands exactly at PEDESTAL_TOP — the same known Y coordinate the contact shadow and placement ring are already positioned at, so all three line up correctly no matter how the source file was originally authored or scaled.

Without it, dragging far enough would let the camera swing underneath the pedestal, breaking the "object resting on a stand" illusion by revealing an underside view that was never meant to be seen. Capping the polar angle just under 90 degrees keeps every possible orbit angle looking like a believable product shot.

Set up the renderer, scene, GLTFLoader call, and OrbitControls inside a mount effect, storing the model, HOME_POSITION, and controls in refs so the Recenter button's click 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.