threejs-graphics-optimizer

from ovachiever/droid-tings

Comprehensive collection of 100+ custom Droids & 300+ Skills for FactoryAI Droid system

19 stars1 forksUpdated Nov 25, 2025
npx skills add https://github.com/ovachiever/droid-tings --skill threejs-graphics-optimizer

SKILL.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) {
    // 

...
Read full content

Repository Stats

Stars19
Forks1
LicenseOther