DEV Community

Cover image for Confused by Mouse Coordinates in Three.js? Here’s the 2-Minute Explanation
Peter Riding
Peter Riding

Posted on

Confused by Mouse Coordinates in Three.js? Here’s the 2-Minute Explanation

Ever clicked a 3D object in Three.js and the raycaster hits somewhere completely wrong?

You're not alone. The fix is just a few lines of code — but the why is what confuses most people.

Here’s the simple explanation.


The raycaster doesn't use pixels

It works in the camera’s coordinate system using Normalized Device Coordinates (NDC) — values from -1 to +1.

  • X: -1 = left, +1 = right
  • Y: +1 = top, -1 = bottom

So instead of pixels, it uses a normalized coordinate system based on the screen (you can think of it like percentages).


The conversion (this is the important part)

const mouse = new THREE.Vector2();

mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

raycaster.setFromCamera(mouse, camera);
Enter fullscreen mode Exit fullscreen mode

What’s happening here:

  • event.clientX / width → gives a value from 0 → 1 across the screen
  • We remap that to -1 → +1
  • Same for Y, but flipped because browser coordinates start at the top

In other words, you're converting from pixel space → camera space.


A quick visual

(-1, +1)       (+1, +1)
     ┌─────────────┐
     │             │
     │    (0,0)    │
     │             │
     └─────────────┘
(-1, -1)       (+1, -1)
Enter fullscreen mode Exit fullscreen mode

Why this exists

This system makes everything consistent:

  • Works on any screen size or resolution
  • Doesn’t depend on pixel values
  • Lets setFromCamera() generate a correct 3D ray every time

Minimal click example

window.addEventListener('click', (event) => {
  const mouse = new THREE.Vector2(
    (event.clientX / window.innerWidth) * 2 - 1,
    -(event.clientY / window.innerHeight) * 2 + 1
  );

  raycaster.setFromCamera(mouse, camera);
  const intersects = raycaster.intersectObjects(scene.children);

  if (intersects.length > 0) {
    console.log("Clicked on:", intersects[0].object.name);
  }
});
Enter fullscreen mode Exit fullscreen mode

That’s it. No complex math, no magic — just a coordinate conversion.

If this cleared things up for you as much as it did for me.

Top comments (0)