Three.js Cheatsheet
Loading Models (glTF)
Use this Three.js reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
GLTFLoader — Basic Load
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; const loader = new GLTFLoader(); loader.load( '/models/scene.glb', (gltf) => { const model = gltf.scene; // THREE.Group scene.add(model); }, (xhr) => { console.log((xhr.loaded / xhr.total * 100).toFixed(1) + '% loaded'); }, (error) => { console.error('Load error:', error); } ); // Async/await const gltf = await new Promise((res, rej) => loader.load(url, res, undefined, rej) ); scene.add(gltf.scene);
GLTF Object Structure
gltf.scene; // THREE.Group — the root scene node gltf.scenes; // Array<THREE.Group> — all scenes in the file gltf.cameras; // Array<THREE.Camera> gltf.animations; // Array<THREE.AnimationClip> gltf.asset; // { version, generator, ... } metadata gltf.userData; // any extensions/extras
Draco Compression
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; const draco = new DRACOLoader(); // Path to the draco decoder WASM files (copy from node_modules) draco.setDecoderPath('/draco/'); draco.setDecoderConfig({ type: 'js' }); // or 'wasm' (default) draco.preload(); // pre-warm the decoder const loader = new GLTFLoader(); loader.setDRACOLoader(draco); loader.load('/models/compressed.glb', (gltf) => { scene.add(gltf.scene); });
Draco decoder files:
copy node_modules/three/examples/jsm/libs/draco/ public/draco/
KTX2 Textures in GLTF
import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js'; const ktx2 = new KTX2Loader() .setTranscoderPath('/basis/') .detectSupport(renderer); const loader = new GLTFLoader(); loader.setKTX2Loader(ktx2); loader.load('/models/scene.glb', (gltf) => scene.add(gltf.scene));
Traversing the Loaded Model
loader.load('/models/robot.glb', (gltf) => { const model = gltf.scene; // Enable shadows on all meshes model.traverse((child) => { if (child.isMesh) { child.castShadow = true; child.receiveShadow = true; // child.material.envMapIntensity = 1; } }); // Find a specific named node const head = model.getObjectByName('Head'); const body = model.getObjectByName('Body'); // Center the model const box = new THREE.Box3().setFromObject(model); const center = new THREE.Vector3(); box.getCenter(center); model.position.sub(center); // Scale to fit const size = new THREE.Vector3(); box.getSize(size); const maxDim = Math.max(size.x, size.y, size.z); model.scale.setScalar(2 / maxDim); scene.add(model); });
Animations
loader.load('/models/character.glb', (gltf) => { const model = gltf.scene; const clips = gltf.animations; // Array<AnimationClip> const mixer = new THREE.AnimationMixer(model); // Play a specific clip by name const walkClip = THREE.AnimationClip.findByName(clips, 'Walk'); const walkAction = mixer.clipAction(walkClip); walkAction.play(); // Or play all clips clips.forEach((clip) => mixer.clipAction(clip).play()); // Advance in render loop const clock = new THREE.Clock(); function animate() { requestAnimationFrame(animate); const delta = clock.getDelta(); mixer.update(delta); renderer.render(scene, camera); } animate(); scene.add(model); });
Other Loaders
| Loader | Import Path | Format |
|---|---|---|
GLTFLoader | three/addons/loaders/GLTFLoader.js | .gltf, .glb |
DRACOLoader | three/addons/loaders/DRACOLoader.js | Draco-compressed geometry |
OBJLoader | three/addons/loaders/OBJLoader.js | .obj (no material) |
MTLLoader | three/addons/loaders/MTLLoader.js | .mtl (for OBJ materials) |
FBXLoader | three/addons/loaders/FBXLoader.js | .fbx |
ColladaLoader | three/addons/loaders/ColladaLoader.js | .dae |
PLYLoader | three/addons/loaders/PLYLoader.js | .ply (point clouds) |
SVGLoader | three/addons/loaders/SVGLoader.js | .svg → paths |
FontLoader | three/addons/loaders/FontLoader.js | typeface JSON |
EXRLoader | three/addons/loaders/EXRLoader.js | .exr HDR |
RGBELoader | three/addons/loaders/RGBELoader.js | .hdr (Radiance) |
TGALoader | three/addons/loaders/TGALoader.js | .tga |
KTX2Loader | three/addons/loaders/KTX2Loader.js | .ktx2 |
OBJ + MTL
import { OBJLoader } from 'three/addons/loaders/OBJLoader.js'; import { MTLLoader } from 'three/addons/loaders/MTLLoader.js'; const mtlLoader = new MTLLoader(); mtlLoader.load('/models/scene.mtl', (materials) => { materials.preload(); const objLoader = new OBJLoader(); objLoader.setMaterials(materials); objLoader.load('/models/scene.obj', (obj) => scene.add(obj)); });
Text / TextGeometry
import { FontLoader } from 'three/addons/loaders/FontLoader.js'; import { TextGeometry } from 'three/addons/geometries/TextGeometry.js'; const fontLoader = new FontLoader(); fontLoader.load('/fonts/helvetiker_regular.typeface.json', (font) => { const geo = new TextGeometry('Hello Three.js', { font: font, size: 0.5, depth: 0.1, curveSegments: 12, bevelEnabled: true, bevelThickness: 0.03, bevelSize: 0.02, bevelSegments: 5, }); geo.center(); const mesh = new THREE.Mesh(geo, new THREE.MeshStandardMaterial({ color: 0xffffff })); scene.add(mesh); });
LoadingManager Pattern (Multiple Assets)
const manager = new THREE.LoadingManager(); let progress = 0; manager.onStart = (url, loaded, total) => console.log('Started'); manager.onLoad = () => console.log('All done — hide loading screen'); manager.onProgress = (url, loaded, total) => { progress = loaded / total; console.log(`${(progress * 100).toFixed(0)}%`); }; manager.onError = (url) => console.error('Failed:', url); const gltfLoader = new GLTFLoader(manager); const texLoader = new THREE.TextureLoader(manager); // All loads tracked by the same manager gltfLoader.load('/model.glb', (g) => scene.add(g.scene)); texLoader.load('/tex/diff.jpg', (t) => (mat.map = t));
Cloning Loaded Models
// Share geometry + material across instances (no re-download) import { SkeletonUtils } from 'three/addons/utils/SkeletonUtils.js'; // For SKINNED meshes (with bones/animations) — must use SkeletonUtils const clone1 = SkeletonUtils.clone(gltf.scene); const clone2 = SkeletonUtils.clone(gltf.scene); // For static meshes without skeleton — simple clone works const clone = gltf.scene.clone(); clone.position.set(5, 0, 0); scene.add(clone);
Gotchas
.glbis binary GLTF (single file, smaller, preferred)..gltfis JSON and references external.binand texture files.
Textures in GLTF are flipped — always set
texture.flipY = falseif you load textures manually and use them in a GLTF material context.
Draco decoder must be served as a static file. The WASM version is faster but requires COOP/COEP headers for SharedArrayBuffer.
gltf.sceneis the first scene; multi-scene files usegltf.scenes[i].
Shadows must be enabled on every mesh after loading — they default to
castShadow = false.
AnimationMixeris per root object. If you clone a model, create a newAnimationMixerfor each clone.
SkeletonUtils.cloneis required when cloning a model that hasSkinnedMeshnodes — regular.clone()will share bone references and break animations.