Using InstancedMesh in Babylon.js to Render Thousands of Objects Efficiently
Learn how Babylon.js InstancedMesh cuts draw calls by sharing geometry and material across thousands of objects, with a concrete code example, limits, and verification steps.
09 Aug 2026, 16:02 UTC

Why InstancedMesh matters
InstancedMesh lets you draw many copies of the same geometry with a single draw call. Each instance keeps its own position, rotation, and scale, but the vertex data and material are shared. This keeps GPU work low even when you need thousands of objects.
Worked configuration
The following snippet creates a simple box and then generates 5 000 instances scattered in a cube. Run this code after you have a scene and an engine initialized (e.g., in a createScene function).
// 1. Create the source mesh that will be shared
const sourceBox = BABYLON.MeshBuilder.CreateBox('sourceBox', { size: 1 }, scene);
// Optional: hide the source so it isn’t rendered directly
sourceBox.setEnabled(false);
// 2. Generate instances
const instanceCount = 5000;
for (let i = 0; i < instanceCount; i++) {
const inst = new BABYLON.InstancedMesh(`inst_${i}`, sourceBox);
// Random position inside a 100‑unit cube
inst.position = new BABYLON.Vector3(
(Math.random() - 0.5) * 100,
(Math.random() - 0.5) * 100,
(Math.random() - 0.5) * 100
);
// Random rotation (optional)
inst.rotation = new BABYLON.Vector3(
Math.random() * Math.PI * 2,
Math.random() * Math.PI * 2,
Math.random() * Math.PI * 2
);
// Random scale (keep uniform for simplicity)
const scale = 0.5 + Math.random() * 1.5;
inst.scaling = new BABYLON.Vector3(scale, scale, scale);
// Disable features you don’t need to save CPU
inst.isPickable = false;
inst.checkCollisions = false;
}
// 3. Verify draw calls (run after the loop)
console.log('Draw calls after instancing:', engine.getDrawCalls());
// Expect a low number, typically 1‑2 for the box plus any other scene objects.
How the mechanism works
When Babylon.js creates an InstancedMesh, it tells the WebGL engine to reuse the same vertex and index buffers of the source mesh for every draw. The only per‑instance data sent to the GPU are the transformation matrices (position, rotation, scale). Because the geometry is not duplicated, the vertex shader runs once per vertex, and the instance ID is used to fetch the correct matrix from a uniform buffer or texture. This reduces the number of draw calls from N (one per mesh) to roughly 1 for the instanced set, regardless of N.
Limits you must respect
- Identical geometry and material: All instances share the exact same
sourceMeshand material. If you need different colors or texture offsets per instance, you must useThinInstancesor a custom shader that reads per‑instance attributes. - Updating transforms: Changing
position,rotation, orscalingeach frame is cheap because only the instance buffer is updated. Changing the source mesh’s geometry (e.g., callingCreateBoxagain) forces a rebuild of the shared buffers and breaks instancing. - Physics and picking:
InstancedMeshdoes not automatically create physics impostors. If you need per‑instance collision, you must manage a separate physics map or use the Havok plugin withThinInstances. Leaving picking enabled on thousands of instances can waste CPU cycles.
Common mistakes and how to avoid them
- Disposing the source mesh while instances still exist: This leaves WebGL with dangling buffer references and can cause errors like "GL_INVALID_OPERATION". Always dispose each
InstancedMeshfirst, then set the source mesh tonullor dispose it. - Assuming each instance can have its own material: Assigning a material to an
InstancedMeshinstance has no effect; the instance will render with the source mesh’s material, often appearing white if the material expects per‑instance data that isn’t supplied. UseThinInstancesor a shader attribute buffer for per‑instance material variations. - Forgetting to disable unnecessary features: Leaving
isPickableorcheckCollisionstrue on every instance forces the engine to run picking or collision tests for each object, negating the performance gain. Turn them off unless you truly need them.
Verification steps
To confirm that instancing is working as expected:
- Open the Babylon.js Inspector (
scene.debugLayer.show()) and look at the Scene Explorer. Select one of theInstancedMeshentries; itssourceMeshproperty should point to the original box. - Run
engine.getDrawCalls()before creating instances (should be higher if you have many separate meshes) and after the loop. The count should stay near the baseline (e.g., 1‑2 for the box plus any skybox or UI). - In the Inspector’s GPU timer view, increase the instance count from 100 to 5 000 and observe that the vertex processing time remains roughly constant while the CPU time for updating transforms grows linearly (which is expected and inexpensive).
When to consider alternatives
If you need per‑instance vertex data such as unique colors, morph targets, or texture atlases, look at ThinInstances (available in Babylon.js 5.0+) or write a custom shader that reads per‑instance attribute buffers. For heavy physics interactions, the Havok physics plugin works with ThinInstances to give each instance its own impostor while still keeping draw calls low.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.