PluginBench
Skill
Pass
Audit score 90

threejs-scene-setup

gamedev-skills/awesome-gamedev-agent-skills

Bootstrap a three.js scene with camera, renderer, animation loop, and OrbitControls.

What is threejs-scene-setup?

Sets up the foundational three.js scene: ES module loading with import maps, the Scene/Camera/Renderer trio, a render loop with Timer, responsive resizing, and orbit camera controls. Use this when starting a three.js project or fixing a blank canvas.

  • Create a WebGL renderer, scene, and perspective camera with proper sizing and pixel ratio
  • Establish a frame-rate-independent render loop using setAnimationLoop and Timer
  • Handle window resize to keep the camera aspect and renderer in sync
  • Add OrbitControls for interactive orbit, pan, and zoom camera movement
  • Configure ES module imports for three.js and addons via import maps or bundlers

How to install threejs-scene-setup

npx skills add https://github.com/gamedev-skills/awesome-gamedev-agent-skills --skill threejs-scene-setup
Prerequisites
  • Node.js and npm (if using a bundler like Vite or webpack)
  • A browser with WebGL support
  • Basic familiarity with ES modules and JavaScript
Claude Code
Cursor
Windsurf
Cline

How to use threejs-scene-setup

  1. 1.Set up an import map in HTML (or use a bundler) to resolve 'three' and 'three/addons/' specifiers
  2. 2.Create a WebGLRenderer, Scene, and PerspectiveCamera, then add them to the DOM
  3. 3.Position the camera away from the origin and add a test mesh (e.g., a cube with MeshNormalMaterial)
  4. 4.Implement the render loop using renderer.setAnimationLoop() with a Timer for delta time
  5. 5.Add a resize listener to update camera.aspect, call updateProjectionMatrix(), and renderer.setSize()
  6. 6.Import and instantiate OrbitControls, enable damping, and call controls.update() each frame

Use cases

Good for
  • Bootstrap a new three.js project from scratch with all core components
  • Fix a blank or black canvas by verifying camera position and lighting setup
  • Make a canvas responsive to window resizing without distortion
  • Add interactive camera controls to an existing three.js scene for development
  • Debug three.js apps by confirming the render loop and scene graph are working
Who it's for
  • Game developers building 3D web experiences
  • 3D visualization engineers setting up interactive scenes
  • Web developers new to three.js who need a working foundation
  • Technical artists prototyping 3D concepts in the browser

threejs-scene-setup FAQ

When should I use this skill vs. threejs-materials-lighting or threejs-gltf-loading?

Use this skill to set up the scene foundation. Use threejs-materials-lighting for lights, shadows, and PBR materials; use threejs-gltf-loading for loading .gltf/.glb models and animations.

Why do I get 'Failed to resolve module specifier "three"'?

You are missing an import map or bundler configuration. Add an import map in HTML with both 'three' and 'three/addons/' (note the trailing slash on addons), or use npm install three with a bundler like Vite.

My canvas is black and nothing shows. What's wrong?

The camera is likely at the origin (inside or behind your object), or you used a lit material without a light. Move the camera back with camera.position.set(...), or use MeshNormalMaterial to test geometry without lighting.

How do I make the canvas responsive without stretching?

On window resize, update camera.aspect, call camera.updateProjectionMatrix(), and then renderer.setSize(). Skipping updateProjectionMatrix() causes squashing or stretching.

OrbitControls feels unresponsive. How do I fix it?

If you enabled damping (enableDamping = true), you must call controls.update() every frame inside the render loop.

Full instructions (SKILL.md)

Source of truth, from gamedev-skills/awesome-gamedev-agent-skills.


name: threejs-scene-setup description: > 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.

three.js Scene Setup

Create the foundation of a three.js app: module loading, the scene/camera/renderer trio, the render loop, responsive resizing, and camera controls. Patterns target r186. Read the installed three version before changing an existing project because examples and addons move across releases.

When to use

  • Use when bootstrapping a three.js scene, fixing a blank/black canvas, making the canvas responsive, setting up the animation loop, or adding OrbitControls.
  • Use when package.json depends on three and code does import * as THREE from 'three'.

When not to use: loading .gltf/.glb models or skinned animation → threejs-gltf-loading. Materials, lights, shadows, environment maps → threejs-materials-lighting. 2D rendering → pixijs-rendering.

