# Core Web Vitals & Loading > Source: [100 Three.js Tips - Utsubo](https://www.utsubo.com/blog/threejs-best-practices-100-tips) Optimize loading performance to improve LCP, FID, and CLS scores. ## Lazy Load 3D Content Below the Fold Don't block page load with 3D content that's not visible. ```javascript const observer = new IntersectionObserver((entries) => { if (entries[0].isIntersecting) { loadThreeJsScene(); observer.disconnect(); } }); observer.observe(canvasContainer); ``` ## Code-Split Three.js Modules Dynamic imports reduce initial bundle size. ```javascript // Load Three.js only when needed const Three = await import('three'); const { GLTFLoader } = await import('three/addons/loaders/GLTFLoader.js'); ``` ## Preload Critical Assets Use `` for critical resources: ```html ``` ## Progressive Loading Show low-res content immediately, upgrade when ready: ```javascript // Show low-res immediately const lowRes = await loadModel('low.glb'); scene.add(lowRes); // Load and swap high-res in background loadModel('high.glb').then(highRes => { scene.remove(lowRes); lowRes.traverse(child => { if (child.geometry) child.geometry.dispose(); if (child.material) child.material.dispose(); }); scene.add(highRes); }); ``` ## Placeholder Geometry Show loading state with placeholder geometry: ```javascript const placeholder = new THREE.Mesh( new THREE.BoxGeometry(1, 1, 1), new THREE.MeshBasicMaterial({ color: 0x808080, wireframe: true }) ); scene.add(placeholder); loadModel().then(model => { scene.remove(placeholder); placeholder.geometry.dispose(); placeholder.material.dispose(); scene.add(model); }); ``` ## Offload to Web Workers Move CPU-intensive work off main thread: ```javascript // main.js const worker = new Worker('/physics-worker.js'); worker.postMessage({ positions, velocities }); worker.onmessage = (event) => { updatePositions(event.data.positions); }; // physics-worker.js self.onmessage = (event) => { const result = computePhysics(event.data); self.postMessage(result); }; ``` ## Stream Large Scenes Load chunks based on camera position: ```javascript function updateVisibleChunks(cameraPosition) { const visibleChunks = getChunksNear(cameraPosition); visibleChunks.forEach(chunk => { if (!chunk.loaded) { loadChunk(chunk); } }); // Unload distant chunks loadedChunks.forEach(chunk => { if (!visibleChunks.includes(chunk)) { unloadChunk(chunk); } }); } ``` ## React Three Fiber: Suspense ```jsx import { Suspense } from 'react'; function App() { return ( }> ); } ``` ## Performance Budget | Asset Type | Target | |------------|--------| | Initial JS bundle | < 150KB gzipped | | Hero model | < 500KB compressed | | Textures | < 1MB total (KTX2) | | Time to interactive | < 3 seconds | ## Checklist - [ ] Lazy load 3D content below the fold - [ ] Code-split Three.js modules - [ ] Preload critical assets - [ ] Implement progressive loading - [ ] Use placeholder geometry during load - [ ] Offload physics to Web Workers - [ ] Stream large scenes by chunks - [ ] Use Suspense in React