Three.js From Zero · Article s13-03
Drei Deep Dive
Drei Deep Dive is Article s13-03 of Three.js From Zero, a MasterAllArts free interactive lesson for artists learning creative 3D on the web.
Season 13 · Article 03 · R3F Mastery
The R3F helper library you'll import on day one and never stop using. Environment maps, controls, model loading, HTML in 3D, performance helpers — 20 components that save weeks of bespoke code.
Code-walkthrough article
This article is a reference tour of the drei API. Best read alongside an open R3F project — try each snippet, then keep this page open as your cheat sheet.
Install
npm install @react-three/drei
import { Environment, OrbitControls, useGLTF, Html, Stats, Float } from '@react-three/drei';
The "every scene needs these" set
<Canvas>
<Environment preset="city" /> {/* HDR lighting — your scene goes from flat to film */}
<OrbitControls makeDefault /> {/* mouse orbit, pan, zoom */}
<Stats /> {/* FPS / draw call counter */}
<YourScene />
</Canvas>
<Environment> with a preset is the single biggest visual upgrade you can make in one line. Presets: 'city', 'sunset', 'dawn', 'night', 'warehouse', 'forest', 'apartment', 'studio', 'park', 'lobby'. They're free, ship with drei, and instantly turn MeshStandardMaterial into something that looks rendered, not flat.
Model loading
function Duck() {
const { scene } = useGLTF('/duck.glb');
return <primitive object={scene} />;
}
useGLTF.preload('/duck.glb'); // optional — start loading before mount
useGLTF is useLoader(GLTFLoader, ...) with caching. Same model URL? Same buffer in memory. Critical when you place the same model 100 times.
HTML in 3D
<Html position={[0, 1.5, 0]} center distanceFactor={6}>
<div className="label">Click me</div>
</Html>
Renders a DOM element anchored at a 3D position. distanceFactor makes it scale with camera distance — feels native. occlude prop hides it behind 3D objects. The pattern for tooltips, callouts, buttons in 3D scenes.
Float — the "looks alive" component
<Float speed={2} rotationIntensity={1} floatIntensity={2}>
<mesh>...</mesh>
</Float>
Wraps any mesh in subtle bobbing rotation. Use it on every static "showcase" object. The difference between "3D logo" and "alive 3D logo" is this one component.
Controls beyond OrbitControls
<PointerLockControls />— FPS-style camera, click to capture cursor.<TrackballControls />— six degrees of freedom, no "up" constraint.<ScrollControls>...</ScrollControls>— a virtual scroll surface that drives your scene state. Combine withuseScrollhook to read position.
Performance helpers
<Detailed distances={[0, 10, 30]}>
<HighDetailMesh /> {/* shown at 0-10m */}
<MidDetailMesh /> {/* shown at 10-30m */}
<LowDetailMesh /> {/* shown at 30m+ */}
</Detailed>
<Instances limit={10000}>
<boxGeometry />
<meshStandardMaterial />
{grass.map((g, i) => <Instance key={i} position={g.pos} />)}
</Instances>
Detailed = R3F's LOD. Instances = the JSX wrapper around InstancedMesh. Both common, both prevent perf cliffs.
The full 20
Memorize these names — when you need something, check if drei has it first:
Environment, OrbitControls, useGLTF, Html, Stats, Float, useTexture, Instances, Detailed, ScrollControls, Loader, PerspectiveCamera, OrthographicCamera, Sky, Stars, ContactShadows, SoftShadows, MeshPortalMaterial, Text, Text3D.
Common first-time pitfalls
Exercises
- Build a product viewer in 30 lines. Environment, OrbitControls, useGLTF on a real model. Add ContactShadows underneath for grounding. That's a product page.
- Label your scene. Place <Html> labels on three points of a model. Test occlusion — toggle the prop and see the difference.
- Replace your loops with <Instances>. Take any scene where you placed N identical meshes manually. Swap to <Instances>. Watch FPS jump.
UP NEXT
S13-04 — R3F + Rapier Physics → Rigid bodies, joints, character controllers — all in JSX.