Skip to content
Three.js Basics Explained — Complete Beginner's Guide to Scene, Camera & Renderer

Three.js Basics Explained — Complete Beginner's Guide to Scene, Camera & Renderer

DodaTech Updated Jun 6, 2026 13 min read

Three.js is a cross-browser JavaScript library that makes 3D graphics in the browser accessible by abstracting the low-level WebGL API into a clean, object-oriented scene graph.

What You’ll Learn

By the end of this guide, you’ll understand how Three.js works under the hood, set up your first 3D scene with a cube, configure cameras and renderers, handle window resizing, add a stats panel, and build a mini solar system — all while learning why each piece exists.

Why Three.js Matters

Three.js powers everything from product configurators to data visualization dashboards. At DodaTech, we use Three.js in Durga Antivirus Pro to render real-time 3D threat visualization dashboards, showing malware propagation patterns across network nodes in an interactive 3D space. This is the foundation you need to build experiences like that.

    flowchart LR
    A[HTML + JS Setup] --> B[Scene = Container]
    B --> C[Camera = Viewpoint]
    C --> D[Renderer = Painter]
    D --> E[Objects = Meshes]
    E --> F[Lights]
    F --> G[Animation Loop]
    G --> H[Your 3D App!]
    style G fill:#44aa88,color:#fff,stroke:none
  
🎯
Prerequisites: Basic JavaScript (variables, functions, objects) and HTML fundamentals. No 3D math or WebGL experience needed — we’ll explain everything.

Three.js Architecture — The Stage Analogy

Think of a Three.js scene like a theatre production:

  • Scene = the stage. Everything lives here — actors, props, lights.
  • Camera = your eyes. Where you stand and what you see.
  • Renderer = the painter. It takes what the camera sees and draws it on the canvas (the screen).
  • Mesh (Geometry + Material) = an actor. The geometry is their body shape; the material is their costume.
  • Light = stage lighting. Without it, you can’t see the actors.

Every Three.js app follows this same pattern: create a scene, add a camera, create objects, add lights, then loop and render.

Installation — Three Ways

CDN (Simplest for Learning)

Add this to your HTML <head>:

<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>

The THREE global is now available.

Import Map (Modern — Recommended)

<script type="importmap">
{
    "imports": {
        "three": "https://cdn.jsdelivr.net/npm/three@0.170.0/build/three.module.js"
    }
}
</script>
<script type="module">
import * as THREE from "three";
</script>

Why import maps? They let you use import statements without a bundler. The browser resolves "three" to the CDN URL automatically. This is how all our examples work.

npm (For Real Projects)

npm install three
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";

Use this when you’re building with Webpack, Vite, or other bundlers.

Hello Cube — Your First 3D Scene Explained Line by Line

Let’s walk through every line of a complete Three.js scene. Don’t skim — each line has a purpose.

<!DOCTYPE html>
<html>
<head>
    <title>Hello Cube — Your First Three.js Scene</title>
    <style>
        /* Remove margin and hide scrollbars so the canvas fills the screen */
        body { margin: 0; overflow: hidden; }
    </style>
