r3f-performance-optimization

star 1

Use when optimizing React Three Fiber scenes, experiencing frame drops, high draw calls, or implementing 3D performance features - ensures systematic performance analysis and applies proven optimization patterns for smooth 60fps rendering

nocksock By nocksock schedule Updated 3/18/2026

name: r3f-performance-optimization description: Use when optimizing React Three Fiber scenes, experiencing frame drops, high draw calls, or implementing 3D performance features - ensures systematic performance analysis and applies proven optimization patterns for smooth 60fps rendering

R3F Performance Optimization Skill

When This Skill Applies

Use this skill when:

  • Frame rates drop below 60fps in R3F scenes
  • Draw call count exceeds 500-1000
  • Implementing new 3D features that may impact performance
  • Scene contains many similar objects (>50 meshes)
  • Static scenes render continuously
  • Performance varies significantly across devices
  • Memory usage grows during scene interaction

Performance Optimization Checklist

When optimizing R3F performance, complete each step systematically:

☐ 1. Measure Current Performance

Required before any optimization:

import { Stats } from '@react-three/drei'
// or
import { Perf } from 'r3f-perf'

<Canvas>
  <Stats />  {/* Basic FPS/memory */}
  <Perf position="top-left" />  {/* Advanced: calls, triangles, textures */}
  <Scene />
</Canvas>

Record baseline metrics:

  • Current FPS (target: 60fps)
  • Draw call count (target: <500, max: 1000)
  • Triangle/vertex count
  • Memory usage patterns

☐ 2. Enable On-Demand Rendering

For static or mostly-static scenes:

<Canvas frameloop="demand">
  {/* Only renders when props change or invalidate() called */}
</Canvas>

With manual control:

const { invalidate } = useThree()

// Before animation starts
invalidate()
requestAnimationFrame(() => {
  // Synchronous animation
})

Impact: Can reduce CPU/GPU usage by 90%+ for static scenes.

☐ 3. Apply Instancing for Repeated Objects

When drawing >50 similar objects:

Replace individual meshes:

// BEFORE: 1000 draw calls
{objects.map(obj => <mesh key={obj.id} geometry={...} material={...} />)}

With InstancedMesh:

// AFTER: 1 draw call
<instancedMesh args={[geometry, material, 1000]}>
  {/* Manage transforms programmatically */}
</instancedMesh>

Impact: 100-1000x draw call reduction.

☐ 4. Implement Level of Detail (LOD)

For objects at varying distances:

import { Detailed } from '@react-three/drei'

<Detailed distances={[0, 10, 20]}>
  <HighPolyMesh />   {/* 0-10 units from camera */}
  <MediumPolyMesh /> {/* 10-20 units */}
  <LowPolyMesh />    {/* 20+ units */}
</Detailed>

Impact: Reduces vertex processing for distant objects without visible quality loss.

☐ 5. Reuse Geometries and Materials

Create once, reference many times:

// WRONG: Creates new geometry/material per mesh
function Cubes() {
  return cubes.map(cube => (
    <mesh>
      <boxGeometry />
      <meshStandardMaterial />
    </mesh>
  ))
}

// CORRECT: Shared resources
const sharedGeometry = new THREE.BoxGeometry()
const sharedMaterial = new THREE.MeshStandardMaterial()

function Cubes() {
  return cubes.map(cube => (
    <mesh geometry={sharedGeometry} material={sharedMaterial} />
  ))
}

Critical: Each unique geometry/material requires GPU compilation.

☐ 6. Use Delta-Based Animations

Ensure refresh-rate independence:

useFrame((state, delta) => {
  // WRONG: Fixed speed, varies by refresh rate
  mesh.current.rotation.x += 0.01

  // CORRECT: Same speed everywhere
  mesh.current.rotation.x += delta * rotationSpeed
})

Best Practice: Mutate directly in useFrame, don't trigger React re-renders.

☐ 7. Implement Movement Regression

For dynamic quality scaling:

const { performance } = useThree()

useFrame(() => {
  // Scale quality based on interaction
  const pixelRatio = performance.current * 2 // 0-1 range
  gl.setPixelRatio(pixelRatio)
})

// Trigger during interaction
const onInteraction = () => {
  performance.regress()
}

Impact: Maintains smoothness during movement, restores quality when static.

☐ 8. Add Adaptive Quality Monitoring

For device-specific optimization:

import { PerformanceMonitor } from '@react-three/drei'

<PerformanceMonitor
  onIncline={() => setQuality(q => Math.min(q + 1, 3))}
  onDecline={() => setQuality(q => Math.max(q - 1, 1))}
>
  <Scene quality={quality} />
</PerformanceMonitor>

Pattern: Adjust shadows, textures, effects, or instance counts based on FPS.

☐ 9. Optimize Asset Loading

Progressive loading strategy:

// Preload in background
useGLTF.preload('/heavy-model.glb')

// Nested Suspense for quality transitions
<Suspense fallback={<Placeholder />}>
  <Suspense fallback={<LowQualityModel />}>
    <HighQualityModel />
  </Suspense>
</Suspense>

Key: useLoader automatically caches identical URLs.

☐ 10. Use React 18 Concurrent Features

For heavy synchronous operations:

import { startTransition } from 'react'

startTransition(() => {
  // Heavy state updates distributed across frames
  setMassiveDataset(computeExpensiveValue())
})

Benchmark: Maintains ~60fps vs ~5fps without transition.

Common Performance Anti-Patterns

When reviewing code, actively look for and fix:

Creating resources in render

// WRONG
<mesh>
  <boxGeometry args={[1, 1, 1]} /> {/* Creates new geometry every render */}
</boxGeometry>

Excessive draw calls

  • More than 1000 individual meshes
  • Not using instancing for repeated objects

Continuous rendering for static scenes

  • No frameloop="demand" when scene is mostly static

Fixed animation values

useFrame(() => {
  mesh.rotation.x += 0.01 // WRONG: refresh-rate dependent
})

Missing LOD for varied distances

  • Same detail level for near and far objects

Blocking heavy operations

  • Synchronous operations without startTransition

Verification Steps

After applying optimizations, verify:

  1. FPS: Stable 60fps on target devices
  2. Draw calls: <500 (optimal), <1000 (max)
  3. Memory: No leaks during interaction
  4. Smoothness: No visible frame drops during animation
  5. Consistency: Performance across different refresh rates

When Optimizations Are Complete

Only mark optimization complete when:

  • ✅ Baseline metrics recorded
  • ✅ All applicable checklist items addressed
  • ✅ Verification shows stable 60fps
  • ✅ No performance anti-patterns remain
  • ✅ Monitoring tools (Stats/Perf) integrated

Additional Resources

For advanced patterns, consult:

Install via CLI
npx skills add https://github.com/nocksock/superpowers --skill r3f-performance-optimization
Repository Details
star Stars 1
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator