threejs-graphics-optimizer
Comprehensive collection of 100+ custom Droids & 300+ Skills for FactoryAI Droid system
npx skills add https://github.com/ovachiever/droid-tings --skill threejs-graphics-optimizerSKILL.md
THREE.js Graphics Optimizer
Version: 1.0
Focus: Performance optimization for THREE.js and graphics applications
Purpose: Build smooth 60fps graphics experiences across all devices including mobile
Philosophy: Performance-First Graphics
The 16ms Budget
Target: 60 FPS = 16.67ms per frame
Frame budget breakdown:
- JavaScript logic: ~5-8ms
- Rendering (GPU): ~8-10ms
- Browser overhead: ~2ms
If you exceed 16ms: Frames drop, stuttering occurs.
Mobile vs Desktop Reality
Desktop: Powerful GPU, lots of VRAM, high pixel ratios
Mobile: Constrained GPU, limited VRAM, battery concerns, thermal throttling
Design philosophy: Optimize for mobile, scale up for desktop (not vice versa).
Part 1: Core Optimization Principles
1. Minimize Draw Calls
The Problem: Each object = one draw call. 1000 objects = 1000 calls = slow.
Solution: Geometry Merging
// ❌ Bad: 100 draw calls for 100 cubes
for (let i = 0; i < 100; i++) {
const geometry = new THREE.BoxGeometry(1, 1, 1)
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 })
const cube = new THREE.Mesh(geometry, material)
cube.position.set(i * 2, 0, 0)
scene.add(cube)
}
// ✅ Good: 1 draw call via InstancedMesh
const geometry = new THREE.BoxGeometry(1, 1, 1)
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 })
const instancedMesh = new THREE.InstancedMesh(geometry, material, 100)
for (let i = 0; i < 100; i++) {
const matrix = new THREE.Matrix4()
matrix.setPosition(i * 2, 0, 0)
instancedMesh.setMatrixAt(i, matrix)
}
instancedMesh.instanceMatrix.needsUpdate = true
scene.add(instancedMesh)
When to use:
- Many similar objects (particles, trees, enemies)
- Static or semi-static positioning
- Shared material/geometry
2. Level of Detail (LOD)
Render simpler geometry when objects are far away:
const lod = new THREE.LOD()
// High detail (near camera)
const highDetailGeo = new THREE.IcosahedronGeometry(1, 3) // Many faces
const highDetailMesh = new THREE.Mesh(
highDetailGeo,
new THREE.MeshStandardMaterial({ color: 0x00d9ff })
)
lod.addLevel(highDetailMesh, 0) // Distance 0-10
// Medium detail
const medDetailGeo = new THREE.IcosahedronGeometry(1, 1)
const medDetailMesh = new THREE.Mesh(
medDetailGeo,
new THREE.MeshBasicMaterial({ color: 0x00d9ff })
)
lod.addLevel(medDetailMesh, 10) // Distance 10-50
// Low detail (far from camera)
const lowDetailGeo = new THREE.IcosahedronGeometry(1, 0)
const lowDetailMesh = new THREE.Mesh(
lowDetailGeo,
new THREE.MeshBasicMaterial({ color: 0x00d9ff })
)
lod.addLevel(lowDetailMesh, 50) // Distance 50+
scene.add(lod)
// Update LOD in render loop
function animate() {
lod.update(camera)
renderer.render(scene, camera)
}
3. Frustum Culling (Automatic)
THREE.js automatically skips objects outside camera view. Help it:
// ❌ Bad: Unnecessarily large bounding volumes
mesh.geometry.computeBoundingSphere()
mesh.geometry.boundingSphere.radius = 1000 // Too large!
// ✅ Good: Accurate bounding volumes
mesh.geometry.computeBoundingSphere() // Uses actual geometry size
mesh.geometry.computeBoundingBox()
4. Texture Optimization
Texture size matters:
- 4K texture (4096x4096): 64MB VRAM (uncompressed)
- 2K texture (2048x2048): 16MB VRAM
- 1K texture (1024x1024): 4MB VRAM
Rules:
- Use smallest textures that look good
- Power-of-two dimensions (512, 1024, 2048)
- Compress textures (use basis/KTX2 format)
const textureLoader = new THREE.TextureLoader()
// ❌ Bad: Loading 4K texture for small object
const texture = textureLoader.load('texture-4k.jpg')
// ✅ Good: Appropriate size for use case
const texture = textureLoader.load('texture-1k.jpg')
// ✅ Better: Set appropriate filtering
texture.minFilter = THREE.LinearFilter // No mipmaps (saves VRAM)
texture.anisotropy = renderer.capabilities.getMaxAnisotropy()
// ✅ Best: Dispose when done
function cleanup() {
texture.dispose()
}
Part 2: Mobile-Specific Optimization
Mobile Detection & Adaptation
/**
* Detect mobile device.
* @returns {boolean}
*/
export function isMobile() {
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile/i.test(navigator.userAgent)
|| window.innerWidth < 768
}
/**
* Get optimal pixel ratio for device.
* @returns {number}
*/
export function getOptimalPixelRatio() {
const mobile = isMobile()
const deviceRatio = window.devicePixelRatio
// Cap pixel ratio on mobile to save performance
return mobile
? Math.min(deviceRatio, 1.5) // Max 1.5x on mobile
: Math.min(deviceRatio, 2) // Max 2x on desktop
}
// Apply to renderer
renderer.setPixelRatio(getOptimalPixelRatio())
Mobile Performance Settings
/**
* Configure renderer for mobile performance.
*/
function setupMobileOptimizations(renderer, scene, camera) {
const mobile = isMobile()
if (mobile) {
//
...