Raycaster: Picking Objects with the Mouse

Raycaster: Picking Objects with the Mouse

A grid of clickable boxes in Three.js

Making your scene interactive means letting the user click on objects. The tool for that is the Raycaster: it casts an invisible ray from the camera through the mouse cursor and tells you which objects that ray hits. In this lesson you build a 3x3 grid of boxes and click one to turn it red.

The Idea

A Raycaster answers one question: "given a ray in 3D space, which objects intersect it?" To convert a mouse click into a world-space ray, you transform the cursor position into normalized device coordinates and pass it (with the camera) to the raycaster.

Full Example

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Three.js – Raycaster Mouse Picking</title>
  <style>body{margin:0;overflow:hidden;background:#0b0f1a}
    #ui{position:absolute;top:12px;left:12px;color:#fff;font:13px sans-serif;z-index:10;background:rgba(0,0,0,.45);padding:8px 12px;border-radius:6px}</style>
</head>
<body>
  <div id="ui">Click a box to change its color</div>
  <script type="importmap">
    { "imports": {
        "three": "https://unpkg.com/three@0.160.0/build/three.module.js",
        "three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/"
    } }
  </script>
  <script type="module">
    import * as THREE from 'three';

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

const camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 0.1, 1000); camera.position.set(0, 2, 6); camera.lookAt(0, 0, 0);

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

scene.add(new THREE.AmbientLight(0x404040)); const dir = new THREE.DirectionalLight(0xffffff, 1); dir.position.set(3, 4, 2); scene.add(dir);

const boxes = []; for (let x = -1; x <= 1; x++) { for (let z = -1; z <= 1; z++) { const box = new THREE.Mesh( new THREE.BoxGeometry(0.9, 0.9, 0.9), new THREE.MeshStandardMaterial({ color: 0x2f80ed }) ); box.position.set(x 1.5, 0.45, z 1.5); scene.add(box); boxes.push(box); } }

const raycaster = new THREE.Raycaster(); const mouse = new THREE.Vector2();

renderer.domElement.addEventListener('click', (event) => { mouse.x = (event.clientX / renderer.domElement.clientWidth) * 2 - 1; mouse.y = -(event.clientY / renderer.domElement.clientHeight) * 2 + 1;

raycaster.setFromCamera(mouse, camera); const hits = raycaster.intersectObjects(boxes); if (hits.length) { hits[0].object.material.color.setHex(0xe02424); } });

function animate() { requestAnimationFrame(animate); renderer.render(scene, camera); } animate(); </script> </body> </html>

Step by Step

Build the grid. Nested loops over x and z from -1 to 1 create 9 boxes in a 3x3 grid 1.5 units apart, and each one is pushed into a boxes array so we can pass them to the raycaster later.

Normalize the mouse. The browser gives us a cursor in pixels. Three.js wants normalized device coordinates (NDC) in the range -1..1:

  • X: (clientX / width) * 2 - 1
  • Y: -(clientY / height) * 2 + 1 (inverted because screen Y grows downward).
Cast the ray. raycaster.setFromCamera(mouse, camera) builds a ray that starts at the camera and passes through the cursor's world position. raycaster.intersectObjects(boxes) returns the list of boxes the ray crosses, sorted nearest-first.

Respond. If there are hits, hits[0].object is the nearest clicked box, and we change its material colour to red. The whole flow takes just a few lines but gives you precise click targeting.

Try It Yourself

  • Click different boxes and watch each one turn red when hit.
  • Change the colour to cycle: material.color.offsetHSL(0.1, 0, 0) for a rotating hue.
  • Only colour boxes you actually clicked by checking hits[0].object === event.target logic or storing IDs.
  • Add OrbitControls and notice the ray still tracks the view because setFromCamera uses the current camera.

Summary

The Raycaster converts a 2D mouse position into a 3D ray and tells you which objects it hits. It is the standard way to add click interaction in Three.js, and it powers the click-to-highlight behaviour in the final capstone project.

Next Lesson

Interaction is in place. The next lesson adds realism with shadows — enabling the renderer's shadow map so objects cast and receive shadows.

Quiz - Quiz - Raycaster

1. What does Raycaster do?

2. Mouse coordinates must be converted to which range before casting?

3. raycaster.intersectObjects(boxes) returns hits

Scene Graph: A Mini Solar System