</head>
<body>
    <!-- Import map tells the browser where to find the "three" module -->
    <script type="importmap">
    {
        "imports": {
            "three": "https://cdn.jsdelivr.net/npm/three@0.170.0/build/three.module.js"
        }
    }
    </script>
    <script type="module">
        import * as THREE from "three";

        // 1. SCENE — the container that holds everything
        var scene = new THREE.Scene();
        scene.background = new THREE.Color(0x222222);  // Dark gray background

        // 2. CAMERA — the viewpoint. PerspectiveCamera mimics human eyes:
        // things farther away appear smaller.
        var camera = new THREE.PerspectiveCamera(
            75,                                     // Field of view (degrees). 50-75 is typical.
            window.innerWidth / window.innerHeight, // Aspect ratio (width/height). Must match canvas.
            0.1,                                    // Near clipping plane. Closer than this is not rendered.
            1000                                    // Far clipping plane. Farther than this is culled.
        );
        camera.position.set(2, 2, 5);  // Move camera back and to the right
        camera.lookAt(0, 0, 0);        // Point the camera at the origin

        // 3. RENDERER — the painter that turns 3D into 2D pixels on screen
        var renderer = new THREE.WebGLRenderer({ antialias: true }); // antialias smooths edges
        renderer.setSize(window.innerWidth, window.innerHeight);     // Match canvas to window
        renderer.setPixelRatio(window.devicePixelRatio);             // Sharp on retina displays
        document.body.appendChild(renderer.domElement);              // Add canvas to the page

        // 4. OBJECT — a mesh is geometry (shape) + material (appearance)
        var geometry = new THREE.BoxGeometry(1, 1, 1);  // A 1x1x1 cube
        var material = new THREE.MeshStandardMaterial({ // Physically-based material (needs light!)
            color: 0x44aa88,     // Teal color
            roughness: 0.3,       // How rough the surface is (0 = mirror, 1 = matte)
            metalness: 0.1       // How metallic (0 = non-metal, 1 = metal)
        });
        var cube = new THREE.Mesh(geometry, material);  // Combine shape + appearance
        scene.add(cube);                                 // Place the cube on the stage

        // 5. LIGHTS — MeshStandardMaterial is dark without light
        var ambientLight = new THREE.AmbientLight(0xffffff, 0.5);  // Soft fill light
        scene.add(ambientLight);

        var directionalLight = new THREE.DirectionalLight(0xffffff, 1);  // Sun-like light
        directionalLight.position.set(5, 10, 7);  // Position the light source
        scene.add(directionalLight);

        // 6. ANIMATION LOOP — runs ~60 times per second
        function animate() {
            requestAnimationFrame(animate);  // Schedule the next frame

            // Rotate the cube a tiny amount each frame
            cube.rotation.x += 0.01;
            cube.rotation.y += 0.01;

            renderer.render(scene, camera);  // Draw the scene from the camera's perspective
        }
        animate();  // Start the loop

        // 7. RESIZE HANDLER — keeps the canvas looking right when the window changes size
        window.addEventListener("resize", function() {
            camera.aspect = window.innerWidth / window.innerHeight;  // Update aspect ratio
            camera.updateProjectionMatrix();  // Tell the camera to recalculate
            renderer.setSize(window.innerWidth, window.innerHeight);  // Resize the canvas
        });
    </script>
</body>
</html>

Expected output: A teal cube rotating in space against a dark gray background.

Scene Deep Dive

var scene = new THREE.Scene();
scene.background = new THREE.Color(0x888888);      // Set background color
scene.fog = new THREE.Fog(0x888888, 10, 50);        // Fog starts at distance 10, fully opaque at 50
scene.fog = new THREE.FogExp2(0x888888, 0.01);      // Exponential fog (denser, more natural)

scene.add(object);       // Add any object to the scene
scene.remove(object);    // Remove it
scene.children;          // Array of everything in the scene

Why fog? Fog hides the edge of your world. If objects suddenly pop out of existence at distance 50, that’s jarring. Fog makes them fade out gradually.

Camera Deep Dive

PerspectiveCamera (Realistic — Use This Most Often)

var camera = new THREE.PerspectiveCamera(
    fov,          // Field of view in degrees (default 75). Higher = wider = fish-eye effect.
    aspect,       // Width / height. Must match canvas or objects look stretched.
    near,         // Near clipping plane (default 0.1). Don't set this too small.
    far           // Far clipping plane (default 1000). Don't set this too large.
);

camera.position.set(x, y, z);    // Move the camera in 3D space
camera.lookAt(x, y, z);          // Point the camera at a specific spot
camera.zoom = 1;                 // Zoom factor (1 = normal, 2 = 2x zoom)
camera.fov = 75;                 // Change FOV at runtime
camera.updateProjectionMatrix(); // ALWAYS call this after changing fov, aspect, near, or far

OrthographicCamera (Isometric / 2D)

Objects stay the same size regardless of distance. Great for 2D games, CAD tools, and isometric views.

var frustumSize = 10;
var aspect = window.innerWidth / window.innerHeight;

var camera = new THREE.OrthographicCamera(
    -frustumSize * aspect / 2,   // left
    frustumSize * aspect / 2,    // right
    frustumSize / 2,             // top
    -frustumSize / 2,            // bottom
    0.1,                         // near
    1000                         // far
);

