Three.js Product Viewer — Draggable 3D Showcase With Color Swatches

Three.js Product Viewer · Animations · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Three-point studio lighting: key, fill, and rim lights recreate real photography lighting in WebGL
Live color swatches: material.color.set() recolors the product instantly with no mesh rebuild
Toggleable auto-rotate: a single button flips OrbitControls' own autoRotate property
Grounding disc: a soft, matte circle beneath the product avoids an empty-void look
Damped drag-to-inspect: OrbitControls with damping lets visitors freely rotate the view by hand
Bounded zoom range: minDistance and maxDistance keep the camera at a sensible viewing distance
Metallic MeshStandardMaterial: metalness and roughness controls for glossy or matte product looks
Loaded entirely from a CDN: no npm install, bundler, or build step required

About this UI Snippet

How to Build a Three.js Product Viewer With Studio Lighting and Color Swatches

Screenshot of the Three.js Product Viewer snippet rendered live

The Three.js Product Viewer snippet renders a metallic 3D object under proper studio lighting, lets visitors drag to inspect it from any angle, tap color swatches to recolor it live, and toggle auto-rotation on or off — the same interaction pattern used by real e-commerce 3D product configurators, built with core Three.js and its OrbitControls addon loaded from a CDN.

Three-point lighting, not a single flat light

A single light source on any metallic object produces harsh, unconvincing shading — either a blown-out highlight or a mostly-black silhouette. This snippet uses the classic photography three-point setup translated into WebGL lights: a bright DirectionalLight key light establishes the main highlight and shadow direction, a dim, cool-tinted fill light on the opposite side softens the shadow so it doesn't go pure black, and a rim PointLight positioned behind the object catches its silhouette edge, separating it visually from the dark background. This is the actual reason the product reads as a real, dimensional object rather than a flat cutout, and it's the single biggest lighting lesson in this snippet.

A grounding disc, not an empty void

A large, dark, matte CircleGeometry disc sits beneath the product, angled flat and positioned just below it. It never needs its own texture or reflection — its only job is to catch a hint of ambient light and give the product something to visually "sit on," which reads as far more grounded than an object floating in an obviously empty black scene.

Live material recoloring via swatch buttons

Each color swatch button is wired to a single line: material.color.set(hexValue). Because the MeshStandardMaterial's color is a live, mutable THREE.Color object, changing it doesn't require rebuilding the mesh, recompiling any shader, or reloading anything — the very next rendered frame simply reflects the new color under the exact same three-point lighting setup.

OrbitControls' autoRotate as a toggleable property, not a separate system

Rather than writing a custom rotation increment in the animation loop, the snippet leans on OrbitControls' own built-in autoRotate boolean and autoRotateSpeed property — both of which the same controls.update() call already handles every frame. The toggle button does nothing more than flip that one boolean, which means auto-rotation and manual dragging never fight each other: grabbing the canvas and dragging always instantly overrides the auto-rotation, and letting go resumes it smoothly thanks to the existing damping.

A TorusKnotGeometry stands in for "the product"

The demo object is a TorusKnotGeometry — a continuously twisting tube — chosen specifically because its complex, non-symmetrical curvature shows off lighting, metalness, and rotation far more clearly than a plain sphere or cube would. In a real product page you'd swap this for an imported GLTF model; every lighting, control, and recoloring technique here applies identically regardless of what geometry sits in that spot.

Where this pattern is used in production

This exact combination — three-point lighting, a grounding plane, OrbitControls with damping and toggleable auto-rotate, and live material property changes — is the backbone of nearly every real 3D product configurator on the web, from sneaker customizers to furniture previews. Pair it conceptually with a color wheel picker for a full 2D-plus-3D product customization flow, or contrast its studio presentation against the more playful, ambient look of the morphing blob snippet.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You do not have to guess at proper 3D lighting setup through trial and error. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly what role each of the key, fill, and rim lights plays and why removing any one of them would make the product look flatter or harsher, or how OrbitControls' autoRotate property interacts with manual dragging without any extra conflict-resolution code. The same assistant can help optimize it, for instance checking whether the ground disc's shadow could be replaced with a real baked shadow map for more realism, or whether the lighting setup could be tuned differently for a matte versus a glossy product. It is also useful for extending the viewer: ask it to load a real GLTF model instead of the torus knot placeholder, add a price tag or "Add to cart" button anchored to the product's screen position, or support swapping between multiple product variants instead of just recoloring one. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a draggable "3D product viewer" in plain HTML, CSS, and JavaScript using Three.js and its OrbitControls addon, both loaded from a CDN (no bundler, no build step).

