threejs-graphics-optimizer
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.
What this skill does
# 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**
```javascript
// ❌ 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:
```javascript
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:
```javascript
// ❌ 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)
```javascript
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
```javascript
/**
* 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
```javascript
/**
* Configure renderer for mobile performance.
*/
function setupMobileOptimizations(renderer, scene, camera) {
const mobile = isMobile()
if (mobile) {
// Disable expensive features
renderer.shadowMap.enabled = false
renderer.antialias = false
// Lower pixel ratio
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 1.5))
// Simpler tone mapping
renderer.toneMapping = THREE.NoToneMapping
// Remove fog (expensive pixel shader)
scene.fog = null
// Reduce lights (expensive)
// Keep only 1-2 lights max on mobile
console.log('[Mobile] Performance optimizations applied')
} else {
// Desktop: enable high-quality features
renderer.shadowMap.enabled = true
renderer.shadowMap.type = THREE.PCFSoftShadowMap
renderer.antialias = true
renderer.toneMapping = THREE.ACESFilmicToneMapping
console.log('[Desktop] High-quality features enabled')
}
}
```
### Fallback Pattern
```javascript
/**
* Create geometry with fallback for low-end devices.
*/
export function createOptimizedGeometry(options = {}) {
const { size = 1, mobile = false } = options
if (mobile) {
// Simple geometry for mobile
return new THREE.SphereGeometry(size, 8, 8) // Low poly
} else {
// Detailed geometry for desktop
return new THREE.IcosahedronGeometry(size, 2) // High poly
}
}
// Usage
const mobile = isMobile()
const geometry = createOptimizedGeometry({ size: 1, mobile })
const material = new THREE.MeshBasicMaterial({ color: 0x00d9ff })
const mesh = new THREE.Mesh(geometry, material)
```
---
## Part 3: Render Loop Optimization
### Efficient Animation Loop
```javascript
class SceneManager {
constructor() {
this.clock = new THREE.Clock()
this.animationId = null
this.lastFrameTime = 0
this.fps = 60
this.frameInterval = 1000 / this.fps
}
/**
* Main render loop with delta time.
*/
animate() {
this.animationId = requestAnimationFrame(() => this.animate())
const now = performance.now()
const delta = now - this.lastFrameTime
// Throttle to target FPS if needed
if (delta < this.frameInterval) return
this.lastFrameTime = now - (delta % this.frameInterval)
// Update logic with delta
const deltaSeconds = this.clock.getDelta()
this.update(deltaSeconds)
// Render
this.renderer.render(this.scene, this.camera)
}
/**
* Update scene objects.
* @param {number} delta - Time since last frame (seconds)
*/
update(delta) {
// Update animations, physics, etc.
this.animatedObjects.forEach(obj => {
if (obj.update) obj.update(delta)
})
}
/**
* Cleanup and stop animation.
*/
dispose() {
if (this.animationId) {
cancelAnimationFrame(this.animationId)
}
}
}
```
### Conditional Rendering
```javascript
/**
* Only render when something changed (for static scenes).
*/
class ConditionalRenderer {
constructor(renderer, scene, camera) {
this.renderer = renderer
this.scene = scene
this.camera = camera
this.needsRender = true
}
/**
* Mark scene as Related in Image & Video
watch
IncludedWatch a video (URL or local path). Downloads with yt-dlp, extracts auto-scaled frames with ffmpeg, pulls the transcript from captions (or Whisper API fallback), and hands the result to Claude so it can answer questions about what's in the video.
physical-ai-defect-image-generation
IncludedUse when the user wants to orchestrate defect image generation, run associated setup, or handle outputs on OSMO. The Day 0 path handles cold-start with USD-to-ROI, image-edit augmentation, and AnomalyGen to create initial PCBA datasets. The Day 1 path performs inference and labeling on real images. This skill helps with first-time asset setup, creation of finetuning checkpoints, and configuring deployment. Trigger keywords: defect image generation, dig workflow, dig pipeline, defect image detection workflow, aoi pipeline, aoi anomalygen, usd2roi anomalygen, day 0 pcba, day 1 pcba, day 1 real-photo alignment, day 1 manual roi, metal surface anomaly, glass defect, anomalygen finetune, setup_pcb, setup_metal, setup_glass, setup_pretrained, dig setup, dig datasets, dig pretrained checkpoint, dig image-edit endpoint.
accelint-react-best-practices
IncludedReact performance optimization and best practices. ALWAYS use this skill when working with any React code - writing components, hooks, JSX; refactoring; optimizing re-renders, memoization, state management; reviewing for performance; fixing hydration mismatches; debugging infinite re-renders, stale closures, input focus loss, animations restarting; preventing remounting; implementing transitions, lazy initialization, effect dependencies. Even simple React tasks benefit from these patterns. Covers React 19+ (useEffectEvent, Activity, ref props). Triggers - useEffect, useState, useMemo, useCallback, memo, inline components, nested components, components inside components, re-render, performance, hydration, SSR, Next.js, useDeferredValue, combined hooks.
elevenlabs-agents
IncludedBuild conversational AI voice agents with ElevenLabs Platform using React, JavaScript, React Native, or Swift SDKs. Configure agents, tools (client/server/MCP), RAG knowledge bases, multi-voice, and Scribe real-time STT. Use when: building voice chat interfaces, implementing AI phone agents with Twilio, configuring agent workflows or tools, adding RAG knowledge bases, testing with CLI "agents as code", or troubleshooting deprecated @11labs packages, Android audio cutoff, CSP violations, dynamic variables, or WebRTC config. Keywords: ElevenLabs Agents, ElevenLabs voice agents, AI voice agents, conversational AI, @elevenlabs/react, @elevenlabs/client, @elevenlabs/react-native, @elevenlabs/elevenlabs-js, @elevenlabs/agents-cli, elevenlabs SDK, voice AI, TTS, text-to-speech, ASR, speech recognition, turn-taking model, WebRTC voice, WebSocket voice, ElevenLabs conversation, agent system prompt, agent tools, agent knowledge base, RAG voice agents, multi-voice agents, pronunciation dictionary, voice speed control, elevenlabs scribe, @11labs deprecated, Android audio cutoff, CSP violation elevenlabs, dynamic variables elevenlabs, case-sensitive tool names, webhook authentication
humanizer
IncludedHumanize AI-generated text by detecting and removing patterns typical of LLM output. Rewrites text to sound natural, specific, and human. Uses 28 pattern detectors, 560+ AI vocabulary terms across 3 tiers, and statistical analysis (burstiness, type-token ratio, readability) for comprehensive detection. Use when asked to humanize text, de-AI writing, make content sound more natural/human, review writing for AI patterns, score text for AI detection, or improve AI-generated drafts. Covers content, language, style, communication, and filler categories.
generating-mermaid-diagrams
IncludedSalesforce architecture diagrams using Mermaid with ASCII fallback. Use this skill when generating text-based diagrams for Salesforce architecture, OAuth flows, ERDs, integration sequences, or Agentforce structure. TRIGGER when: user says "diagram", "visualize", "ERD", or asks for sequence diagrams, flowcharts, class diagrams, or architecture visualizations in Mermaid. DO NOT TRIGGER when: user wants PNG/SVG image output (use generating-visual-diagrams), or asks about non-Salesforce systems.