Publishing gate

Run the WebGL scene health check

Use the checklist to score assets, camera behavior, rendering, mobile performance, loading states, and fallback content before publishing.

Run Scene Check Compare lab tools

Labels are not just text

A label has to answer several questions. Which 3D point owns it? Does it stay visible behind objects? Can the user click it? Does it scale with distance? Does it need to be selectable text? The rendering technique depends on those answers.

Three.js scenes often start with canvas-only thinking, but labels are usually better as HTML. Real DOM labels are easier to style, localize, and make accessible. They can also share the same design system as the rest of the page.

CSS2DRenderer is the clean starter

CSS2DRenderer renders DOM elements positioned from 3D objects. It is useful for pins, names, simple callouts, and annotations that should remain readable. The WebGLRenderer draws the scene; CSS2DRenderer draws labels above it.

The tradeoff is depth. CSS2D labels are DOM elements, so they do not automatically disappear behind meshes like real 3D geometry. You may need raycasting, distance checks, or manual visibility logic if occlusion matters.

Manual projection gives control

For custom UIs, projecting a Vector3 into screen space can be enough. Take the object's world position, call `.project(camera)`, convert normalized coordinates into CSS pixels, and position an absolutely placed element. This is flexible and keeps the label system inside your app's own layout.

Manual projection also makes it easier to clamp labels to the viewport, hide them behind panels, or attach them to a separate list. It is a little more code, but it gives product pages more control than a generic label layer.

Avoid label clutter

Labels can ruin an otherwise clean 3D scene. Use progressive disclosure: show key labels by default, reveal details on hover or selection, and hide labels that overlap too aggressively. A dense product or data scene may need search and list controls rather than every label visible at once.

For mobile, assume less space and less pointer precision. Tap targets should be larger than the visual label, and text should not sit directly over critical model detail.

When this guide is the right tool

Use this guide when the problem is specific enough that a broad Three.js tutorial would waste time. The focus here is labels in a real browser page: what to check first, what to measure, and what to avoid before the scene becomes harder to reason about.

The practical rule is simple: Use CSS2DRenderer when labels should be real DOM elements. If that sentence describes the scene in front of you, treat the article as a checklist. Read the explanation, copy the smallest useful code pattern, then test the result in a narrow mobile viewport and a wide desktop viewport before adding more polish.

Common mistake to avoid

The common mistake is treating Three.js HTML labels over 3D objects as a visual tweak instead of a scene-system decision. Three.js problems often look like one broken line of code, but the actual issue is usually a chain: asset assumptions, renderer settings, camera math, material setup, interaction state, and page layout all meet inside the canvas.

That is why the safest fix is incremental. Start from a known-good scene, change one variable, and keep a visible diagnostic while testing. Project object positions manually when you need tight integration with your own UI. When the scene works, remove temporary helpers and leave behind only the checks that make sense for users.

Publishing check

Before publishing, run the scene through three questions. Does the page remain useful if WebGL fails or the asset loads slowly? Does the same scene still read clearly on a phone? Can another developer understand the important settings without reverse-engineering the whole file?

The last pass should be boring on purpose: verify canvas size, console errors, mobile performance, source links, internal links, and the related guide path. Plan how labels hide, overlap, or respond when objects move behind the camera. If any answer is fuzzy, fix that before introducing a new effect or a larger asset.

Manual DOM label projection

function positionLabel(label, object, camera, container) {
  const point = object.getWorldPosition(new THREE.Vector3());
  point.project(camera);

  const x = (point.x * 0.5 + 0.5) * container.clientWidth;
  const y = (-point.y * 0.5 + 0.5) * container.clientHeight;

  label.style.transform = `translate(${x}px, ${y}px)`;
  label.hidden = point.z < -1 || point.z > 1;
}

FAQ

When should I use Three.js HTML labels over 3D objects?

Use it when your Three.js scene has a focused labels problem and you need a practical checklist before adding more visual polish.

Is the code snippet production-ready?

Treat the snippet as a clear starting point. Test it in your scene, adapt naming and paths, and verify behavior on mobile hardware before publishing.

What should I check after applying this guide?

Run the scene through mobile sizing, console errors, loading states, source links, and related guide paths so the page remains useful even when assets or WebGL fail.

Sources and further reading

Related guides