performance

Keep a Three.js game at a smooth framerate as it grows — instancing, level-of-detail, object pooling, culling, draw-call and allocation discipline, and profiling with vitexec. Use when a game stutters or drops frames, or proactively when a scene has many objects, enemies, particles, or lights.

Skill file

Preview skill file
---
name: performance
description: "Keep a Three.js game at a smooth framerate as it grows — instancing, level-of-detail, object pooling, culling, draw-call and allocation discipline, and profiling with vitexec. Use when a game stutters or drops frames, or proactively when a scene has many objects, enemies, particles, or lights."
---

# Performance

A scene that runs fine with a few objects stutters once it has crowds, particles, and a big map. The framerate is set by three budgets — **draw calls**, **GPU work**, and **per-frame CPU/allocations** — and the levers below each target one. Measure first, then fix the actual bottleneck.

## Measure first
Don't guess. Profile the running game with `vitexec --performance-trace` (and `--cpu-profile` for hot functions). Read the verdict from where the frame time goes: long scripting/JS time per frame and a busy main thread mean **CPU-bound** (apply "less per-frame CPU"); a cheap main thread but high GPU/render time, many draw calls, or large overdraw mean **GPU-bound** (apply "less GPU work" / "fewer draw calls"). Fix the budget the trace actually points to, not the one you assumed.

## Fewer draw calls
Each distinct mesh is roughly one draw call; thousands of them stall the CPU submitting work.
- **Instancing**: render many copies of the same geometry (trees, rocks, grass, bullets, identical enemies) as one `InstancedMesh`. Position each copy with `mesh.setMatrixAt(i, matrix)` and then set `mesh.instanceMatrix.needsUpdate = true` — omit that flag and instances never move.
- **Merge static geometry**: combine non-moving meshes that share a material into one buffer geometry. The tradeoff: a merged mesh is culled all-or-nothing, so don't merge geometry spread across a large area that benefits from per-object frustum culling.
- **Share materials/textures**: reuse material and texture instances; an atlas lets different props share one draw call.

## Less GPU work
- **Cull**: leave `frustumCulled = true` (the default) so off-screen objects are skipped; for huge worlds, also cull by distance.
- **LOD**: swap distant objects for cheaper versions (`THREE.LOD`), or hide small distant detail entirely.
- **Overdraw**: cap large transparent/additive particles and full-screen effects; bound shadow-map size and the number of shadow-casting lights.

## Less per-frame CPU
- **Pool, don't allocate**: reuse objects, and reuse scratch `Vector3`/`Quaternion`/`Matrix4` instances instead of `new`-ing them inside the update loop — per-frame allocation causes GC hitches.
- **Cap dynamic counts**: bound the number of live enemies, particles, and projectiles; recycle from a pool rather than creating/destroying.
- **Don't trigger recompiles**: changing the light count or a material's defines at runtime recompiles shaders (see the `lights` skill). Keep them fixed.
- **Spread work**: amortize expensive systems (pathfinding, AI re-planning) across frames instead of doing all of it every frame.

Source

Creator's repository · drawcall-ai/skills

View on GitHub

Security

Security checks in progress
Results will appear here once audits complete
Checked by 3 independent security firms
Does it try to trick the AI?Not yet checkedPending · Gen Agent Trust Hub
Does it sneak in hidden code?Not yet checkedPending · Socket
Does it have known bugs?Not yet checkedPending · Snyk