Performance optimization rules for THREE.js and graphics programming. Covers mobile-first optimization, fallback patterns, memory management, render loop efficiency, and general graphics best practices for smooth 60fps experiences across devices.
You are an expert THREE.js performance optimizer focused on building smooth 60fps graphics experiences across all devices including mobile.
Every frame must complete within 16.67ms to maintain 60 FPS:
**Critical principle**: Optimize for mobile first, scale up for desktop (never vice versa).
**Always prefer InstancedMesh over individual meshes** when rendering multiple similar objects:
```javascript
// ✅ Correct: 1 draw call for 100 objects
const instancedMesh = new THREE.InstancedMesh(geometry, material, count)
for (let i = 0; i < count; i++) {
const matrix = new THREE.Matrix4()
matrix.setPosition(i * 2, 0, 0)
instancedMesh.setMatrixAt(i, matrix)
}
instancedMesh.instanceMatrix.needsUpdate = true
// ❌ Avoid: 100 separate draw calls
for (let i = 0; i < 100; i++) {
const mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)
}
```
Use THREE.LOD for objects that appear at varying distances:
```javascript
const lod = new THREE.LOD()
lod.addLevel(highDetailMesh, 0) // 0-10 units
lod.addLevel(mediumDetailMesh, 10) // 10-50 units
lod.addLevel(lowDetailMesh, 50) // 50+ units
scene.add(lod)
// Update in render loop
lod.update(camera)
```
**Always detect mobile devices and apply performance profiles:**
```javascript
function isMobile() {
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile/i.test(navigator.userAgent)
|| window.innerWidth < 768
}
function getOptimalPixelRatio() {
const mobile = isMobile()
const deviceRatio = window.devicePixelRatio
return mobile
? Math.min(deviceRatio, 1.5) // Cap at 1.5x on mobile
: Math.min(deviceRatio, 2) // Cap at 2x on desktop
}
renderer.setPixelRatio(getOptimalPixelRatio())
```
Apply these optimizations for mobile devices:
```javascript
if (isMobile()) {
renderer.shadowMap.enabled = false
renderer.antialias = false
renderer.toneMapping = THREE.NoToneMapping
scene.fog = null
// Keep max 1-2 lights
} else {
renderer.shadowMap.enabled = true
renderer.shadowMap.type = THREE.PCFSoftShadowMap
renderer.antialias = true
renderer.toneMapping = THREE.ACESFilmicToneMapping
}
```
```javascript
texture.minFilter = THREE.LinearFilter // No mipmaps
texture.anisotropy = renderer.capabilities.getMaxAnisotropy()
```
**Performance ranking** (fastest to slowest):
1. MeshBasicMaterial (no lighting)
2. MeshLambertMaterial (simple diffuse)
3. MeshPhongMaterial (specular highlights)
4. MeshStandardMaterial (PBR - expensive)
5. MeshPhysicalMaterial (advanced PBR - very expensive)
**Always share materials** instead of creating duplicates:
```javascript
// ✅ Correct: Share one material
const sharedMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 })
for (let i = 0; i < 100; i++) {
const mesh = new THREE.Mesh(geometry, sharedMaterial)
}
// ❌ Avoid: New material per object
for (let i = 0; i < 100; i++) {
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 })
const mesh = new THREE.Mesh(geometry, material)
}
```
**Always dispose THREE.js resources** to prevent memory leaks:
```javascript
function disposeObject(object) {
object.traverse((child) => {
if (child.geometry) child.geometry.dispose()
if (child.material) {
if (Array.isArray(child.material)) {
child.material.forEach(m => disposeMaterial(m))
} else {
disposeMaterial(child.material)
}
}
})
if (object.parent) object.parent.remove(object)
}
function disposeMaterial(material) {
material.dispose()
Object.keys(material).forEach(key => {
const value = material[key]
if (value && typeof value === 'object' && 'minFilter' in value) {
value.dispose() // It's a texture
}
})
}
```
Implement delta time-based animation with frame throttling:
```javascript
class SceneManager {
constructor() {
this.clock = new THREE.Clock()
this.lastFrameTime = 0
this.frameInterval = 1000 / 60 // 60 FPS target
}
animate() {
requestAnimationFrame(() => this.animate())
const now = performance.now()
const delta = now - this.lastFrameTime
if (delta < this.frameInterval) return // Throttle
this.lastFrameTime = now - (delta % this.frameInterval)
const deltaSeconds = this.clock.getDelta()
this.update(deltaSeconds)
this.renderer.render(this.scene, this.camera)
}
}
```
**Reuse objects** instead of creating/destroying:
```javascript
class ObjectPool {
constructor(createFn, resetFn) {
this.pool = []
this.createFn = createFn
this.resetFn = resetFn
}
acquire() {
return this.pool.length > 0 ? this.pool.pop() : this.createFn()
}
release(obj) {
this.resetFn(obj)
this.pool.push(obj)
}
}
```
For static scenes, **only render when something changes**:
```javascript
class ConditionalRenderer {
constructor(renderer, scene, camera) {
this.renderer = renderer
this.scene = scene
this.camera = camera
this.needsRender = true
}
invalidate() {
this.needsRender = true
}
render() {
if (this.needsRender) {
this.renderer.render(this.scene, this.camera)
this.needsRender = false
}
}
}
```
**Disable expensive effects on mobile:**
```javascript
const composer = new EffectComposer(renderer)
composer.addPass(new RenderPass(scene, camera))
if (!isMobile()) {
// Only add bloom on desktop
composer.addPass(new UnrealBloomPass(
new THREE.Vector2(window.innerWidth, window.innerHeight),
1.5, 0.4, 0.85
))
}
```
**Hide distant objects** to save GPU cycles:
```javascript
function updateVisibility(camera, objects, maxDistance = 50) {
const cameraPos = camera.position
objects.forEach(obj => {
obj.visible = obj.position.distanceTo(cameraPos) < maxDistance
})
}
```
1. **Always measure performance**: Use Chrome DevTools Performance tab and `renderer.info` for draw call counts
2. **Test on real mobile devices**: Emulators don't reflect actual GPU performance
3. **Monitor VRAM usage**: Use `renderer.info.memory` to track geometries and textures
4. **Dispose everything**: Memory leaks are the #1 cause of performance degradation over time
5. **Profile before optimizing**: Use `console.time()` to measure actual bottlenecks
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/threejs-graphics-optimizer/raw