Three.js performance: smooth on every device
Years with Three.js taught me that smooth 3D scenes aren't a matter of raw power, but of draw calls, instancing, and clean disposal of resources.
A 3D scene that runs at 120 frames per second on my development machine tells me little. The honest question is: how does it behave on a client's three-year-old smartphone? From the projects of recent years I've learned that performance in Three.js is rarely a question of raw compute — it's one of restraint.
Fewer draw calls, more calm
The biggest lever is almost always draw calls. Every mesh drawn individually costs the CPU a round of conversation with the GPU. A thousand separate objects mean a thousand of those conversations per frame — and this is exactly where mobile devices break down.
Performance is rarely a single trick. It's the sum of many small decisions you account for from the start.
When I need the same geometry a hundred times over — trees, particles, building blocks — I reach for InstancedMesh. A hundred draw calls become one.
const mesh = new THREE.InstancedMesh(geometry, material, 1000);
const matrix = new THREE.Matrix4();
for (let i = 0; i < 1000; i++) {
matrix.setPosition(positions[i]);
mesh.setMatrixAt(i, matrix);
}
Where objects are static and don't need to move individually, I merge geometries instead — with mergeGeometries from BufferGeometryUtils, many meshes become one. Sharing material instances rather than constantly creating new ones saves noticeably too.
Cleaning up is part of the work
What many underestimate: Three.js doesn't clean up on its own. Geometries, materials, and textures live on in GPU memory until I explicitly release them with .dispose(). In single-page apps that swap scenes, I've seen memory leaks that crashed after minutes. Ever since, cleanup isn't an afterthought for me — it's part of every component.
For mobile devices I go a step further. I cap pixelRatio at 2, because feeding a retina display at factor 3 costs an enormous number of pixels. I detect weaker devices and reduce shadows, post-processing, and polygon count there. A scene doesn't have to look the same everywhere — it has to run smoothly everywhere.
In the end it's not about the maximum scene that runs somewhere. It's about the right scene that runs everywhere.