# 3D Product Showcase Template

> Single-product website template with a real-time three.js glass object over full-bleed video: the bottle refracts the footage and wordmark, follows the cursor and turns through scroll chapters. Worked as a fragrance house; suits skincare, spirits, candles and any product launch.

Canonical: https://gsapvault.com/templates/3d-product-showcase-template
Live demo: https://gsapvault.com/demos/3d-product-showcase-template/index.html

| Property | Value |
|----------|-------|
| Type | template |
| Tier | paid |
| Price | £10 |
| Difficulty | advanced |
| Plugins | ScrollTrigger |
| Techniques | webgl-shader, video-playback, scrub, scroll-reveal, load-sequence |
| Uses Lenis | Yes |

## Overview

A complete single-product website built around two materials at once: live footage and a real-time 3D object. A glass bottle rendered in three.js hangs in the middle of full-bleed video and behaves like glass, so the moving water and the giant wordmark behind it are refracted, split into faint colour at the edges and bent as it turns. It ships as a finished site for Oriel, a small fictional fragrance house on the Atlantic coast, with one scent told in three notes.

The hero sells as a still: corner navigation, an oversized serif wordmark across the screen and the bottle dead centre over black-sand surf. Move the pointer and a water-drop lens magnifies the footage under it while the bottle leans towards you; press and hold and the atomiser mists and a ripple runs through the water. Scroll and the stage holds while three chapters pass: each one dissolves the footage into the next clip, turns the bottle a full revolution back to its label and moves the glass tint from clear salt to fig green to cedar amber.

Below the stage the page goes quiet on salt-coloured paper: an atelier split with figures, a formula table with ingredient photographs, a full-bleed coast photograph with a pull quote, a collection section with a size picker, stockists and a footer wordmark. Castoro serif carries every heading; Red Hat Text in tracked capitals carries every label.

Making it yours means three video clips, six photographs, the words on the label and in the wordmark, and a short token block. The chapter tints, words and label text are one settings object at the top of the script. Without WebGL, under reduced motion or with no JavaScript the stage becomes four stacked full-height sections with a composed still and the clips, so nothing is lost.

## Features

- Real-time three.js glass bottle with transmission, dispersion and tinted attenuation, refracting the footage and wordmark behind it
- Full-bleed video stage: three muted, looping clips dissolved into each other in one shader as scroll moves through the chapters
- Cursor-driven water-drop lens with a chromatic rim, plus a bottle that tilts towards the pointer on a frame-rate independent chase
- Press-and-hold spray: atomiser mist particles and a ripple through the footage, also on a keyboard-accessible Spray button
- Sticky scroll stage with three note chapters, a full bottle turn between each, per-chapter glass tint and wordmark
- Autonomous lens drift on touch devices so phones get a moving, reduced version rather than a dead canvas
- Designed fallback: composed desktop and portrait stills plus stacked chapter sections for reduced motion, no WebGL, no JavaScript or a blocked CDN
- Atelier split, formula table with ingredient photographs, full-bleed quote, stockists and a footer wordmark on a quiet paper ground
- Collection size picker with animated price and silhouette, plus a front-end bag counter and newsletter form
- Full-screen menu that stops smooth scrolling while open and returns focus on close
- Optional Lenis smooth scrolling on a shared clock with ScrollTrigger
- Bundled re-encoded H.264 clips and baked WebP photographs with a regeneration manifest

## Use Cases

- Perfume, skincare and beauty brands launching a hero product
- Craft gin, spirits and beverage bottles
- Candle and home fragrance studios
- Any single-product launch with a physical object and brand footage

## Questions about this template

### What kinds of products does this 3D showcase suit besides perfume?

Anything sold as one beautiful object with a mood behind it: skincare and serums, craft gin and spirits, candles and home fragrance, olive oil, watches or headphones. Oriel is a fictional fragrance house; replace the three clips, the label words and the copy, and change the glass tints to suit your bottle or jar.

### Can I use my own product model instead of the built bottle?

Yes. The bottle is a group of simple meshes in one function, so you can swap it for a glTF loaded with three.js's GLTFLoader and keep the same glass material, tilt, turn and mist. The README shows where the group is built and which numbers position and scale it for desktop and portrait screens.

