webdevpuneet.com

GLB Lighting Studio Viewer — Free Three.js glTF Lighting Preset Snippet

GLB Lighting Studio Viewer · Media · Plain HTML, CSS & JS · Live preview

Copy & paste

Source Code

<div class="gls-wrap">
  <canvas id="glsCanvas"></canvas>
  <div class="gls-hint">Drag to orbit · Ctrl/Cmd + scroll to zoom</div>
  <div class="gls-zoom">
    <span class="gls-zoom-label">+</span>
    <input type="range" id="glsZoom" class="gls-zoom-slider" min="0" max="100" step="1" />
    <span class="gls-zoom-label">&minus;</span>
  </div>
  <div class="gls-presets" id="glsPresets">
    <button class="gls-preset active" data-preset="studio">Studio</button>
    <button class="gls-preset" data-preset="sunset">Sunset</button>
    <button class="gls-preset" data-preset="dramatic">Dramatic</button>
    <button class="gls-preset" data-preset="noir">Noir</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, 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 with real PBR materials.
Four full lighting recipes
Studio, Sunset, Dramatic, and Noir each retune every light and the background.
Lights retuned, never rebuilt
Four persistent THREE.Light objects are simply re-colored per preset, instantly.
Coordinated background wash
Each preset also transitions the scene's backdrop gradient to match the mood.
Auto-fit model scale
A bounding-box measurement scales the model to a known height, never a guess.
Honest load-failure fallback
A logged error swaps in a placeholder mesh that still demonstrates every preset.
Slider + Ctrl/Cmd-scroll zoom
Zoom is an explicit, opt-in gesture, never a hijacked plain scroll wheel.
Fully extensible presets
Add a new named lighting recipe by extending one plain JS object.

About this UI Snippet

GLB Lighting Studio Viewer — Four Full Lighting Recipes on a Real PBR Model

Screenshot of the GLB Lighting Studio Viewer snippet rendered live

A single fixed lighting setup only ever shows one mood. This snippet loads a real, PBR-textured .glb model and gives it four complete lighting *recipes* to switch between — Studio, Sunset, Dramatic, and Noir — each one a coordinated change to every light's color and intensity plus the surrounding background wash, so the same physical geometry and materials can read as a clean product shot, a golden-hour scene, a high-contrast hero shot, or a moody character study.

One set of lights, retuned — not rebuilt

Rather than destroying and recreating light objects per preset (expensive, and a common source of memory leaks in longer-lived scenes), the four THREE.Light instances — ambient, keyLight, fillLight, rimLight — are created exactly once. applyPreset() simply calls .color.set() and reassigns .intensity on each existing light. Every preset switch is just property mutation on the same four objects, which is instant and leaves nothing to garbage-collect.

A recipe, not a slider

Each entry in the PRESETS object is a small, self-contained object holding a [color, intensity] pair for every light plus a CSS background gradient string — real art direction, not one shared brightness knob. Sunset warms every light's hue and pushes the ambient and key warmer; Dramatic drops ambient and fill almost to zero while cranking the key light hard, producing real chiaroscuro; Noir desaturates everything toward cool grays and blues. Because every preset sets *all four* lights explicitly, switching between any two presets in either order always produces a fully correct result — there's no residual state left over from whichever preset was active before.

A real PBR model to catch the difference

The loaded Damaged Helmet sample asset has genuine metalness/roughness texture variation across its surface — a lighting study is only worth doing on a model whose materials actually respond differently to changing key/fill/rim balance, unlike a flat-colored placeholder.

The background changes too, not just the lights

Each preset also sets a CSS radial-gradient on the wrapper element behind the transparent-alpha canvas, with a 1-second CSS transition already declared on that property — so switching from Studio's cool blue-black to Sunset's warm amber backdrop reads as one coordinated scene change, not just a lighting tweak floating in an unrelated background.

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

Add a fifth preset by extending the PRESETS object with a new [color, intensity] recipe, swap MODEL_URL for any other PBR-textured .glb, or pair this with GLB wireframe/solid toggle viewer for a combined material-and-lighting study tool.

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 helmet loads under the neutral Studio lighting preset.
  3. 3
    Click a preset pillAll four lights and the background retune together, live.
  4. 4
    Drag on the canvasOrbit freely under any lighting preset to inspect how it catches the surface.
  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
    Add your own presetExtend the PRESETS object with a new named lighting recipe.

Real-world uses

Common Use Cases

Product photography direction
Preview how a real PBR asset looks under four moods, Studio, Sunset, Dramatic and Noir, before committing to a render.
Game and character lighting
Test how a character reads under different light recipes, retuning the same ambient, key, fill and rim lights instead of rebuilding them.
glTF and PBR teaching
Show how lighting affects physically based materials, with a coordinated backdrop gradient transitioning alongside each preset.
Technical portfolio showpieces
Demonstrate real lighting control rather than a single fixed setup, since every preset swaps colours and intensities in place.
Mesh structure companion
Pair with the GLB wireframe and solid toggle to study both the lighting and the underlying geometry of one model.
Related: Competitive Rank Tier Badge
See the Competitive Rank Tier Badge for a related cards pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

Creating and destroying THREE.Light objects repeatedly is wasteful and, in a longer-lived scene, a common source of memory churn. Because each light's color and intensity are ordinary mutable properties, applyPreset() simply reassigns them on the same four persistent light objects every time a preset button is clicked — instant, and nothing needs to be garbage-collected.

Every entry in the PRESETS object sets an explicit color and intensity for all four lights (ambient, key, fill, rim), never just the ones that differ from the previous preset. That means there's never any residual light setting left over from whichever preset was active before — clicking Noir then Sunset then Dramatic in any sequence always produces exactly the same result as clicking Dramatic directly.

A lighting study is only informative on a surface with real material variation to catch light differently across it. The Damaged Helmet's genuine PBR metalness and roughness texture maps respond visibly differently to each preset's ambient/key/fill/rim balance, unlike a flat single-color placeholder shape, which would look nearly identical under every preset.

Both. Each preset also sets a CSS radial-gradient background on the canvas's wrapper element, with a one-second CSS transition already declared on that property, so the backdrop color shifts in sync with the lighting change — a full scene mood switch, not an isolated lighting tweak against an unrelated background.

Set up the renderer, scene, GLTFLoader call, OrbitControls, and the four persistent light objects inside a mount effect, keeping them and the PRESETS object in refs or module scope so the preset button click handlers can reach current values. Call controls.dispose() and renderer.dispose() in the cleanup function to release the WebGL context and drag listeners on unmount.