When to use which? Use PerspectiveCamera for anything realistic — games, product viewers, architectural walkthroughs. Use OrthographicCamera for UI, 2D games, or when you need precise measurements (CAD).

Renderer Deep Dive

var renderer = new THREE.WebGLRenderer({
    antialias: true,                // Smooth jagged edges (slight performance cost)
    alpha: true,                    // Transparent background (for overlays)
    powerPreference: "high-performance"  // Prefer dedicated GPU
});

renderer.setSize(width, height);                              // Canvas resolution
renderer.setPixelRatio(window.devicePixelRatio);              // Sharp on retina
renderer.setClearColor(0x222222, 1);                          // Background clear color
renderer.shadowMap.enabled = true;                            // Enable shadows
renderer.shadowMap.type = THREE.PCFSoftShadowMap;             // Soft shadow quality
renderer.toneMapping = THREE.ACESFilmicToneMapping;           // Film-like color response
renderer.toneMappingExposure = 1.0;                           // Brightness adjustment

document.body.appendChild(renderer.domElement);               // Add <canvas> to page

The Animation Loop — Why requestAnimationFrame?

function animate() {
    requestAnimationFrame(animate);  // Browser calls this before next paint (~60fps)
    controls.update();               // Update user controls (if any)
    renderer.render(scene, camera);  // Draw the frame
}
animate();

Why not setInterval? requestAnimationFrame pauses when the tab is hidden (saving battery/CPU), syncs with the monitor’s refresh rate, and gives smoother animation. setInterval keeps firing even when nobody’s watching.

Clock for Time-Based Animation

var clock = new THREE.Clock();

function animate() {
    var delta = clock.getDelta();         // Seconds since last frame (~0.016 at 60fps)
    var elapsed = clock.getElapsedTime(); // Total seconds since clock started

    cube.position.y = Math.sin(elapsed) * 2;  // Bob up and down (2 units amplitude)
    cube.rotation.x = elapsed * 0.5;           // Rotate at 0.5 radians per second

    renderer.render(scene, camera);
    requestAnimationFrame(animate);
}

Why delta time matters: Without it, a fast computer spins the cube faster than a slow one. Multiplying movement by delta makes speed consistent across all devices. Always use delta.

Debug & Stats

import Stats from "three/addons/libs/stats.module.js";

var stats = new Stats();
stats.showPanel(0);  // 0: FPS, 1: MS (frame time), 2: MB (memory)
document.body.appendChild(stats.dom);

function animate() {
    stats.begin();
    // ... your render code ...
    stats.end();
    requestAnimationFrame(animate);
}

Dat.GUI for Runtime Controls

import { GUI } from "three/addons/libs/lil-gui.module.min.js";

var gui = new GUI({ width: 300 });
gui.add(cube.position, "x", -5, 5).name("Position X");
gui.add(cube.material, "roughness", 0, 1).name("Roughness");
gui.add(cube.material, "metalness", 0, 1).name("Metalness");
gui.addColor(cube.material, "color").name("Color");

Common Mistakes

1. Camera at origin (inside the cube)

The camera defaults to (0, 0, 0) — the same position as your cube. You’re inside it. Always set camera.position.set() before expecting to see anything.

2. MeshStandardMaterial with no lights

MeshStandardMaterial and MeshPhysicalMaterial require lights to be visible. Without them, objects render pure black. Use MeshBasicMaterial (unlit) for testing, or always add at least an AmbientLight.

3. Forgetting updateProjectionMatrix() on resize

If you change camera.aspect and don’t call camera.updateProjectionMatrix(), the rendered image stays distorted. This is the #1 resize bug.

4. Using setInterval instead of requestAnimationFrame

setInterval doesn’t pause when the tab is hidden, doesn’t sync with refresh rate, and can queue overlapping callbacks. Always use requestAnimationFrame.

5. Not disposing materials and geometries

Every new THREE.Mesh() allocates GPU memory. When removing objects, call geometry.dispose() and material.dispose() to avoid memory leaks — especially in dynamic scenes.

6. Ignoring setPixelRatio