### What footage works best behind the glass?

Slow, steady clips with strong texture and contrast: surf, leaf shadows, smoke, fabric, pouring liquid. Glass refracts detail, so a flat or blurry clip makes the bottle look empty. Keep each loop around ten seconds at 1600 pixels wide, muted with no audio track, and encode as H.264 MP4 for a few megabytes per clip.

### Is the Add to bag button and the size picker connected to a shop?

No. The size picker updates the displayed price and silhouette, and Add to bag only increments the counter in the header and prints a status message; nothing is saved and there is no checkout. Point the form at Shopify Buy Button, Stripe Payment Links, Snipcart or your own cart to sell for real.

### Does the newsletter form in the footer collect addresses?

Not yet. It is a static front end that collects one email address and shows a thank-you message without sending anything. Connect it to Mailchimp, Buttondown, ConvertKit, Formspree or your own endpoint by giving the form an action and removing the demo submit handler described in the README.

### How heavy is the page, and how does it behave on phones?

The three clips total about six megabytes and load with the page; photographs are baked WebP under 100KB each. Phones render at a lower pixel ratio without dispersion, replace hover with a slow drift, and place a smaller bottle higher in portrait. Under reduced motion or without WebGL they get the static composed still instead.

## Vibe-Code Ready Setup

This template includes `START-HERE-AI.md`, a product-specific copy-paste setup prompt for Cursor, Claude Code, ChatGPT, GitHub Copilot, Windsurf, and other coding assistants. It tells the assistant to inspect the existing stack, integrate the supplied files, preserve the design, scope selectors, retain accessibility and responsive behaviour, add framework-appropriate GSAP cleanup, and report what it tested.

