One Draw Call, Massive Crowd - Performance Engineering in Three.js

I’ve had this in the oven for quite a while. My goal was to build a clean, scalable high-count instanced crowd system in pure Three.js and vanilla JS. At its core, this was a performance engineering problem.

Find Walter on CodePen →

My “Where’s Walter?” game (yes, you read that right, it is Walter, not Waldo) renders a massive animated crowd in a single draw call, while staying extremely fast. The core problem was balancing visual detail with scalability, lots of characters, all moving, without killing the CPU or GPU.

Here are the key ideas:

  • Everything’s instanced. All characters share one merged geometry and render in a single InstancedMesh. A small per-vertex ID lets the shader treat head, body, legs, etc. differently without separate meshes.
  • Data is packed aggressively. Character colors and flags are packed into a minimal set of instance attributes and reconstructed in the shader. Less bandwidth, fewer attributes, better scaling.
  • LOD happens in the shader. Detail scales by distance. Far characters are simplified directly in the vertex shader; fewer moving parts, cheaper lighting, even merging limbs when no one will notice.
  • Camera-aware optimization. If a character is facing away, the shader pushes it to a cheaper level of detail earlier. You can’t see facial features from behind, so why pay for them?
  • Work is amortized over frames. Not every character updates every frame. Movement and transforms are spread across frames so the CPU load stays flat while the crowd still looks smooth.
  • Expensive math is cached. Rotation trig and other repeated calculations are computed only when needed and reused elsewhere. This alone recovered ~25fps at scale. A good reminder that at high instance counts, “small” math adds up fast.
  • Far characters update less often. Close characters stay smooth; distant ones get cheaper updates because the difference isn’t perceptible.
  • Visual detail is procedural. Eyes, stripes, buttons, blinking → all done in shaders. No extra geometry, no extra draw calls.

The result is a fully playable crowd-search game that scales extremely well, but not infinitely. Eventually you hit GPU fill rate, memory bandwidth, or sheer triangle throughput. Hardware limits are still hardware limits.

If you want to test the limits, try Chaos mode, or go to Custom and increase the people count. On my rig, I’m getting max FPS (240) with sub-2 ms GPU and ~0.1 CPU up to 100,000 people.

Try it on CodePen →

If you’re building large-scale simulations or crowd systems, I hope some of these techniques will help.

4 Likes