Three.js / R3F

Fix Three.js & React Three Fiber performance

Upload the model behind your scene to find what's tanking your frame rate.

Why teams trust it
Freeno sign-upPrivateruns in your browserInstantgrade in secondsBenchmarkedShopify · Quest · WebGL

When an R3F scene stutters, the model is usually the cause: too many draw calls, dense geometry, or textures that exhaust GPU memory. The analyzer surfaces these instantly so you stop guessing in the profiler.

The usual Three.js performance killers

Draw calls are the classic bottleneck — every separate material and mesh adds CPU overhead. Merging geometry that shares a material and using texture atlases is often the biggest single win.

After that it's geometry density and texture memory. The analyzer estimates draw calls and reports both, so you know where to focus before touching code.

From asset to 60fps

Most R3F performance work is asset work, not code. Get the model lean — fewer draw calls, compressed textures, sensible triangle counts — and instancing, frustum culling and the rest of your tuning suddenly pays off.

Done-for-you optimization

Want your scene's models optimized?

We'll rework the heavy assets in your Three.js / R3F project — draw calls, geometry, textures — so the scene hits its frame-rate target. Free review and quote.

Typically 70–90% smaller

Draco/Meshopt geometry, KTX2 textures and clean decimation — without visible quality loss.

Built for web & mobile

Optimized to load fast and render smoothly on phones, not just high-end desktops.

You keep the visuals

We protect silhouettes, UVs and materials so the model still looks like your model.

Grade your model firstFree quote · no commitment

The three causes of a slow Three.js or R3F scene

When a ThreeJS scene drops frames or performs poorly on mobile, the root cause is almost always the loaded model, not the render loop. Three.js and R3F are efficient renderers; they struggle when the assets they are given are too heavy. Before reaching for profiler counters or useFrame optimizations, inspect the model itself.

Draw calls are the first thing to measure. Every mesh with its own material generates a separate draw call — a CPU-to-GPU command with non-trivial overhead per frame. A single model exported from CAD or a sculpting tool can contain hundreds of meshes with individual materials, producing hundreds of draw calls per frame. On mobile, draw calls above ~50 become a significant CPU bottleneck regardless of GPU speed. Merge geometries that share a material using mergeGeometries from three-stdlib, and consider InstancedMesh for repeating objects to collapse many draw calls into one.

Uncompressed textures are the second lever. A PNG or JPEG texture must be fully decoded into GPU memory before it can be used. A single 4096×4096 RGBA texture consumes ~64 MB of VRAM — before mipmaps. Three or four maps of that size exhaust the VRAM budget on most mobile devices, causing texture loss, frame drops, or tab crashes. KTX2 (Basis Universal) solves this: the texture stays compressed natively on the GPU, reducing VRAM by 4–6×. Three.js ships a KTX2Loader that handles format transcoding automatically.

import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';

const ktx2Loader = new KTX2Loader()
  .setTranscoderPath('/basis/')
  .detectSupport(renderer);

const loader = new GLTFLoader();
loader.setKTX2Loader(ktx2Loader);

Draco compression: smaller geometry, faster parse

If the GLB uses Draco geometry compression — which it should, for any production asset — you need to configure the DRACOLoader on the GLTFLoader. Without it, Three.js cannot parse Draco-compressed meshes at all. With it, geometry that was 20 MB on disk loads as 3 MB and parses significantly faster.

The MeshGrade analyzer reports whether Draco and KTX2 compression are present in your GLB. If they are not, that is almost always the highest-impact change available. A model that goes from uncompressed to Draco + KTX2 typically drops from 20–40 MB to 2–5 MB with no visible quality change.

import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';

const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('/draco/');

const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);

Measure first — then optimize

Before writing any of the above, run your model through the free MeshGrade analyzer. It reports the exact draw call count, triangle and vertex totals, every texture's resolution and estimated VRAM cost, and whether Draco and KTX2 compression are present. A five-second grade tells you whether the bottleneck is draw calls, texture memory, or geometry density — so you invest your optimization time in the right place and skip changes that won't move the needle.

FAQ

Frequently asked questions

Why is my React Three Fiber scene slow?

Most often the loaded model: high draw calls, too many triangles, or large uncompressed textures. The analyzer measures all three from your GLB.

How many draw calls is too many?

For smooth mobile rendering, keep it under ~50 where you can. Hundreds of draw calls will bottleneck the CPU regardless of how fast your GPU is.