threejs-scene-setup
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.
Overview
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.jsondepends onthreeand code doesimport * 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
- 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 underthree/addons/.... - Create the trio. A
Scene(root of the graph), aPerspectiveCamera(fov, aspect, near, far)moved back from the origin, and aWebGLRendererwhosedomElementis in the DOM. Set size andpixelRatio. - Add a mesh.
new Mesh(geometry, material)andscene.add(mesh). With a lit material you also need a light (seethreejs-materials-lighting). - Drive a render loop with
renderer.setAnimationLoop(fn). It's the modern, WebXR-/WebGPU-safe replacement for hand-rolledrequestAnimationFrame. Use aTimerfor delta time (Clockis deprecated since r183). - Handle resize so the camera aspect and renderer match the canvas; update
camera.aspect, callupdateProjectionMatrix(), andrenderer.setSize(...). - Add
OrbitControlsfor 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/[email protected]/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/[email protected]/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 loop4. 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; useMeshNormalMaterial/MeshBasicMaterialto verify geometry first. - Nothing animates → you never called
renderer.renderinside the loop, or you callsetAnimationLoopbut 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 = trueyou must callcontrols.update()every frame. - Old tutorials use
<script src="three.min.js">→ since r147 three.js ships ES modules only; usetype="module"+ import maps.
References
- For coordinate conventions, the scene-graph (
Group, parent/child transforms,Object3Dadd/remove),OrthographicCamerafor 2.5D, and disposing of geometries/materials/textures to avoid leaks, readreferences/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.
Files 2 files
Install it by hand
Deska runs this for you, for every agent you pick. To run it yourself in a terminal:
npx skills add gamedev-skills/awesome-gamedev-agent-skills --skill threejs-scene-setupMore from gamedev-skills
- game-feelgamedev-skillsAdd "juice" and game feel that makes actions satisfying — screen shake, hit-stop/freeze frames, tweened/eased motion, squash & stretch, knockback, and layered audio-visual feedback — as engine-neutral techniques that pair with the detected engine's tween, particle, and camera APIs. Use when the user mentions game feel, juice, "make it feel good/punchy", screen shake, hit stop, screen freeze, easing, squash and stretch, impact frames, or feedback/polish on hits, jumps, pickups, and deaths.5,900 installs on skills.sh0
- game-ui-uxgamedev-skillsDesign and build game UI/UX — HUDs, menus, and overlays — that survive every screen: anchor- based responsive layout, resolution/aspect scaling and safe areas, keyboard/gamepad focus navigation, a screen/menu state stack, and event-driven (not polled) HUD updates. Engine- neutral patterns that pair with the detected engine's UI skill. Use when the user mentions HUD, health bar, main menu, pause menu, settings screen, UI layout, anchors, UI scaling, aspect ratio, safe area, controller/keyboard menu navigation, or wiring UI to game state.5,900 installs on skills.sh0
- level-designgamedev-skillsDesign and build playable levels — the blockout/whitebox-to-playable workflow, player metrics and grid layout, pacing and flow (tension/rest curve), gating and the critical path, and encounter design. Engine-neutral practice. Use when the user mentions level design, blockout/whitebox/greybox, level layout, level pacing, encounter design, or the critical path through a level.4,700 installs on skills.sh0
- game-aigamedev-skillsDesign NPC and enemy decision-making with finite state machines, behavior trees, steering behaviors, and A* pathfinding — engine-neutral algorithms that pair with the detected engine's navigation API. Use when building enemy AI, an FSM or behavior tree, steering/flocking, or pathfinding, or when the user mentions state machine, behavior tree, blackboard, A*, navmesh, seek, or patrol/chase.4,500 installs on skills.sh0
- audio-designgamedev-skillsImplement game audio practice — bus/mixer architecture and gain in decibels, ducking (sidechain), adaptive/dynamic music via layering and re-sequencing, SFX variation, and beat synchronization. Engine-neutral. Use when the user mentions audio mixing, audio buses, adaptive/dynamic music, ducking, SFX variation, music layers, or syncing gameplay to the beat.4,400 installs on skills.sh0