Awwwards-Style 3D Websites
You are building scroll-driven, cinematic 3D web experiences in the lineage of Active Theory, Lusion, 14islands, Bonhomme, Resn, Studio Freight, Locomotive, and Igloo Inc. The aesthetic target is premium, slow, lerped, glossy, atmospheric — never snappy, never busy, never default.
Core Philosophy
Read these before touching code. They are the difference between "Three.js demo" and "Awwwards Site of the Day".
- Aesthetics > Geometry. A 6-poly cube with a good HDRI, ACESFilmic tone mapping, and bloom looks better than a 200k-poly model lit by a single DirectionalLight. Spend budget on lighting/post-processing first, geometry last.
- Lerp everything. No value should ever change instantly. Camera position, rotation, scroll progress, mouse position, even raycaster targets — all run through a damping factor (0.06–0.1 is the sweet spot). The world feels alive because nothing snaps.
- Single-file HTML when possible. Default deliverable is one
.htmlfile using ESM imports fromunpkg(andesm.shfor GSAP). No build step unless the user explicitly asks for one. This is what makes the work portable, reviewable, and shareable. - Mobile first, performance always. Cap
pixelRatioatMath.min(devicePixelRatio, 2). Frustum-cull. Halve post-processing on mobile. Test on a mid-tier Android in Chrome DevTools before declaring done. - ACESFilmic tone mapping is non-negotiable.
renderer.toneMapping = THREE.ACESFilmicToneMappingandrenderer.outputColorSpace = THREE.SRGBColorSpace. Without this, everything looks flat and "WebGL-ish". metalness > 0requires an environment map. A metallic material with no envMap is a black blob. Always load an HDRI (or useRoomEnvironmentas a fallback) before introducing metals/glass.- 60fps target on desktop, 30fps floor on mobile. If you can't hit it, cut post-processing passes before cutting geometry — the eye notices noise more than triangles.
Locked Tech Stack
These versions are tested together. Do not upgrade without asking.
three 0.170.0
gsap 3.12.5 (with ScrollTrigger)
lenis 1.1.0 (NOT @studio-freight/lenis — that scope was retired)
Post-processing uses three's built-in EffectComposer from three/addons/postprocessing/. The standalone postprocessing library (v6.36.0) was evaluated and dropped from this skill — its internals call Material.onBeforeRender(), which three r170 removed, producing a console warning per material per frame. Three's built-in passes (UnrealBloomPass, OutputPass, ShaderPass, LUTPass, etc.) cover every effect we need with zero version drift.
Glass uses three's built-in MeshPhysicalMaterial with transmission: 1.0 + dispersion: 1.2 (r166+ feature — chromatic refraction without external libs). The drei-vanilla MeshTransmissionMaterial is NOT in three core/addons; reach for it only when you need temporal distortion or per-object backbuffer sampling (then add drei-vanilla and three-mesh-bvh@0.7.8 to your importmap). See PATTERNS.md #5 for both paths.
CDN imports use this importmap pattern (see assets/templates/minimal.html for the canonical example):
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.170.0/build/three.module.js",
"three/addons/": "https://unpkg.com/three@0.170.0/examples/jsm/",
"gsap": "https://esm.sh/gsap@3.12.5",
"gsap/ScrollTrigger": "https://esm.sh/gsap@3.12.5/ScrollTrigger",
"lenis": "https://unpkg.com/lenis@1.1.0/dist/lenis.mjs"
}
}
</script>
Composer choice — settled: This skill uses three's built-in
EffectComposerfromthree/addons/postprocessing/. The standalonepostprocessingnpm library was tested and rejected:postprocessing@6.36.0flagsMaterial.onBeforeRender()removed in r170, flooding the console once per material per frame. Three built-in passes cover every effect this skill needs (bloom, vignette via ShaderPass, grain via ShaderPass, LUT via LUTPass, output tone-mapping via OutputPass). Seereferences/POST_PROCESSING.mdfor the canonical pass chain.
CDN reliability: Default to
unpkg.comforthreeandlenis— it mirrors the npm directory structure exactly sothree/addons/...resolves cleanly.cdn.jsdelivr.netwas tested and intermittently failed to servethree@0.170.0/examples/jsm/postprocessing/*files (ERR_CONNECTION_CLOSED) from some networks. Useesm.shfor GSAP because that's the cleanest ESM gateway for the GSAP UMD bundle. If unpkg is unreachable, fallback order: unpkg → esm.sh → cdn.skypack.dev. For production, vendor the files locally (download to/vendor/) — strict CDN dependency is acceptable for prototypes only.
Update policy: Versions are locked because they were tested together. To upgrade: (1) bump in
assets/templates/minimal.htmlfirst, (2) run all four templates in a browser, (3) fix any breaks, (4) update SKILL.md and PATTERNS.md. Do not silently bump versions in a single template — drift between templates is the #1 source of "works on my machine".
The 5-Step Workflow
Follow this order. Skipping a step produces "tech demo" output instead of "premium site" output.
Step 1 — Identify the Experience Archetype
Every Awwwards 3D site fits one of these. Pick one and commit:
| Archetype | Pattern | Reference |
|---|---|---|
| Object showcase | Single hero object, camera orbits/zooms on scroll | Bonhomme product pages, Apple AirPods Pro |
| Room walkthrough | Camera path through 3D interior | Igloo Inc., Bruno Simon portfolio |
| Tunnel | Camera flies forward through endless geometry | Active Theory experiments |
| Vertical descent | Scroll = falling/rising through layers | 14islands case studies |
| Flyover | Camera traverses a landscape | Lusion case studies |
| Particle field | Thousands of points reacting to scroll/mouse | Resn experiments |
Ask the user which archetype if they haven't said. Do not invent a new one — these are proven.
Step 2 — Plan Assets
Choose the cheapest source that meets the bar:
- Three.js primitives (
SphereGeometry,BoxGeometry,TorusKnotGeometry) — for abstract/showcase work. Most Awwwards sites use these. Don't overlook them. - Blender via MCP — when you need a custom shape. See
references/BLENDER_PIPELINE.mdfor recipes (coin, glass orb, blob, low-poly room) and the GLB export config (Draco + KTX2). - Polyhaven — HDRIs and textures, free, CC0. The HDRI is more important than the model.
- Sketchfab — last resort for ready-made models. Always check polycount.
Step 3 — Pick a Template
Start from assets/templates/:
minimal.html— empty scene with HDRI, tone mapping, Lenis, post-processing wired up. Use as the foundation for anything custom.coin-scroll.html— coin rotates and falls on scroll (object showcase).room-walkthrough.html— camera moves along a spline through a room (room walkthrough).glass-product.html— single product withMeshPhysicalMaterialtransmission + dispersion (r166+), HDRI, gentle bloom (object showcase, glass).
Templates are starting points, not final work. Strip what you don't need, layer what you do.
Step 4 — Layer the Polish (in this order)
The order matters. Each layer assumes the previous is in place.
- HDRI loaded and applied as
scene.environment - Tone mapping —
ACESFilmicToneMapping, exposure1.0–1.2 - Lighting — even with HDRI, add one
DirectionalLightfor shadow direction - Post-processing — three built-in
EffectComposerwith HDR target →RenderPass→UnrealBloomPass(strength ~0.45, radius 0.4, threshold 0.82) → vignetteShaderPass→OutputPass(appliesrenderer.toneMapping = ACESFilmic) → grainShaderPasslast - Lenis smooth scroll wired to GSAP
ScrollTrigger.update - Custom cursor (optional, but a Bonhomme/Lusion signature