Convert 3D Coordinates to 2D Screen Space in Three.js
Converting a 3D object’s position into 2D screen-space coordinates is
essential for positioning HTML overlays, UI tooltips, or labels over
WebGL elements. This guide explains how to use the Three.js
Vector3.project() method to project 3D coordinates into
Normalized Device Coordinates (NDC) and map them directly to 2D pixel
coordinates on your canvas.
Step 1: Get the 3D World Position
Before projecting, you must retrieve the absolute world position of
your 3D object. While object.position gives local
coordinates relative to its parent,
object.getWorldPosition() ensures you obtain the correct
global coordinates in the 3D scene.
import * as THREE from 'three';
const tempV = new THREE.Vector3();
mesh.getWorldPosition(tempV);Step 2: Project the Vector Using the Camera
Pass your camera to the vector’s project method. This
projects the 3D world coordinates onto a 2D plane using the camera’s
projection matrix.
tempV.project(camera);After calling .project(camera), the vector’s
x and y values are converted to Normalized
Device Coordinates (NDC). In this coordinate space: * X
ranges from -1 (left edge of the screen) to +1
(right edge of the screen). * Y ranges from
-1 (bottom edge of the screen) to +1 (top edge
of the screen). * Z represents the depth value (useful
for checking if the object is behind the camera).
Step 3: Convert NDC to Screen Pixels
To position an HTML element, you must map the -1 to 1
NDC range to the width and height of your rendering canvas (in
pixels).
Additionally, you must invert the Y-axis. In WebGL, the Y-axis points
upwards, whereas in standard HTML/CSS screen coordinates, the Y-axis
points downwards starting from the top-left corner
(0,0).
const canvas = renderer.domElement;
// Convert NDC (-1 to +1) to pixel coordinates (0 to width/height)
const x = (tempV.x * .5 + .5) * canvas.clientWidth;
const y = (tempV.y * -.5 + .5) * canvas.clientHeight;Step 4: Apply to HTML Elements
You can now use these calculated x and y
coordinates to update the CSS style of an absolutely positioned HTML
element.
const tooltip = document.getElementById('tooltip');
tooltip.style.transform = `translate(-50%, -50%) translate(${x}px, ${y}px)`;Handling Off-Screen Objects
If the object moves behind the camera, the projection calculations
can still yield coordinates that appear on screen. To prevent tooltips
from rendering when the object is behind the camera, check the projected
vector’s z depth value:
const isBehindCamera = tempV.z > 1;
if (isBehindCamera) {
tooltip.style.display = 'none';
} else {
tooltip.style.display = 'block';
}