All skills
gamedev-skills avatar

/threejs-scene-setup

@a8320b7

Stand up a three.js scene: import maps and the three/addons path, the Scene/PerspectiveCamera/WebGLRenderer trio, the setAnimationLoop render loop, responsive resize, and OrbitControls. Use when starting or debugging a three.js app — when the user mentions three.js, THREE.Scene, WebGLRenderer, PerspectiveCamera, the render loop, resizing, or OrbitControls. For models use threejs-gltf-loading; for materials/lights use threejs-materials-lighting.

Use this Skill: https://skilld.dev/gh/gamedev-skills/awesome-gamedev-agent-skills/threejs-scene-setup

This session only. Nothing lands on disk.

referencesscene-graph.md

≈1k tokens on demand. Your agent reads this file only when SKILL.md points to it.

three.js scene graph, cameras & cleanup (r150+)

Depth behind the scene-setup skill: coordinate conventions, the Object3D hierarchy, the orthographic camera, and resource disposal.

Coordinates & conventions

  • Right-handed coordinate system: +X right, +Y up, +Z toward the viewer. A fresh PerspectiveCamera looks down -Z.
  • Rotations are in radians (except PerspectiveCamera's fov, which is degrees). Use THREE.MathUtils.degToRad(deg) when you think in degrees.
  • Units are arbitrary but be consistent. glTF models are authored in metres; pick a scale and stick to it across the project.

The Object3D hierarchy

Every visual thing (Mesh, Group, Camera, Light) extends Object3D and has position, rotation, quaternion, and scale. Children inherit their parent's transform.

import * as THREE from 'three';

const turret = new THREE.Group();         // empty transform node
turret.add(barrelMesh);                   // child, positioned relative to turret
scene.add(turret);
turret.rotation.y = Math.PI / 4;          // rotates the whole group

barrelMesh.removeFromParent();            // detach (r129+)
scene.add(barrelMesh);                    // re-parent to the scene root

Useful traversal/lookup helpers:

scene.getObjectByName('Player');          // first descendant with that .name
root.traverse((obj) => { /* visit every descendant */ });
obj.getWorldPosition(new THREE.Vector3()); // world-space position

Avoid scaling parents of physics/gameplay objects: non-unit parent scale compounds through children and makes world-space math (raycasts, distances) error-prone.

PerspectiveCamera vs OrthographicCamera

  • PerspectiveCamera(fov, aspect, near, far) — objects shrink with distance. Default for 3D. Keep near/far as tight as the scene allows; a huge far/near ratio wrecks depth precision (z-fighting).
  • OrthographicCamera(left, right, top, bottom, near, far) — no perspective; ideal for 2.5D, isometric, or CAD-like views. Size the frustum to the aspect:
const aspect = window.innerWidth / window.innerHeight;
const d = 5;
const cam = new THREE.OrthographicCamera(-d * aspect, d * aspect, d, -d, 0.1, 100);
// On resize, recompute left/right from the new aspect, then updateProjectionMatrix().

After changing any projection property (aspect, fov, frustum extents, zoom) call camera.updateProjectionMatrix().

Disposing of resources (avoiding leaks)

three.js cannot garbage-collect GPU memory for you. Removing an object from the scene frees nothing on the GPU; you must dispose geometries, materials, and textures explicitly.

function disposeObject(obj) {
  obj.traverse((node) => {
    if (node.geometry) node.geometry.dispose();
    const materials = Array.isArray(node.material) ? node.material : [node.material];
    for (const mat of materials) {
      if (!mat) continue;
      for (const key of Object.keys(mat)) {
        const value = mat[key];
        if (value && value.isTexture) value.dispose(); // map, normalMap, etc.
      }
      mat.dispose();
    }
  });
  obj.removeFromParent();
}

Also dispose render targets (renderTarget.dispose()) and, on full teardown, the renderer (renderer.dispose()). When swapping levels, dispose the old level's subtree before loading the next, or memory grows every transition.

Stopping and restarting the loop

renderer.setAnimationLoop(fn) starts the loop; setAnimationLoop(null) stops it (e.g. when the tab is hidden via the Page Visibility API, or a menu is open). This is also the loop required by WebXR — requestAnimationFrame does not drive XR frames.

Render-on-demand

For static scenes or editors, you don't need a continuous loop. Render only when something changes (input, controls' change event, a tween step):

let needsRender = true;
controls.addEventListener('change', () => { needsRender = true; });
renderer.setAnimationLoop(() => {
  if (!needsRender) return;
  needsRender = false;
  renderer.render(scene, camera);
});

This cuts GPU/battery use dramatically for non-animated content.

Source: SKILL.md on GitHub

No alerts5d3 checks · Risk SAFE
  • Gen Agent Trust Hub5d

    The skill provides standard patterns and documentation for setting up a Three.js scene. It uses official Three.js modules from a trusted CDN (JSDelivr) and follows industry best practices for rendering and resource management. No security risks were identified.

  • Socket5d

    No alerts

  • Snyk5d

    Risk: LOW · No issues

Signed by skilld at a8320b7. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 3 days ago.

Activeupdated last week

README badge

README badge for gamedev-skills/awesome-gamedev-agent-skills/threejs-scene-setup