PlaybookEdition 1 · October 2026three.js r186 · Blender 5.2

3D Landing Playbook

How the best 3D landing pages on the web are actually built (the scroll flow, the realism, the stack and the tools) and a step-by-step plan to go from an empty folder to a live, realistic 3D site. Every technique here is running in the reference build.

Hero model
203 KB GLB · 65 KB sent
3D code
~180 KB gzip, lazy
3D ready
0.3 s (M4, Chrome)
Texture files
0
Fallbacks
poster · reduced motion
01

The short answer

Almost every award-winning 3D landing page is the same three things done carefully: a scroll story with one idea per section, an asset that is lit and shaded like a product photo, and a boring, reliable stack that keeps it at 60 fps on a phone.

Flow

One pose per section

Preloader, hero thesis, a posed scroll story, one pinned "explain" moment, proof, call to action. Scroll scrubs the 3D between poses; the render loop eases toward them.

Realism

Light, not polygons

  • Real-scale, bevelled geometry
  • PBR materials with measured values
  • Image-based lighting (HDRI or softboxes)
  • Correct colour space + AgX/Neutral tone mapping
  • Grounding: baked AO or contact shadows
  • Light post: bloom on HDR only, grain, vignette
  • Damped, physically plausible motion
Stack

The usual suspects

Blender → glTF 2.0 → gltf-transform (Meshopt/Draco, KTX2) → three.js or React Three Fiber → GSAP ScrollTrigger + Lenis → pmndrs postprocessing → Vite / Next.js / Nuxt / Astro → Netlify or Vercel.

Blender→.glb→gltf-transform→three.js scene→postprocessing→scroll poses→Netlify

Budgets that keep you honest: hero GLB under 1–3 MB (the reference model is 0.2 MB), under ~100 draw calls, textures at most 2K and KTX2-compressed, device pixel ratio capped at 1.5–2, and the page's Largest Contentful Paint is the headline text, never the canvas.

02

Reference sites and how they are built

28 live sites, 22 of them service or business sites, checked on 9 October 2026. The three.js revision was read from each live page (window.__THREE__), libraries from the shipped bundles, hosting from response headers, and awards from Awwwards' own pages. Stack items are what was found, not guessed; anything unconfirmed says so.

What the winners actually use