Requirements:
- A full-viewport canvas with a WebGLRenderer sized to match it, updated on window resize including camera aspect ratio, plus OrbitControls with damping enabled, a bounded min/max zoom distance, and a toggleable built-in auto-rotate feature.
- Light the scene with a proper three-point setup: a bright directional key light, a dimmer, differently-tinted directional fill light positioned on the opposite side to soften shadows without eliminating them, and a point or directional rim light positioned behind the product to separate its silhouette from the background, plus a small amount of ambient light.
- Add a large, flat, dark, non-glossy disc beneath the product (angled to lie flat, like a floor) purely to ground the object visually — it does not need any texture, reflection, or shadow map.
- Render one product mesh with a metallic MeshStandardMaterial (a TorusKnotGeometry or similar geometry with complex curvature works well to show off lighting and shape).
- Add a row of at least four color swatch buttons; clicking a swatch must change the product material's color property directly and instantly, with no geometry or shader changes.
- Add a separate button that toggles the OrbitControls auto-rotate feature on and off, and update the button's label or appearance to reflect the current state.
- Ensure manual dragging on the canvas always works immediately regardless of the auto-rotate toggle's state, using OrbitControls' own built-in behavior rather than custom conflict-resolution logic.

Step by step

How to Use

  1. 1
    Load both CDN scriptsAdd three.min.js and OrbitControls.js from the CDN panel, in that order.
  2. 2
    Paste HTML, CSS, and JSThe product appears immediately under studio lighting, auto-rotating on a grounding disc.
  3. 3
    Drag to inspectClick and drag to rotate manually; release and it resumes auto-rotating after a moment.
  4. 4
    Tap a color swatchClick any of the four color dots to recolor the product material instantly.
  5. 5
    Toggle auto-rotateClick the pill button to turn automatic rotation on or off entirely.
  6. 6
    Swap in your own geometryReplace TorusKnotGeometry with any other Three.js geometry or an imported model to showcase a different product.

Real-world uses

Common Use Cases

E-commerce product pages
The exact interaction pattern used by real 3D product configurators — drag to inspect, tap to recolor.
Furniture and interior design previews
Swap the torus knot for an imported model to preview furniture, decor, or fixtures in customizable colors.
Portfolio and agency showpieces
Demonstrates studio-quality 3D lighting and live material control, a strong technical portfolio piece.
Teaching three-point lighting in WebGL
A focused, minimal demonstration of key/fill/rim lighting translated from photography into Three.js.
Character or item customization screens
Reuse the swatch-to-material pattern for skin, armor, or item color customization in a game UI.
Print-on-demand and merch previews
Preview a product in multiple colorways before committing to a purchase or print run.

Got questions?

Frequently Asked Questions

A single light on a metallic surface produces either a harsh blown-out highlight or a nearly black silhouette on the shadowed side. The key light establishes the main highlight, the dim fill light on the opposite side softens that shadow without eliminating it, and the rim light behind the object catches its edge to separate it from the dark background — the standard three-point lighting setup borrowed directly from photography and film.

Each swatch button calls material.color.set() with that swatch's hex value. Because a MeshStandardMaterial's color property is a live, mutable THREE.Color object, changing it takes effect on the very next rendered frame — no mesh rebuild, shader recompile, or geometry change is required.

The toggle button only flips OrbitControls' own built-in autoRotate boolean, which the existing controls.update() call already respects every frame. Because OrbitControls treats a manual drag as taking priority automatically, grabbing and rotating the canvas by hand always works instantly regardless of the auto-rotate state, and releasing it resumes auto-rotation smoothly.

The dark, matte CircleGeometry disc beneath the product exists purely to give it something to visually rest on. Without it, the object appears to float in an obviously empty black void; with it, the scene reads as a real studio or stage, even though the disc itself is a completely flat, untextured surface.

Yes. Any Three.js geometry, or a model imported via a loader such as GLTFLoader, can replace the TorusKnotGeometry mesh directly — every lighting, control, and material-recoloring technique in this snippet applies identically regardless of what geometry occupies that position in the scene.

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. Pass the available colors and geometry as props, set up the renderer and controls inside a mount effect, and call controls.dispose() plus renderer.dispose() on cleanup so the WebGL context and drag listeners are released when the component unmounts.