[How AI-assisted setup works](https://gsapvault.com/vibe-coding)

## How It Works

### Glass that refracts the page

The footage and the wordmark are drawn on a plane behind the bottle rather than in the page, so the glass material can refract them. The bottle itself is built from extruded and turned primitives with a canvas-drawn paper label, so there is no model file to load and every dimension is a number you can change.

### Pointer and press

On a mouse the pointer drives a magnifying lens in the footage and a gentle tilt of the bottle; both chase their targets at the same speed on 60Hz and 120Hz screens. Pressing anywhere on the stage, or using the Spray button, releases a short mist from the neck and sends a ring through the water. Touch devices get a slow autonomous drift instead of hover.

### Scroll chapters

The stage is a tall section with a sticky frame. Its scroll progress chooses the chapter: the copy for that note fades in, the clip dissolves along a noise edge, the wordmark changes to the note's name and the bottle completes a full turn so its label faces front again when the chapter settles. Only the clips being shown are playing.

### Fallbacks that are finished pages

A tiny head script checks for WebGL and reduced motion before first paint. If either fails, or three.js never arrives, the stage renders as four full-height sections: a composed still of the hero and the three chapters over their own footage, with clips held on their poster frames under reduced motion.

## Documentation

How this template works and how it goes into a page. The reference you use once you own the files (worked examples, events, the programmatic API, the class list) ships with the download.

### Quick Start

Open the folder through any static server (videos and ES modules do not load from `file://` in every browser), edit the copy and upload it to any static host. There is no build step.

Two things in `index.html` are required and must stay where they are:

1. **The head script before the stylesheet.** It adds `has-js` (gates the scroll reveals) and, when the device has WebGL 2 and no reduced-motion preference, `gl` (switches the stage from stacked sections to the sticky WebGL frame). Both happen before first paint, so neither version flashes.
2. **The import map and module shim at the end of `<body>`.** They load three.js r180 and its `RoomEnvironment` lighting helper as ES modules, then hand them to `assets/script.js` with an `oriel:three-ready` event. If the module fails, the shim removes `gl` and the fallback shows.

### Using It With Your Own Design

Start with the token block at the top of `assets/style.css`: tide (dark ground), salt and paper (reading grounds), ink, foam and one cedar accent, each with an RGB triple. Then open the `ORIEL_STAGE` settings object at the top of `assets/script.js`:

| Setting | What it does |
|---|---|
| `words` | Four words drawn behind the bottle: the hero wordmark, then one per chapter |
| `tints` | Glass tint for the hero and each chapter, as hex colours |
| `label` | The four lines printed on the bottle's paper label |
| `cap`, `capFinish` | Cap shape (`block` or `pebble`) and finish (`lacquer`, `white`, `brass` or `chrome`); add `?cap=brass` to the URL to preview a finish |
| `ease` | How quickly the scroll turn and pointer tilt catch up (per 60Hz frame) |
| `lensRadius` | Radius of the cursor lens in CSS pixels |
| `dprFine`, `dprCoarse` | Pixel-ratio caps for mouse and touch devices; the first performance lever |

Keep the `data-` hooks on elements whose behaviour you want: `data-stage`, `data-stage-frame`, `data-stage-mount`, `data-stage-block`, `data-stage-video`, `data-stage-index`, `data-spray`, `data-hero-fade`, `data-reveal`, `data-reveal-group`, `data-menu*`, `data-collection`, `data-silhouette`, `data-bag-count`, `data-newsletter`. Every section is guarded; delete any section below the stage and the rest still runs.

### Sections and Configuration

- **Stage.** One hero block and three chapter blocks. Each chapter owns one `<video data-stage-video="n">`, `muted loop playsinline autoplay`, with a poster. With WebGL the clips keep playing underneath the canvas as textures and only the ones in view play; without it they are the backgrounds of four stacked sections. The hero's `.stage__brand` line is visually hidden because the canvas (or the composed still) draws the wordmark; it remains the heading's accessible text. If you replace the still with a picture that has no wordmark, delete that CSS rule.
- **The bottle.** Built in `buildScene()` from an extruded rounded rectangle (body), two cylinders (neck and brass collar), a squashed sphere (stone cap) and a plane with a canvas label. `BODY_W`, `BODY_H`, `BODY_D`, `BEVEL` and `RADIUS` set its proportions. To use your own product, load a glTF with three.js's `GLTFLoader`, add it to the `bottle` group and reuse the `glass` material on its glass parts.
- **Pointer and spray.** Hover drives the lens and tilt on mouse devices; touch devices get a slow drift. Pressing on the stage (not on a link or button) mists the atomiser and sends a ripple through the footage; the **Spray** button does the same for keyboard users.
- **Chapter index.** The `01 / 02 / 03` links jump to the settled position of each chapter, through Lenis when it is running. It is hidden on the hero and on narrow phones, where each chapter's kicker carries its number.
- **Atelier, Formula, Wearing, Stockists.** Plain content. Formula rows are a list; add or remove rows freely.
- **Collection.** Native radios carry `data-price` and `data-scale`. Changing size animates the price and the CSS bottle silhouette. **Add to bag only increments the header counter and prints a message: there is no cart or checkout.** Connect a real one (Shopify Buy Button, Stripe Payment Links, Snipcart) by replacing the submit handler in `initCollection()`.
- **Newsletter.** A static form that collects one email and shows a thank-you message without sending anything. Give it an `action` for Mailchimp, Buttondown, ConvertKit or Formspree and delete `initNewsletter()`.
- **Menu.** A full-screen overlay; it stops Lenis while open (hence `data-lenis-prevent`), closes on Escape and returns focus to the Menu button.

### Footage and Photographs

Clips are 1920x1080, 24fps, six seconds, with no audio track, cross-faded end to start so they loop without a jump. Fig and cedar ship as a VP9 WebM, listed first because it is a fraction of the size, with an H.264 MP4 fallback; salt ships as MP4 only, because on dense surf H.264 came out both smaller and sharper. Only the hero clip autoplays: the chapter clips are `preload="none"` and start a chapter before they are needed, so the page does not fetch footage nobody has scrolled to. When you swap a clip, keep that pattern, or drop the WebM `<source>` and keep the MP4. Slow, textured footage refracts best; flat or blurred clips make the glass look empty. Photographs are baked WebP derivatives listed in `assets/img-manifest.json`; regenerate with `bun scripts/build-template-assets.ts 3d-product-showcase-template`, or simply replace the files in `assets/img/` at the same sizes.

`hero-still.webp` and `hero-still-portrait.webp` are frames rendered from the WebGL stage itself; after changing the bottle or clips, screenshot your own stage with the copy hidden and replace them.

| File | Source (Pexels licence) |
|---|---|
| `assets/video/salt.mp4` | https://www.pexels.com/video/aerial-view-of-waves-crashing-on-black-sand-beach-39296252/ |
| `assets/video/fig.webm`, `.mp4` | https://www.pexels.com/video/shadowy-silhouettes-of-leafy-branches-at-dusk-34671612/ |
| `assets/video/cedar.webm`, `.mp4` | https://www.pexels.com/video/a-smoke-in-a-dark-room-4320605/ |
| `assets/img/atelier.webp` | https://www.pexels.com/photo/8450352/ |
| `assets/img/material-salt.webp` | https://www.pexels.com/photo/3693292/ |
| `assets/img/material-fig.webp` | https://www.pexels.com/photo/34041533/ |
| `assets/img/material-cedar.webp` | https://www.pexels.com/photo/7906948/ |
| `assets/img/wearing.webp` | https://www.pexels.com/photo/person-on-cliff-during-overcast-4641017/ |

### Palette

One palette. Measured contrast pairs are recorded in the `:root` comment of `assets/style.css`. Type over footage sits in the lower third, where the stage scrim is strongest; keep new stage copy there. `--cedar` is never body text on salt; use `--cedar-ink`.

### Accessibility

Content is real HTML throughout: the stage copy, chapter headings and index are in the document whether or not the canvas exists, and the canvas is `aria-hidden`. All actions are native links and buttons with visible focus, a skip link leads to the content, and the menu manages focus. The pointer lens and tilt are decorative; the spray has a keyboard button. Under reduced motion the WebGL stage never starts, the clips are held on their poster frames and every reveal is visible. With JavaScript off or the CDN blocked, the stacked stage and the full page render without gates.

### Dependencies

GSAP 3.15.0 and ScrollTrigger (compatibility floor GSAP 3.12+), Lenis 1.3.17 (optional), and three.js r180 through the import map. Castoro (regular and italic) and Red Hat Text (400, 500) load from Google Fonts with `display=swap`. GSAP owns the matchMedia branches, teardown, stage scroll progress, the hero intro, chapter copy transitions, reveals and the collection picker; the three.js loop on `gsap.ticker` owns every continuous stage value (pointer chase, bottle turn, footage dissolve, mist). If your site already imports three.js, reuse that module in the shim rather than loading a second copy.

### Teardown

Call `window.gsapContext.revert()` before removing the page in a client-routed app. It stops the frame loop, disconnects the observers, disposes every geometry, material and texture, releases the WebGL context, removes the canvas and all owned listeners, destroys the Lenis instance it created and leaves unrelated animations alone.

### Files

`index.html`, `assets/style.css`, `assets/script.js`, `assets/script.min.js`, `assets/video/` and `assets/img/` make up the deployable site. `assets/img-src/` holds poster and still masters for the manifest. `thumbnail.html` and `config.json` are catalogue build files.

### Browser Support and Performance

The WebGL stage needs WebGL 2 and import maps (current Chrome, Edge, Firefox and Safari). Everything else falls back to the stacked stage. The glass uses three.js transmission, which renders the backdrop a second time each frame; the stage only renders while it is on screen. Lower `dprFine` first, then set `dispersion` to `0` in the glass material. The three clips total about six megabytes.

## What You Get

- `index.html`: working demo page
- `assets/script.js`: commented, readable source
- `assets/style.css`: effect styles
- `README.md`: full documentation with examples and framework integration notes
- `START-HERE-AI.md`: product-specific copy-paste prompt for AI-assisted setup
- `LICENSE.txt`: standard license terms
- Lifetime updates: re-download anytime from your library

## Get the Code

This is a premium template. The standard license costs £10 one-time and covers unlimited personal and commercial projects with no attribution required for our code. Bundled third-party assets retain their own licences and attribution requirements. The only restrictions: no redistribution of the code itself and no competing effect libraries.

- [Buy 3D Product Showcase Template](https://gsapvault.com/templates/3d-product-showcase-template)
- [The Vault (£99 one-time, best value): every collection in the Vault library, plus future items added to those collections](https://gsapvault.com/effects)

---

From [GSAP Vault](https://gsapvault.com): production-ready GSAP animation effects. Full catalog for agents: https://gsapvault.com/llms-full.txt