Renderer
three.js on 25 of 28
Shell
Nuxt 7 · Astro 6 · Next 4 · Svelte 3
React Three Fiber
2 of 28
GSAP · Lenis
19 · ~13 of 28
WebGPU live
2 of 28
  • Plain three.js inside a content framework is the norm; React Three Fiber appears on Shopify Editions and basement only. Lusion even ships a patched three.js build; Active Theory runs its own engine; Kriss.ai uses the tiny OGL.
  • Realism is prepared offline. Baked lightmaps (basement ships ~20 KTX2 lightmaps, IVRESS a packed EXR bake), studio HDRIs (Lando Norris and Shopify use Poly Haven's studio_small_08/09), camera paths exported from Blender or Cinema 4D (Oryzo, IVRESS, Cerebrium), and Houdini or VDB data (Hubtown, Igloo).
  • Draco or meshopt + KTX2 is near-universal; top studios add custom binary formats.
  • WebGPU ships with a WebGL path. Cerebrium built in WebGPU + TSL, then shipped WebGL because first-load shader compilation took about 20 seconds.
  • SaaS sites keep copy in the DOM and confine WebGL to the hero and section intros (Stripe, GitHub, Cerebrium, Floema), which keeps SEO and readability intact.

The sites

SiteStack (verified)How the 3D worksAward
Igloo Inc
Fintech / web3 · abeto
three.js r165 · Svelte + Vite · GSAP · pmndrs postprocessing · Draco + KTX2Portfolio projects frozen in procedurally grown ice blocks; VDB volumes drive GPGPU particle morphs; the UI is rendered in WebGL.Site of the Year 2024
Lando Norris
Personal brand · OFF+BRAND
three.js r174 · Webflow · GSAP · Lenis · Rive2.5D portrait from depth/alpha/shadow maps; full PBR helmet with liveries, lit by a Poly Haven studio HDRI; Rive page transitions.Site of the Year 2025
Messenger
Web game · abeto
three.js r180 · Svelte 5 + Vite · WebSocketsMultiplayer tiny-planet courier game; biome ambience changes as you walk.Site of the Year 2025
Lusion v3
Agency
three.js r158 · Astro · custom virtual scroll · NetlifyCursor-reactive hero → reel → "tunnel" chapter with an animated astronaut → work. Custom binary geometry, EXR matcap, glass shards, LD/HD asset tiers.Site of the Year 2023
Noomo Agency
Agency
three.js r156 · Nuxt · GSAP ScrollSmoother · Prismic · Vercel~25 Draco GLB props, one per client industry; glass cards on scroll; 3D page transitions.Site of the Year 2023
Oryzo AI
Product launch (parody) · Lusion
three.js r178 (patched) · Astro · Rive · GSAPHero coaster on a camera path baked in the 3D app; 3D → 2D → 3D transition; Rive for crisp UI motion.Site of the Month Apr 2026
Shopify Editions Winter '26
SaaS launch · Shopify Design
three.js r181 + React Three Fiber · React Router · Lenis · RiveOne painting-like 3D scene per product category; prefiltered PMREM studio environment in KTX2; only the active and neighbouring scenes stay mounted.Site of the Month Feb 2026
Bruno Simon
Portfolio · source, MIT
three.js r183 TSL (WebGPU or WebGL) · Vite · Rapier · HowlerNo scroll: a drivable island; Blender names become physics bodies; 78k grass blades; weather and seasons.Site of the Month Jan 2026
Cerebrium
SaaS (AI infrastructure)
three.js r183 WebGL · Astro 6 · GSAP · Lenis · DatoCMSThe camera travels one canvas through several environments; Poly Haven HDRI, Cinema 4D camera baked, GPGPU particles, bloom, interactive force-field globe.Site of the Day Sep 2026
Kriss.ai
SaaS (AI health)
OGL (not three.js) · SvelteKitPercentage loader, then one scene explored by zoom transitions and feature hotspots. A tiny engine keeps the payload small.Site of the Month Oct 2024
Why Zero
Startup (edtech)
three.js r160 · Vite · GSAPHold-to-proceed loader; five-stage narrative scroll with per-stage ambient sound; waitlist form that generates an origami.Site of the Month Sep 2026
Zentry
Platform (AI / web3) · Resn
three.js r163 · Nuxt · GSAP · Lenis · RiveVideo-led with WebGL accents; one chapter per product layer. Proof that "mostly video" can win.Site of the Month Aug 2024
Stripe
Fintech SaaS
three.js r178 · Next.jsShader wave behind the hero headline; 3D as an accent while the copy stays DOM.none current
GitHub
SaaS
three.js r148 · GSAPMascot GLBs rise behind the product demo; a small WebGL canvas per section intro. Globe making-of.Honorable Mention 2021 (older globe)
Spline
SaaS (3D tool)
Spline runtime (embeds three.js r149) · Next.js · VercelLive embedded Spline scenes plus video tiles: the no-code route to an interactive hero.unverified
Hatom
Fintech (DeFi) · Immersive Garden
three.js r165 · Nuxt · GSAP · Lenis · Draco + BasisGated "5 phases" story on an egg metaphor, click-and-hold transitions, glass transmission, spatial sound.Site of the Day Nov 2024
Montfort
Commodities / investment · Immersive Garden
three.js r169 · Astro · GSAP ScrollSmoother · LenisCalm 3D mountain landscape; one 3D chapter per division; EXR environment map. How a conservative B2B brand uses 3D.Site of the Month Jun 2025
Hubtown
Real estate · Unseen Studio
three.js r179 · Nuxt · Theatre.js · GSAP · Lenis · NetlifyFour terrain-and-water scenes from Houdini height maps, point clouds, map navigation, zoom into project pages.Site of the Day Jun 2026
Explore Primland
Resort · Outpost
three.js r179 · Nuxt · Theatre.js · GSAP · Lenis · NetlifyReal-elevation terrain flyover, birds on spline paths, instanced trees, a seasons toggle that swaps textures.Site of the Day Feb 2026
Floema
B2B furniture · Bürocratik
three.js r181 · Nuxt · GSAP · Lenis · Rive (WebGPU code in bundle)Several small WebGL canvases inside the DOM rather than one fullscreen canvas; a physical-material object for the sustainability story.Site of the Month May 2026
Immersive Garden
Agency
three.js r151 · Nuxt · GSAP + ScrollTrigger + SplitText · Lenis · VercelHigh-poly bas-relief hero with KTX2 normal maps at two quality tiers; a tiny WebGL viewport per project in the list.Site of the Month Jan 2025
basement.studio
Agency
three.js r180 + React Three Fiber · Next.js · Sanity · VercelA full 3D model of the office behind the DOM sections, with ~20 baked lightmaps shipped as KTX2.Site of the Day Apr 2025
Unseen Studio
Agency
three.js r143 · WordPress · GSAP · ASScroll3D diorama hero with a pearl matcap and baked AO in KTX2: rich look, no real-time lights.Site of the Day Feb 2023
Active Theory v6
Agency
Own "Hydra" WebGL engine · Draco + Basis · worker poolThe whole site is one canvas: SDF text, bloom, particles, and what look like DOM mirrors for SEO and accessibility.Site of the Day Feb 2024
Alche
Agency (Japan)
three.js r177 · Astro + swup · Lenis · GSAPSound opt-in → one scene GLB hero → service videos → works.Honorable Mention Oct 2025
Trionn
Agency (India)
three.js r128 · Next.js · GSAP + Lenis on one ticker · Web Audio3D brand symbol with hover and hold-to-explode, all driven by one value; DOM headline over the canvas with mix-blend-mode: difference.self-reported
IVRESS "Spin a Tale"
Brand story · Utsubo
three.js r182 on WebGPU (WebGL fallback) · Astro · Lenis · GSAPSix scenes, each with its own camera GLB scrubbed by scroll; lighting baked into packed EXR.FWA case page; level unverified
Apple AirPods Pro 3
Hardware product page
No WebGL: scroll-scrubbed pre-rendered videoPinned sections play or scrub video at scroll keyframes. Offline-render quality at zero GPU cost, no interactivity.reference only

The flow they share

Across the business sites the same sequence keeps appearing, and it is the sequence chapter 03 turns into code:

  1. A gated or percentage preloader, often with a sound opt-in (Hatom, Alche, IVRESS, Why Zero).
  2. A 3D hero that is the brand metaphor: an igloo, a coaster, a helmet, a mountain, a shield.
  3. One scroll chapter per offering (Montfort's divisions, Shopify's categories, Hatom's phases).
  4. Proof: a project grid, often with small WebGL thumbnails or in-world objects.
  5. One interactive "toy" moment: click-and-hold, a hover gag, a seasons toggle, a force field.
  6. A particle or interactive footer, then the call to action.

Making-of case studies

Igloo Inc · Awwwards

Built in the browser with live-reload tools for shaders and models, tested on low-end devices throughout; a custom VDB-to-browser volume format drives the particles.

Shopify Spring '26 · Codrops

Scroll drives shader uniforms through refs, not React renders; GPU tiers 0–3 with DPR clamping; only nearby scenes stay mounted.

Cerebrium · Codrops

WebGPU dropped for WebGL after ~20 s shader compiles; Cinema 4D lights exported as helper cubes; "moving" lines are static meshes with a scrolling UV mask.

Trionn · Codrops

One clock (gsap.ticker) drives Lenis and ScrollTrigger; plain three.js over R3F for control of the render loop; reduced motion slows rather than stops.

Shader.se · Codrops

Next.js + R3F + TSL: pages outside the scroll window skip rendering; scenes render into FBOs so each can sample the next for transitions.

Bruno Simon · Awwwards

Blender naming conventions become physics bodies and code handles; one geometry of 78k grass triangles; TSL targets WebGPU and WebGL.

GitHub globe · GitHub blog

No textures; ~12k instanced dots; four quality tiers when FPS drops below ~55; an SVG placeholder crossfades to the canvas.

Checked but left out: Prometheus Fuels and Opal Tadpole (award pages no longer live), Linear, Vercel, Raycast, Ramp, Mercury, Exo Ape and Dogstudio (no WebGL on the current sites), and Cartier, Locomotive, Coinbase and Kraken (blocked automated checks).

03

Anatomy of the flow

Award sites feel cinematic because the scroll is storyboarded like a film: each section owns one idea and one camera move, and the 3D object is the actor that carries you between them.

PreloaderReal progress from the loader, then a curtain reveal. Never block longer than the asset needs.
Hero thesisHeadline left, object right, idle motion and pointer parallax. The headline is the LCP.
Posed storyEach section scrubs the object to a new pose: position, turn, scale, glow.
Pinned explainPin one section and take the product apart (exploded view) with labels on the parts.
ProofWork, numbers, logos. The object recedes so the content leads.
Specs / stackDense information on panels; the object returns at the side.
Call to actionThe object centres and glows. One primary button.

How the scroll drives the 3D

The pattern below is what the reference build uses, and it is the pattern behind most GSAP-driven 3D sites. The DOM never touches three.js directly: sections write numbers into one plain object, and the render loop eases the scene toward those numbers every frame.

section[data-pose]→ScrollTrigger scrub→target {x, rotY, explode…}→damp() each frame→rig + gimbal joints→composer.render()

src/choreography.js

const DESKTOP = {
  hero:     { x: 0.47,  y: -0.05, scale: 1,    rotY: -0.4,  spin: 1,    explode: 0, glow: 1 },
  services: { x: -0.55, y: 0,     scale: 0.92, rotY: 0.55,  spin: 0.55, explode: 0, glow: 0.8 },
  process:  { x: 0.16,  y: -0.02, scale: 0.74, rotY: -1.22, spin: 0,    explode: 0, glow: 1 },
  /* … work, stack, contact */
};

sections.forEach((section, i) => {
  if (i > 0) gsap.fromTo(target, { ...exitOf(previousPose) }, {
    ...poses[section.dataset.pose], ease: 'none', immediateRender: false,
    scrollTrigger: { trigger: section, start: 'top bottom', end: 'top 25%', scrub: true },
  });
  if (section.dataset.pose === 'process') pinProcess(target);   // keep page order!
});

src/scene/stage.js (inside the frame loop)

// Frame-rate independent easing: smooth even on a jerky trackpad.
for (const key in target) now[key] = MathUtils.damp(now[key], target[key], 4.2, dt);

// Pose x is a fraction of the visible width, so layouts hold at any aspect ratio.
const halfWidth = Math.tan(MathUtils.degToRad(camera.fov / 2)) * (now.camZ - now.z) * camera.aspect;
rig.position.set(now.x * halfWidth, now.y, now.z);

Rules that prevent the classic bugs

  • Use fromTo with immediateRender: false for scrubbed pose tweens, so reloading halfway down the page lands in the right pose.
  • Create ScrollTriggers in page order. A pinned section adds height; triggers created before the pin but placed after it are measured without that height and fire early. (This bug showed up in the reference build and is fixed in choreography.js.)
  • Drive Lenis from GSAP's ticker (gsap.ticker.add(t => lenis.raf(t * 1000)), lagSmoothing(0)) so the DOM and the WebGL frame update together.
  • Scale velocity, not angle, when slowing an idle spin, and unwind to the nearest whole turn before an exploded view. Otherwise parts snap or spin backwards.
  • Labels on 3D parts are plain HTML moved each frame by projecting a 3D anchor with vector.project(camera). They stay crisp, selectable and accessible.
  • Phones get their own poses via gsap.matchMedia(): centred, smaller, a vertical exploded view, and the canvas dims behind text-heavy sections.
04

How the realism is built

"Looks real" is the sum of seven layers. Skip one and the eye notices. Each layer below lists what it is, the values that work, and where it lives in the reference build.

1 · Geometry

Real scale, bevelled edges

Model at real-world scale (1 unit = 1 m) and apply transforms before export. Bevel every hard edge: razor edges never catch a highlight, and highlights are what the eye reads as "real". Small functional details (pins, seams, tick marks) give reflections something to break on.

  • Bevel width ~0.5–2% of the object size, 2–3 segments, harden normals (or a Weighted Normal modifier).
  • Enough segments that silhouettes stay round at full screen: the reference rings use 192.
  • Budget: Khronos 3D Commerce guidance is ≤100k triangles and ideally <5 MB per asset.

In the build: bevel() and the tick marks in tools/blender/build_orrery.py.

2 · Materials

PBR with measured values

Use metal/rough PBR (MeshStandardMaterial, glTF core) for almost everything. Metals are metalness 1 with a coloured base (their reflectance), dielectrics are metalness 0. Reach for MeshPhysicalMaterial only for what needs it: glass (transmission, ior, thickness, dispersion), lacquer (clearcoat), fabric (sheen), brushed metal (anisotropy), soap-film (iridescence).

MaterialBase colour (linear)MetalRoughnessExtras
Chrome0.95, 0.94, 0.9210.07envMapIntensity 0.95
Brass0.93, 0.74, 0.4210.24
Titanium0.56, 0.54, 0.5110.32
Glazed ceramic0.86, 0.84, 0.8000.38clearcoat 0.6
Glass1, 1, 100.04transmission 1, ior 1.5, thickness 0.9, dispersion 0.4

Transmission renders the opaque scene a second time; lower renderer.transmissionResolutionScale on phones. In React Three Fiber, drei's MeshTransmissionMaterial adds blur and chromatic aberration at the cost of one more render per material.

In the build: Principled BSDF values in the Blender script; glass is upgraded in src/scene/orrery.js.

3 · Lighting

Image-based, like a studio

Reflections come from the environment map, not from lights. Use an HDRI (HDRLoader, renamed from RGBELoader in r180; or UltraHDRLoader for gain-map JPEGs, the smallest format), RoomEnvironment, or your own softboxes rendered into a PMREM. A 1K equirect is enough for lighting; ship bigger only if the HDRI is visible as the background.

  • Product look: one big soft key, two long strip rims, a top bounce, a warm low fill, in a dark dome with a faint horizon so mirrors never reflect pure black.
  • Tune scene.environmentIntensity first at exposure 1, then per-material envMapIntensity.
  • Free CC0 HDRIs: Poly Haven. In drei, <Environment> + <Lightformer> builds the same softbox studio.

In the build: src/scene/studio.js (zero download; the softboxes mirror the Blender poster lights).

4 · Colour

Linear workflow + tone mapping

three.js has worked in linear space since r152. Tag colour textures (map, emissiveMap) as SRGBColorSpace; leave data textures (normal, roughness, AO, ORM) untagged. Then pick a tone mapper deliberately:

  • Neutral (Khronos PBR Neutral): brand and product colours stay exact, no hue shift. The three.js editor default since r183.
  • AgX: cinematic HDR, bright emissives and bloom; Blender's default view since 4.0, so it matches look-dev.
  • ACES Filmic: punchier and darker; React Three Fiber's default.

With pmndrs postprocessing, keep renderer.toneMapping = NoToneMapping, use HalfFloat buffers and end the chain with a ToneMappingEffect. In React Three Fiber, <EffectComposer> silently switches tone mapping off: always add <ToneMapping />.

In the build: AgX inside the effect pass, src/scene/stage.js.

5 · Grounding

Shadows and ambient occlusion

Objects look pasted-on until they touch something. From cheapest to most expensive:

  • Baked: Cycles AO or lightmap on a second UV set (exports as TEXCOORD_1), or a soft blob-shadow texture on a plane. Keep specular real-time.
  • Accumulated: drei AccumulativeShadows costs nothing once accumulated.
  • Contact shadows: a depth render from below, blurred. Use frames={1} for static scenes.
  • One shadow-casting light, tight shadow camera, normalBias ≈ 0.02. Note r186 removed PCFSoftShadowMap; in R3F use shadows="percentage".
  • Screen-space AO (N8AO, halfRes): hardware MSAA does not work with it, so switch to SMAA.

In the build: the object floats in a dark studio, so rim light and vignette do the grounding. A product sitting on a surface needs one of the options above.

6 · Post

The lens, not a filter

Order matters: depth effects (AO, depth of field) → Bloom on HDR values → tone mapping → vignette and grain → anti-aliasing. Bloom only what is genuinely brighter than white: threshold 1, emissive × intensity > 1.

new EffectComposer(renderer, { frameBufferType: HalfFloatType, multisampling: 4 });
composer.addPass(new RenderPass(scene, camera));
composer.addPass(new EffectPass(camera,
  new BloomEffect({ mipmapBlur: true, luminanceThreshold: 1, intensity: 0.7, radius: 0.72 }),
  new ToneMappingEffect({ mode: ToneMappingMode.AGX }),
  new VignetteEffect({ offset: 0.32, darkness: 0.62 }),
  new NoiseEffect({ premultiply: true, blendFunction: BlendFunction.SCREEN, opacity: 0.045 }),
));

Depth of field in postprocessing 6.39 uses world units (focusDistance 3, focusRange 2); the React docs still show the old API. On phones, drop AO and depth of field first.

7 · Motion

Damped and physically plausible

Real objects have mass. Ease every value toward its target with frame-rate independent damping (MathUtils.damp, or maath/easing in R3F), keep pointer parallax small (≈0.1 rad), and make mechanical parts move the way they would in reality: each gimbal ring in the reference build turns only about its own pins.

  • Product-lens camera: field of view 30–35°, so shapes are not distorted.
  • Slow a spin by scaling its velocity, never its angle; unwind to the nearest whole turn before assembling an exploded view.
  • Compile shaders before the reveal (renderer.compileAsync) so the first scroll never hitches.

In the build: JOINTS and update() in src/scene/orrery.js.

05

Choose your stack

Pick the path that matches your team, not the one with the flashiest demo. All five ship award-level work; they differ in control, cost and who maintains the 3D. Of the 28 reference sites, most run plain three.js inside Nuxt, Astro or Svelte (paths A and C); two use React Three Fiber.

PathStackChoose it whenWatch out for
A · Vanilla
this repo
Vite + three.js + pmndrs postprocessing + GSAP ScrollTrigger + LenisAgency-grade control, one hero object or scene, smallest bundle (a minimal three.js scene is ~132 KB gzip).You write loading, resize, disposal and the render loop yourself.
B · ReactNext.js + React Three Fiber 9 + drei 10 + @react-three/postprocessing 3 + GSAP/Lenis (ReactLenis)React teams, configurators, many interactive components, reuse of drei helpers (Environment, Lightformer, MeshTransmissionMaterial, AccumulativeShadows).Composer drops tone mapping; <Canvas shadows> warns on r186 (use "percentage"); @theatre/r3f only declares R3F 8.
C · IslandsAstro (or Nuxt) page with one lazy 3D island (client:visible / dynamic import())Content-heavy marketing sites where 3D is one moment, and Core Web Vitals matter most.Keep a single canvas; browsers cap live WebGL contexts (about 16).
D · No-codeSpline, Unicorn Studio (2.5D shader art), Rive (2D state machines), WebflowDesigner-owned hero, fast iteration, simple interactions.Less control of tone mapping and post. Spline advises 1–2 embeds per page and fewer than 3 lights.
E · Pre-renderedCycles/Octane render → WebP image sequence or all-intra video scrubbed by ScrollTriggerPath-traced fidelity (caustics, cloth) matters more than interaction; identical on every device (the Apple product-page approach).Bandwidth: 60–150 frames, separate mobile set; encode video with -g 1 so seeking does not snap.

WebGL or WebGPU?

Default to WebGLRenderer. The three.js manual still calls WebGPURenderer experimental, and pmndrs postprocessing, onBeforeCompile and custom ShaderMaterial do not run on it. Choose WebGPU + TSL for compute-heavy work (hundreds of thousands of particles) or a fresh TSL project; it ships in Chrome/Edge 113+, Android Chrome 121+, Safari 26 and Firefox 141+ (Windows), and falls back to WebGL 2 automatically.

Versions this playbook was verified against

PackageVersionNote
three0.186.1Pin exactly: almost every release renames something (see the Migration Guide).
postprocessing6.39.5Peer three <0.187; bump together.
gsap3.15.0All plugins free, including SplitText and ScrollSmoother, for commercial use.
lenis1.3.26Respects reduced motion by default.
@react-three/fiber · drei9.8.1 · 10.7.9v10 alpha is WebGPU/TSL-first.
@react-three/postprocessing3.1.3Bundles N8AO.
@gltf-transform/cli4.5.1KTX2 needs the KTX-Software ktx binary.
@theatre/core0.7.2No public release since May 2024; prefer GSAP timelines or Blender camera animation.
06

Tools and assets

Everything below was checked on 9 October 2026: repos with gh repo view (stars, last push, licence), versions on npm, every link fetched. Stale or abandoned projects are listed at the end so you do not build on them by accident.

Libraries

ToolUse it forVersion · starsCost
three.jsThe renderer under almost everything here (WebGL + WebGPU)r186 · 116k★Free, MIT
React Three Fiber + dreithree.js as React components; drei adds Environment, Lightformer, useGLTF, ScrollControls, MeshTransmissionMaterial9.8.1 · 10.7.9Free, MIT
postprocessingMerged effect passes: bloom, tone mapping, SMAA, DOF, vignette, noise6.39.5Free, Zlib
GSAP + ScrollTriggerTimelines and scroll-linked scenes. All plugins (SplitText, MorphSVG…) are free for commercial use; the one restriction is building no-code animation tools that compete with Webflow3.15.0 · 29k★Free (GSAP licence)
LenisSmooth scrolling that keeps native scroll semantics1.3.26 · 16k★Free, MIT
maath · Motion · react-springDamping helpers for R3F; React animation; spring physics0.10.8 · 14.0 · 10.1Free
r3f-scroll-rig · detect-gpuKeep 3D meshes in sync with DOM elements; GPU tiering to serve lighter scenes980★ · 1.2k★Free, MIT
OGL · Babylon.js · PlayCanvas · NeedleTiny WebGL for one shader effect; full engines with editors; Unity/Blender-to-web (pin Needle @stable)—Free / freemium
Threlte · TresJSthree.js for Svelte 5 and Vue8.6 · 5.9Free, MIT

No-code and low-code

ToolGood forPricing (vendor page)
SplineDesigner-built 3D scenes, hosted viewer or React component; official MCP server in the desktop appFree with watermark · Hobby $15/mo · Pro $30/mo (code + glTF export) · self-hosted export is Enterprise only
Unicorn StudioLayered WebGL shader effects for 2D/2.5D heroes (not 3D models)Free: 8 publishes with branding · Legend $20/mo
RiveInteractive 2D vector UI with state machines, layered over a 3D canvasFree with splash · from $9/seat/mo
Vectary · WompBrowser 3D/AR product scenes; easy SDF-style modellingVectary from $25/mo · Womp Pro $9.99/mo (glTF export unverified)

Making the 3D

Modelling and texturing

  • Blender 5.2 LTS: free, glTF exporter built in, supported to July 2028
  • Cinema 4D, Houdini 22 (vertex animation textures for the web), Substance 3D Painter, Marmoset Toolbag 5

Scans and Gaussian splats

  • Capture: Polycam, RealityScan 2 (free under $1M revenue), Scaniverse (free)
  • Render in three.js with Spark; clean up with SuperSplat; compress to SPZ

AI 3D generators: read the licence

  • Meshy and Tripo: free-plan outputs are CC BY 4.0 (credit required); paid plans remove it
  • Hyper3D Rodin: commercial licence listed on the Business tier
  • Hunyuan3D: licence excludes the EU, UK and South Korea
  • TRELLIS.2: MIT code, self-hosted (weights licence not checked)

Assets and licences

SourceLicenceNotes
Poly HavenCC0HDRIs for lighting, PBR textures, models. Use 1K HDRIs for lighting.
ambientCG · Kenney · QuaterniusCC0PBR materials; low-poly kits and characters
Khronos glTF Sample AssetsPer modelTest models for every glTF extension (transmission, KTX2, Draco)
Sketchfab · Poly PizzaPer modelCheck each model; filter by licence
Fab (Epic)Standard LicenceWeb use allowed, redistribution not. A public .glb can be downloaded from the browser, so bake paid assets into your scene.
MixamoFree with Adobe IDAuto-rigging and motions; unmaintained but online. FBX → Blender → glTF.

Pipeline and debugging

Optimise and check assets

Tune and profile

  • lil-gui (vanilla) or Leva (React) for live tweaking
  • stats-gl (GPU timing), r3f-perf, Spector.js frame capture
  • Official Three.js DevTools extension (the old three-devtools repo is archived)
  • Lighthouse / PageSpeed with GPU flags; software WebGL overstates the cost of 3D

Learn from code

Courses and reading

Hosting: what hosts do with a .glb

Host.glb by defaultCompressed?Custom headers
GitHub Pagesmodel/gltf-binarygzipNone; cache fixed at 10 minutes
Netlifyapplication/octet-streamNo (brotli for application/wasm and JSON)netlify.toml or _headers
Vercelnot checkedNo model/* on its compression listvercel.json
Cloudflare Pagesnot checkedNo model/*; application/wasm yes_headers (100 rules)

Compress at build time (meshopt or Draco for geometry, KTX2 for textures), set Content-Type and caching yourself, and use immutable only on hashed or versioned paths. Self-host the Draco and Basis decoders from three/examples/jsm/libs/ so their version matches three.js; the meshopt decoder is plain JS and needs no hosting.

Skip these

  • Theatre.js: no public release since May 2024, and @theatre/r3f only declares R3F 8.
  • pmndrs/react-three-next (last push 2024): use satus instead.
  • mkkellogg/GaussianSplats3D and lumalabs/luma-web-examples: abandoned or archived; use Spark.
  • Discover three.js: good concepts, but written for r132 (2021); check every API against r186.
07

AI agent kit

Coding agents build good 3D pages when they get three things: skills (how-to knowledge loaded on demand), MCP servers (live docs and tools such as Blender or a browser), and instruction files (your project's rules). All of them were checked on 9 October 2026.

Skills

Skill setWhat it coversStars · licence
greensock/gsap-skillsOfficial GSAP skills: core, timeline, ScrollTrigger, plugins, React, performance, plus an llms.txt16k★ · MIT
CloudAI-X/threejs-skills10 three.js skills: fundamentals, geometry, materials, lighting, textures, animation, loaders, shaders, postprocessing, interaction. Copy the skills/ folders (the README's clone URL is broken).3.5k★ · no licence file
Threejs-Awesome-Graphics-Agent-Skills24 advanced graphics skills: bloom, SSAO, volumetrics, water, shadows, visual validation885★ · MIT
dgreenheck/webgpu-claude-skillWebGPU + TSL with three.js1.2k★ · no licence file
enzed/r3f-skills11 React Three Fiber skills128★ · no licence file
3d-landing-page (this repo)This playbook as one skill: story table, asset rules, look order, scroll pattern, guards, verification—

Install a skill by copying its folder (the one containing SKILL.md) into .claude/skills/ in your project, or ~/.claude/skills/ for every project. Repos without a licence file are all-rights-reserved by default: read them, but check before redistributing.

MCP servers

ServerWhat the agent getsNotes
Three.js ResourcesVersion-stamped three.js guides, a tools and showcase index, and a GLSL → TSL converterFree, no key
Blender Lab MCP (official)Drive Blender 5.1+ from the agent; also an official Claude connectorRuns model-written code in Blender with no safeguards: isolate it
blender-mcp (community)Blender control plus Poly Haven / Sketchfab imports and AI generators30k★ · MIT · now named mcp-for-blender
Chrome DevTools MCP · Playwright MCPScreenshots, console and performance traces of the WebGL page at 1440 and 375 widthsFree
Context7Current docs for three.js, R3F and drei in the agent's contextFreemium
threejs-devtools-mcpInspect and edit a running three.js scene117★ · MIT
Spline · Unicorn Studio · FigmaOfficial MCPs: agents edit Spline scenes and shader scenes, and read Figma layoutsPlan-dependent
Netlify MCPCreate projects and deploy from the agent (how this site was deployed)Free
claude mcp add --transport http threejsresources https://threejsresources.com/api/mcp

Instruction files

  • Point agents at three.js's own llms.txt (and llms-full.txt, which includes TSL). It tells models to use import maps and when to pick WebGL or WebGPU.
  • Keep a CLAUDE.md / AGENTS.md with commands, where things live and the rules that must not break. This repo's CLAUDE.md is a working example.
  • Prefer scripts the agent writes and you can read (like tools/blender/build_orrery.py) over unguarded live control of desktop apps.

Starter prompt

Paste this into Claude Code inside a clone of the repo, and fill in the brackets.

Read playbook/playbook.html and .claude/skills/3d-landing-page/SKILL.md first.
Build a 3D landing page for [brand], a [what they sell] for [audience].
1. Propose the scroll story as a table (section, idea, model pose) and wait for my OK.
2. Model the hero object as a headless Blender script in tools/blender/, export a GLB and a
   Cycles poster, and compress it with the optimize script.
3. Reuse src/scene/* and src/choreography.js; change poses, materials and copy, not the guards.
4. Verify with npm run build, npm run preview and npm run shoot at 1440x900 and 375x812,
   and show me the screenshots before you call it done.
08

Zero-to-live plan

Twelve steps from an empty folder to a deployed site. Each step names the file in the reference repo that already does it, so you can copy the working version instead of starting from a blank page. The fastest route is step 02: clone the repo, then replace the model and the copy.

  1. Install the tools

    Node 22+, Git, the GitHub CLI, Blender 5.x and Chrome. On macOS with Homebrew:

    brew install node git gh webp
    brew install --cask blender google-chrome
    gh auth login

    Accounts: GitHub, and Netlify or Vercel for hosting. Optional: Poly Haven (no account needed) for HDRIs and textures.

  2. Start from the reference build (or scaffold)

    gh repo clone 1MrHad1/3d-landing-playbook my-3d-site
    cd my-3d-site && npm install && npm run dev

    Or from zero:

    npm create vite@latest my-3d-site -- --template vanilla
    cd my-3d-site
    npm i three@0.186.1 postprocessing@6.39.5 gsap@3.15.0 lenis@1.3.26
    npm i -D @gltf-transform/cli@4.5.1
  3. Write the scroll story before any 3D

    One line per section: the idea, the pose of the object, the one camera move. If you cannot write it as a table, the page will feel random. The reference build's story:

    SectionIdeaObject
    Hero3D websites that feel realRight side, idle gimbal spin, pointer parallax
    ServicesOne studio, whole pipelineSwings left, slows down
    Process (pinned)Four layers, taken apartExploded view, labels on parts, steps highlight
    ReferencesStudied from the bestRecedes far right, dims
    StackThe reliable stackReturns left, glows
    ContactStart from zeroCentres above the CTA, brightest glow
  4. Build the hero model in Blender

    • Real scale, applied transforms, bevels on hard edges, named objects (code finds parts by name).
    • Principled BSDF only: its inputs map 1:1 to glTF PBR. Custom node networks do not export.
    • Parent moving parts the way they move in reality (the reference gimbal is outer → middle → inner → core).
    • Script it if you can: a Python script is repeatable and reviewable. Hand-modelled is fine too.

    tools/blender/build_orrery.py

    npm run model   # blender -b -P tools/blender/build_orrery.py -- assets/orrery.raw.glb assets/orrery-poster.png
  5. Export glTF and render a poster

    Export GLB with +Y up and Apply Modifiers; skip UVs if there are no textures; leave compression off in Blender. In the same script, render a Cycles poster (transparent PNG, AgX) for the no-WebGL fallback and social image. On an Apple-silicon GPU it renders in about 12 seconds.

  6. Compress with gltf-transform

    npx gltf-transform optimize assets/orrery.raw.glb public/models/orrery.glb \
      --compress meshopt --palette false --join false --flatten false \
      --instance false --simplify false
    npx gltf-transform inspect public/models/orrery.glb

    The defaults merge nodes and materials (palette, join, flatten), which breaks code that targets named parts: the reference build hit exactly this and turned them off. Result: 955 KB → 203 KB. Add --texture-compress ktx2 (or webp) when the model has textures.

  7. Set up the stage

    Renderer without its own antialiasing or depth (the composer handles both), sRGB output, no renderer tone mapping, a PMREM environment, a 32° camera, and the model inside a rig group that scroll moves.

    src/scene/stage.js · src/scene/studio.js

    const renderer = new WebGLRenderer({ canvas, powerPreference: 'high-performance', antialias: false, stencil: false, depth: false });
    renderer.setPixelRatio(Math.min(devicePixelRatio, 1.75));
    renderer.toneMapping = NoToneMapping;               // AgX happens in the effect pass
    scene.environment = createStudioEnvironment(renderer); // softboxes → PMREM
    const camera = new PerspectiveCamera(32, aspect, 0.1, 60);
    await renderer.compileAsync(scene, camera);          // no hitch on reveal
  8. Load the model and finish the materials

    const loader = new GLTFLoader().setMeshoptDecoder(MeshoptDecoder);
    const gltf = await loader.loadAsync('/models/orrery.glb', (e) => onProgress(e.loaded / e.total));
    nodes.Core.material = new MeshPhysicalMaterial({ transmission: 1, thickness: 0.9, ior: 1.5, dispersion: 0.4, roughness: 0.04 });

    glTF carries the PBR basics; tune physical extras and envMapIntensity in code against the live lighting.

  9. Add post-processing

    HalfFloat composer with MSAA, then Bloom (threshold 1) → AgX → vignette → grain, as in chapter 04. Check that only genuinely bright pixels bloom: in the reference build the chrome ring bloomed like neon until the threshold went from 0.92 to 1.0.

  10. Choreograph the scroll

    Lenis on GSAP's ticker, a pose table per breakpoint, one scrubbed fromTo per section, one pinned timeline for the exploded view, damping in the render loop, and HTML labels projected onto 3D anchors.

    src/main.js · src/choreography.js

    const lenis = new Lenis({ lerp: 0.1, anchors: true });
    lenis.on('scroll', ScrollTrigger.update);
    gsap.ticker.add((t) => lenis.raf(t * 1000));
    gsap.ticker.lagSmoothing(0);
    choreograph(target);   // gsap.matchMedia(): desktop and phone pose tables
  11. Guard performance and accessibility

    • Load three.js with a dynamic import() after first paint; the headline is the LCP.
    • Cap DPR (1.75 desktop, 1.25 low-power) and adapt it from measured FPS.
    • Pause the loop in hidden tabs; start the load timeout only once the tab is visible.
    • prefers-reduced-motion or no WebGL 2 → the Cycles poster, no smooth scroll.
    • On phones, dim the canvas behind long text and use a vertical exploded view.
  12. Test, then deploy

    Build, serve the production bundle and capture every section at 1440×900 and 375×812 with real GPU Chrome. The script also reports LCP, the 3D-ready mark, the model size, horizontal overflow and console errors.

    npm run build && npm run preview &
    npm run shoot          # tools/shoot.mjs → .shots/*.png + timings
    git init && git add -A && git commit -m "First 3D landing page"
    gh repo create my-3d-site --public --source . --push

    Then on Netlify: Add new project → Import from GitHub (build npm run build, publish dist; netlify.toml already says so), or npx netlify-cli deploy --prod. Long-cache /assets/*.

    Measured on Netlify, October 2026Netlify brotli-compresses application/wasm and application/json responses but not model/gltf-binary. Meshopt files are designed to be compressed in transit, so the reference model drops from 203 KB to 65 KB when labelled application/wasm (Draco would be 116 KB plus a 75 KB decoder). GLTFLoader reads the bytes as an ArrayBuffer, so the label does not affect loading. If your host compresses model/gltf-binary, or strict MIME types matter more to you, keep the correct type.
09

Launch checklist

Tick these off before you call it done. Your ticks are saved in this browser only.

Story and layout
Asset
Look
Performance and access
10

Repo map

Everything in this playbook is in 1MrHad1/3d-landing-playbook.

index.html                   the reference landing page (sections carry data-pose)
src/main.js                  boot: Lenis + GSAP, text reveals, loader, lazy 3D, poster fallback
src/choreography.js          pose tables (desktop / phone), scrubbed tweens, pinned exploded view
src/scene/stage.js           renderer, composer (bloom, AgX, vignette, grain), loop, adaptive DPR
src/scene/studio.js          generated softbox environment → PMREM (no HDRI download)
src/scene/orrery.js          GLB load, material finishing, gimbal joints, explode, label anchors
src/data/references.js       the reference sites shown on the landing page
src/style.css                layout and type for the landing page
tools/blender/build_orrery.py headless Blender: model, bevels, materials, GLB export, Cycles poster
tools/shoot.mjs              headless GPU Chrome: screenshots + LCP + 3D timing + overflow check
tools/build-playbook.mjs     wraps this playbook into /playbook/index.html for Netlify
playbook/playbook.html       this document
public/models/orrery.glb     the compressed model (203 KB, meshopt)
public/img/                  Cycles poster (WebP), social image, favicon
netlify.toml                 build command, publish dir, cache headers, GLB served compressed
npm run dev        # local dev server
npm run model      # rebuild the GLB + poster in Blender (headless)
npm run optimize   # compress the GLB with gltf-transform
npm run build      # playbook + production bundle into dist/
npm run shoot      # screenshots and timings (needs `npm run preview` running)
11

Sources

Primary sources first. Numbers marked as measured were taken on the reference build (Apple M4, Chrome with the GPU enabled) or with the npm packages on 9 October 2026.

Reference sites

Not verified: Cinema 4D, Houdini Indie, Tripo and Needle Pro prices (third-party sources only), Womp glTF export, TRELLIS.2 weight licences, and the default .glb content type on Vercel and Cloudflare.