Core workflow

  1. Load three.js as an ES module with an import map. Since r147 the bare specifier 'three' and 'three/addons/' must be mapped (in HTML or by a bundler). Addons (controls, loaders) live under three/addons/....
  2. Create the trio. A Scene (root of the graph), a PerspectiveCamera(fov, aspect, near, far) moved back from the origin, and a WebGLRenderer whose domElement is in the DOM. Set size and pixelRatio.
  3. Add a mesh. new Mesh(geometry, material) and scene.add(mesh). With a lit material you also need a light (see threejs-materials-lighting).
  4. Drive a render loop with renderer.setAnimationLoop(fn). It's the modern, WebXR-/WebGPU-safe replacement for hand-rolled requestAnimationFrame. Use a Timer for delta time (Clock is deprecated since r183).
  5. Handle resize so the camera aspect and renderer match the canvas; update camera.aspect, call updateProjectionMatrix(), and renderer.setSize(...).
  6. Add OrbitControls for orbit/pan/zoom while developing. Confirm something actually renders (a lit cube, the controls responding) before assuming success.

Patterns

1. HTML import map + module entry (no bundler)

<canvas id="c"></canvas>
<script type="importmap">
{
  "imports": {
    "three": "https://cdn.jsdelivr.net/npm/three@0.186.0/build/three.module.js",
    "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.186.0/examples/jsm/"
  }
}
</script>
<script type="module" src="./main.js"></script>

With a bundler (Vite/webpack), skip the import map and just npm i three; the same import statements resolve.

2. Scene + camera + renderer

// main.js
import * as THREE from 'three';

const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); // cap for perf
renderer.setSize(window.innerWidth, window.innerHeight);

const scene = new THREE.Scene();
scene.background = new THREE.Color(0x101018);

const camera = new THREE.PerspectiveCamera(
  60,                                   // vertical field of view (degrees)
  window.innerWidth / window.innerHeight, // aspect
  0.1,                                  // near
  100                                   // far
);
camera.position.set(3, 2, 5);
camera.lookAt(0, 0, 0);

const cube = new THREE.Mesh(
  new THREE.BoxGeometry(1, 1, 1),
  new THREE.MeshNormalMaterial()        // unlit; shows orientation without a light
);
scene.add(cube);

3. The render loop (setAnimationLoop + Timer)

const timer = new THREE.Timer();        // replaces the deprecated THREE.Clock (r183+)
timer.connect(document);                // optional: no huge delta after a hidden tab

renderer.setAnimationLoop((time) => {
  timer.update(time);                   // call once per frame, before getDelta()
  const dt = timer.getDelta();          // seconds since last frame
  cube.rotation.x += dt;                // frame-rate independent
  cube.rotation.y += dt * 0.7;
  renderer.render(scene, camera);
});
// renderer.setAnimationLoop(null); // stop the loop

4. Responsive resize

function onResize() {
  const w = window.innerWidth, h = window.innerHeight;
  camera.aspect = w / h;
  camera.updateProjectionMatrix();      // required after changing aspect
  renderer.setSize(w, h);
}
window.addEventListener('resize', onResize);

5. OrbitControls (orbit / pan / zoom)

import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;          // inertial feel
controls.target.set(0, 0, 0);

renderer.setAnimationLoop(() => {
  controls.update();                    // needed every frame when damping is on
  renderer.render(scene, camera);
});

Pitfalls

  • Failed to resolve module specifier "three" → missing import map (or bundler config). Map both "three" and "three/addons/"; addon paths must end with /.
  • Black canvas, no errors → camera is at the origin (inside/behind the object), or you used a lit material (MeshStandardMaterial) with no light. Move the camera back; use MeshNormalMaterial/MeshBasicMaterial to verify geometry first.
  • Nothing animates → you never called renderer.render inside the loop, or you call setAnimationLoop but render outside it.
  • Stretched / squashed view on resize → you resized the renderer but didn't update camera.aspect + updateProjectionMatrix().
  • Blurry or jagged on HiDPI → set renderer.setPixelRatio(...); cap it (≈2) so 4K/retina screens don't tank performance.
  • OrbitControls feel dead → with enableDamping = true you must call controls.update() every frame.
  • Old tutorials use <script src="three.min.js"> → since r147 three.js ships ES modules only; use type="module" + import maps.

References

  • For coordinate conventions, the scene-graph (Group, parent/child transforms, Object3D add/remove), OrthographicCamera for 2.5D, and disposing of geometries/materials/textures to avoid leaks, read references/scene-graph.md.

Related skills

  • threejs-materials-lighting — give surfaces a lit look (lights, shadows, PBR).
  • threejs-gltf-loading — load 3D models and play their animations.
  • pixijs-rendering — 2D rendering in the browser.
  • fps-shooter — a 3D genre template that composes three.js skills.