| name | dev-performance-mobile-optimization |
| description | Mobile-specific optimization for R3F/Three.js. Use when targeting mobile devices. |
| category | performance |
Mobile Optimization
Optimize R3F/Three.js for mobile devices with limited GPU/CPU.
When to Use
Use when:
- Targeting mobile devices
- FPS drops on mobile
- Memory issues on phones
Mobile vs Desktop Targets
| Feature | Desktop | Mobile |
|---|
| Pixel Ratio | 2.0 | 1.0-1.5 |
| Shadows | On | Off |
| Anti-aliasing | MSAA | Off |
| Post-processing | Full | Minimal |
| Draw calls | < 200 | < 50 |
| Polygons | < 1M | < 100K |
Mobile Detection
const config = useMemo(() => {
const isMobile = /iPhone|iPad|Android/i.test(navigator.userAgent);
return {
dpr: isMobile ? 1 : Math.min(window.devicePixelRatio, 2),
shadows: !isMobile,
antialias: !isMobile,
maxDrawCalls: isMobile ? 50 : 200,
};
}, []);
Canvas Configuration
<Canvas
dpr={config.dpr}
shadows={config.shadows}
gl={{
antialias: config.antialias,
powerPreference: 'high-performance',
}}
performance={{
min: 0.5,
max: 1,
debounce: 200,
}}
>
<Scene />
</Canvas>
Texture Optimization
const textureLoader = new THREE.TextureLoader();
textureLoader.load('/textures/ground.webp');
const MAX_TEXTURE_SIZE = isMobile ? 512 : 2048;
const gl = (canvas.getContext('webgl') || canvas.getContext('experimental-webgl'));
if (gl) {
gl.getExtension('WEBGL_compressed_texture_s3tc');
gl.getExtension('WEBGL_compressed_texture_astc');
}
Geometry Optimization
const MOBILE_SETTINGS = {
treePolygons: 500,
rockPolygons: 200,
characterPolygons: 2000,
};
<mesh>
{/* Mobile: 8 segments, Desktop: 32 */}
<sphereGeometry args={[1, isMobile ? 8 : 32, isMobile ? 6 : 32]} />
<meshStandardMaterial />
</mesh>
Lighting Optimization
<ambientLight intensity={isMobile ? 0.3 : 0.5} />
{isMobile ? (
<directionalLight position={[10, 10, 5]} intensity={0.8} />
) : (
<>
<directionalLight position={[10, 10, 5]} intensity={1} castShadow />
<pointLight position={[-10, 5, -5]} intensity={0.5} />
<hemisphereLight args={['#ffffff', '#444444', 0.6]} />
</>
)}
Material Optimization
<meshStandardMaterial
roughness={isMobile ? 0.8 : 0.5}
metalness={0}
color={isMobile ? '#8B4513' : undefined}
map={isMobile ? undefined : woodTexture}
/>
Post-Processing
import { EffectComposer, RenderPass } from '@react-three/postprocessing';
function PostProcessing({ isMobile }) {
if (isMobile) {
return null;
}
return (
<EffectComposer>
<RenderPass />
{/* Add effects for desktop only */}
<Bloom luminanceThreshold={0.5} intensity={0.5} />
</EffectComposer>
);
}
Frustum Culling
<mesh frustumCulled={true}>
<complexGeometry />
<meshStandardMaterial />
</mesh>
<mesh
frustumCulled={false} // Disable auto-culling
onPointerOver={(e) => {
// Custom culling logic
const distance = camera.position.distanceTo(e.object.position);
if (distance < 100) {
e.object.visible = true;
} else {
e.object.visible = false;
}
}}
>
Performance Monitoring
const TARGET_FPS = isMobile ? 30 : 60;
useFrame(() => {
const fps = 1 / clock.getDelta();
if (isMobile && fps < 25) {
setQualityLevel(Math.max(0.5, qualityLevel - 0.1));
}
});
Common Mobile Issues
| Issue | Cause | Solution |
|---|
| Overheating | Too many draw calls | Reduce instances, lower poly |
| Battery drain | 60 FPS on mobile | Cap at 30 FPS |
| Crashes | Memory leak | Dispose textures/geometries |
| Visual artifacts | Unsupported format | Use WebP, not PNG |
Checklist
Reference