Billboarding

빌보드

Keeping a label or icon always facing the camera no matter how the scene rotates — like a billboard that always faces the road.

Also known as: Camera-facing spritelookAt(camera)
···
js
import * as THREE from 'three';
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x0a0b14);
const camera = new THREE.PerspectiveCamera(50, 1, 0.1, 30);
camera.position.set(0, 1.4, 7.4);
function makeLabel(text, color) {
  const c = document.createElement('canvas'); c.width = 256; c.height = 96;
  const ctx = c.getContext('2d');
  ctx.fillStyle = 'rgba(10,11,20,.85)'; ctx.beginPath(); ctx.roundRect(4, 20, 248, 56, 20); ctx.fill();
  ctx.fillStyle = color; ctx.font = 'bold 34px system-ui'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle';
  ctx.fillText(text, 128, 50);
  const sprite = new THREE.Sprite(new THREE.SpriteMaterial({ map: new THREE.CanvasTexture(c), transparent: true }));
  sprite.scale.set(1.3, 0.49, 1);
  return sprite;
}
const group = new THREE.Group(); scene.add(group);
const data = [{ x: -2.4, c: '#8ecbff', t: 'North' }, { x: 0, c: '#ff9d7a', t: 'Core' }, { x: 2.4, c: '#9dff9d', t: 'East' }];
data.forEach((d) => {
  const sphere = new THREE.Mesh(new THREE.IcosahedronGeometry(0.6, 1), new THREE.MeshBasicMaterial({ color: d.c, wireframe: true }));
  sphere.position.x = d.x;
  const label = makeLabel(d.t, d.c);
  label.position.set(d.x, 1.05, 0);
  group.add(sphere, label);
});
function resize() { renderer.setSize(innerWidth, innerHeight); camera.aspect = innerWidth / innerHeight; camera.updateProjectionMatrix(); }
addEventListener('resize', resize); resize();
renderer.setAnimationLoop((t) => {
  group.rotation.y = t * 0.00045;
  camera.lookAt(0, 0.3, 0);
  renderer.render(scene, camera);
});

Attach a name tag or icon to a 3D object and let it rotate together with the object, and the moment it swings to the side or back, the text flips or disappears entirely. Billboarding rotates just that label, every frame, to face the camera — so it always reads front-on.

The easiest route in three.js is THREE.Sprite — sprites are built to always face the camera. To do it with a plain Mesh, copy the camera's rotation onto the object every frame with `object.quaternion.copy(camera.quaternion)`.

The demo attaches a label to each of three rotating spheres. The spheres themselves (shown as wireframes) keep turning with the scene, while the labels always face the screen head-on — the text never flips.

When to use

Use it for 2D elements that must always stay legible over a 3D scene — name tags, HP bars, marker icons. Skip it when the object’s own front-and-back needs to be visible, like a character’s face.