Without renderer.setPixelRatio(), the canvas renders at the CSS size’s pixel resolution. On retina displays (2x, 3x), the image looks blurry. Always set it — but cap it with Math.min(devicePixelRatio, 2) for performance.

7. Forgetting that Three.js uses a right-handed coordinate system

X = right, Y = up, Z = toward the viewer. If your objects appear mirrored or flipped, double-check your coordinate axes.

Practice Questions

Q1: What are the three essential components of every Three.js scene? A: Scene (container), Camera (viewpoint), and Renderer (draws to canvas).

Q2: Why does MeshStandardMaterial render black without lights? A: It uses physically-based rendering (PBR) which simulates how light interacts with surfaces. No light = no illumination = black. Add an AmbientLight or use MeshBasicMaterial for unlit objects.

Q3: What happens if you don’t call camera.updateProjectionMatrix() after changing the aspect ratio? A: The rendered image stays distorted because the camera’s internal projection matrix still uses the old aspect ratio. The view will look stretched or squashed.

Q4: Why should you use requestAnimationFrame instead of setInterval for animation? A: rAF pauses when the tab is hidden, syncs with the display refresh rate, and prevents overlapping callbacks — giving smoother, more efficient animation.

Q5: What is delta time and why is it important? A: Delta time is the seconds elapsed between frames. Multiplying movement by delta ensures consistent speed across different frame rates (a 30fps machine moves objects the same distance per second as a 120fps machine).

Challenge: Modify the Hello Cube example to create a scene with 3 cubes arranged in a triangle, each rotating at a different speed. One should use BasicMaterial (no light needed), one StandardMaterial with a light, and one PhysicalMaterial with transparency. Hint: use transparent: true and opacity: 0.5 on the physical material.

FAQ

What is the difference between WebGL and Three.js?
WebGL is a low-level browser API that draws triangles on a canvas. Three.js is a high-level library that makes WebGL accessible — you create objects, lights, and cameras instead of writing shader code and managing buffers.
Do I need to know WebGL to use Three.js?
No. Three.js handles all WebGL details internally. You can build complex 3D scenes without writing a single line of shader code. However, understanding the WebGL basics helps when debugging performance.
What is the animation loop for?
The animation loop runs continuously (targeting 60fps) to update object positions, process user input, and re-render the scene. Without it, you’d see a static snapshot — a 3D screenshot.
How do I handle window resizing?
Listen to the resize event on window, update camera.aspect to match the new ratio, call camera.updateProjectionMatrix(), and then call renderer.setSize() with the new dimensions.
What coordinate system does Three.js use?
Right-handed: X = right, Y = up, Z = toward the viewer. This matches most 3D modeling software and physics engines.

Try It Yourself

Copy this complete HTML file, save it as hello-threejs.html, and open it in a browser. No server needed.

<!DOCTYPE html>
<html>
<head>
    <title>Hello Three.js — Your First 3D Scene</title>
    <style>
        body { margin: 0; overflow: hidden; background: #000; }
        #info { position: absolute; top: 10px; left: 10px; color: white;
            font-family: sans-serif; font-size: 14px; }
    </style>
