--- name: scene-graph-planning description: Structuring a three.js scene graph so transforms, culling and disposal stay tractable as it grows. when_to_use: You are the scene designer laying out a three.js scene before objects are built. tags: [threejs, architecture] --- # The graph decides what is cheap later Scene-graph shape determines transform cost, culling effectiveness and whether teardown is possible. All three are painful to change once content exists. ## Group by what moves together, not by what looks similar Every `Object3D` with a dirty transform forces a matrix recomputation down its subtree. A graph grouped by *material* or by *asset file* means moving one object dirties unrelated branches. Grouped by motion, a static branch stays clean for the life of the scene. ``` world ├── static ← matrixAutoUpdate = false, set once │ ├── terrain │ └── props └── dynamic ├── player └── vehicles ``` `matrixAutoUpdate = false` on the static branch removes it from per-frame traversal entirely. This is usually the single largest CPU win in a scene with many objects, and it costs one line. ## Frustum culling works on bounding volumes, not intent Culling is per-`Mesh` against its bounding sphere. Two consequences: - **A merged mesh cannot be partially culled.** Merging 500 props into one draw call also means all 500 are drawn whenever any part is on screen. Merge by spatial locality, not by material alone. - **A wrong bounding volume silently misbehaves.** After deforming geometry, call `computeBoundingSphere()`, or the object pops out of view when its stale sphere leaves the frustum. ## Plan disposal with the graph WebGL resources are not garbage collected. Every geometry, material and texture needs an explicit `dispose()`. If ownership is not planned into the graph, a scene swap leaks GPU memory until the tab dies — see `threejs-perf-and-teardown`. The rule that makes this tractable: **one owner per resource**, recorded where it is created. A texture shared by twenty materials is disposed once, by the thing that loaded it, not by whichever material is torn down first. ## Depth is not free Deep hierarchies cost traversal on every frame. Prefer a shallow graph with explicit groups over mirroring an asset's exported nesting, which is usually an artefact of how it was modelled rather than how it behaves.