DEV Community

Cover image for What normalize() does (and why Three.js raycasting needs it)
Peter Riding
Peter Riding

Posted on

What normalize() does (and why Three.js raycasting needs it)

If you’ve seen this line in raycasting examples:

rayDirection.normalize();
Enter fullscreen mode Exit fullscreen mode

and thought “yeah yeah, makes it normal… whatever that means” — here’s what it actually does and why it matters.

The short version

normalize() makes a vector exactly 1 unit long while keeping it pointing in the same direction.

That’s it.

If you start with this:

const rayDirection = new THREE.Vector3(10, 0, 0);
Enter fullscreen mode Exit fullscreen mode

You have a vector pointing along the positive X axis, but its length is 10.

After this:

rayDirection.normalize();
Enter fullscreen mode Exit fullscreen mode

It becomes:

(1, 0, 0)
Enter fullscreen mode Exit fullscreen mode

Same direction. Length is now exactly 1.

What normalization really means

Mathematically, normalization just divides a vector by its length:

normalized = v / |v|
Enter fullscreen mode Exit fullscreen mode

So if the length is 10, every component gets divided by 10.

You can see it directly:

const v = new THREE.Vector3(10, 0, 0);

console.log(v.length()); // 10

v.normalize();

console.log(v.length()); // 1
Enter fullscreen mode Exit fullscreen mode

The direction didn’t change. Only the magnitude did.

Here's a handy diagram to visualize it:
Vector Normalization: Before and After

(credit: from a Medium article on text analysis, but it fits perfectly here)

Why Three.js raycasting needs this

The important part: Raycaster expects the direction to be normalized.

There are two main reasons.

  1. Distance calculations assume unit length

    When a ray hits something, Three.js calculates how far along the ray the intersection happened.

    Those distance values only make sense if the direction vector has length 1.

    If your direction vector had length 10, the internal math would scale everything incorrectly. Distances would no longer match world units properly.

    A normalized vector makes 1 unit of direction = 1 unit in the scene.

  2. The math assumes it

    Many ray–geometry intersection formulas assume the direction vector is a unit vector. The docs explicitly state that the direction must be normalized.

    So even if things seem to work without it, you're relying on undefined behavior.

What your ray code is actually doing

const rayOrigin = new THREE.Vector3(-3, 0, 0);
const rayDirection = new THREE.Vector3(10, 0, 0);

rayDirection.normalize();

const raycaster = new THREE.Raycaster(rayOrigin, rayDirection);
Enter fullscreen mode Exit fullscreen mode

This creates:

  • A ray starting at x = -3

  • Shooting straight right along the X axis

  • With a properly normalized direction

Now when you call:

raycaster.intersectObjects(scene.children);
Enter fullscreen mode Exit fullscreen mode

The intersection distances will be correct and consistent.

Important detail: normalize() mutates

One thing people miss:

vector.normalize();
Enter fullscreen mode Exit fullscreen mode

This changes the original vector.

If you need the original untouched:

const direction = original.clone().normalize();
Enter fullscreen mode Exit fullscreen mode

How to control ray length (don’t scale the direction)

If you want to limit how far the ray checks for intersections, don’t multiply the direction.

Do this instead:

raycaster.far = 100;
raycaster.near = 0;
Enter fullscreen mode Exit fullscreen mode

Keep the direction normalized. Let the raycaster handle range.

Quick mental model

Think of a direction vector like a compass needle.

Before normalization, it might be a 10-meter long arrow.

After normalization, it’s a 1-meter arrow pointing the same way.

Raycasting wants the 1-meter version so all distance math stays clean and predictable.

One sentence explanation

normalize() makes your direction vector exactly 1 unit long while keeping it pointing the same way — and Three.js raycasting depends on that.

Top comments (0)