</head>
<body>
    <div id="info">🎯 Your First Three.js Scene</div>

    <script type="importmap">
    {
        "imports": {
            "three": "https://cdn.jsdelivr.net/npm/three@0.170.0/build/three.module.js"
        }
    }
    </script>
    <script type="module">
        import * as THREE from "three";

        var scene = new THREE.Scene();
        scene.background = new THREE.Color(0x111122);

        var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
        camera.position.set(0, 5, 15);
        camera.lookAt(0, 0, 0);

        var renderer = new THREE.WebGLRenderer({ antialias: true });
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.setPixelRatio(window.devicePixelRatio);
        document.body.appendChild(renderer.domElement);

        // Sun
        var sunGeo = new THREE.SphereGeometry(1.5, 32, 32);
        var sunMat = new THREE.MeshStandardMaterial({
            color: 0xffaa00, emissive: 0xff5500, emissiveIntensity: 0.5
        });
        var sun = new THREE.Mesh(sunGeo, sunMat);
        scene.add(sun);

        // Earth
        var earthGeo = new THREE.SphereGeometry(0.6, 32, 32);
        var earthMat = new THREE.MeshStandardMaterial({ color: 0x4488ff, roughness: 0.5 });
        var earth = new THREE.Mesh(earthGeo, earthMat);
        scene.add(earth);

        // Moon
        var moonGeo = new THREE.SphereGeometry(0.2, 16, 16);
        var moonMat = new THREE.MeshStandardMaterial({ color: 0xcccccc });
        var moon = new THREE.Mesh(moonGeo, moonMat);
        scene.add(moon);

        // Lights
        var ambient = new THREE.AmbientLight(0x222244);
        scene.add(ambient);
        var sunLight = new THREE.PointLight(0xffffff, 2, 30);
        sunLight.position.set(0, 0, 0);
        scene.add(sunLight);

        // Star field
        var starsGeo = new THREE.BufferGeometry();
        var starPositions = new Float32Array(3000);
        for (var i = 0; i < 3000; i++) {
            starPositions[i] = (Math.random() - 0.5) * 200;
        }
        starsGeo.setAttribute("position", new THREE.BufferAttribute(starPositions, 3));
        var starsMat = new THREE.PointsMaterial({ color: 0xffffff, size: 0.2 });
        var stars = new THREE.Points(starsGeo, starsMat);
        scene.add(stars);

        // Orbit path
        var orbitPoints = [];
        for (var i = 0; i <= 64; i++) {
            var angle = (i / 64) * Math.PI * 2;
            orbitPoints.push(new THREE.Vector3(Math.cos(angle) * 4, 0, Math.sin(angle) * 4));
        }
        var orbitGeo = new THREE.BufferGeometry().setFromPoints(orbitPoints);
        var orbitMat = new THREE.LineBasicMaterial({ color: 0x444444 });
        var orbit = new THREE.Line(orbitGeo, orbitMat);
        scene.add(orbit);

        var clock = new THREE.Clock();

        function animate() {
            var elapsed = clock.getElapsedTime();

            earth.position.x = Math.cos(elapsed * 0.5) * 4;
            earth.position.z = Math.sin(elapsed * 0.5) * 4;

            moon.position.x = earth.position.x + Math.cos(elapsed * 2) * 1;
            moon.position.z = earth.position.z + Math.sin(elapsed * 2) * 1;

            sun.rotation.y += 0.002;
            earth.rotation.y += 0.01;

            renderer.render(scene, camera);
            requestAnimationFrame(animate);
        }
        animate();

        window.addEventListener("resize", function() {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        });
    </script>
</body>
</html>

You should see a glowing sun at the center, an orbiting Earth with its moon, and a field of distant stars.

What’s Next

TopicDescriptionLink
Cameras & ControlsMaster Perspective/Orthographic cameras and OrbitControlshttps://tutorials.dodatech.com/frontend/libraries/threejs/threejs-cameras-controls/
Geometries & MaterialsDeep dive into shapes, PBR materials, and textureshttps://tutorials.dodatech.com/frontend/libraries/threejs/threejs-geometries-materials/
Lights & ShadowsLight types, shadow maps, and 3-point lightinghttps://tutorials.dodatech.com/frontend/libraries/threejs/threejs-lights-shadows/
AnimationClocks, easing, TWEEN.js, and GSAP integrationhttps://tutorials.dodatech.com/frontend/libraries/threejs/threejs-animation/
WebGL FundamentalsThe low-level API that Three.js builds onWebGL

This Three.js basics tutorial is your starting point. You now understand the scene-camera-renderer trinity, how to create and animate objects, and why each piece matters. This same architecture powers the 3D visualization dashboard in Durga Antivirus Pro, where threat data is rendered as interactive 3D node graphs. Master these fundamentals, and you can build anything in Three.js.

What’s Next

Congratulations on completing this Threejs Basics tutorial! Here’s where to go from here:

  • Practice daily — Consistency is more important than long study sessions
  • Build a project — Apply what you learned by building something real
  • Explore related topics — Check out other tutorials in the same category
  • Join the community — Discuss with other learners and share your progress

Remember: every expert was once a beginner. Keep coding!

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro