Skip to content

Three.js - Creating Text

Displaying text is a common requirement in 3D scenes, whether for user interfaces, annotations, or artistic effects. There are several ways to achieve this in Three.js, each with its own advantages and disadvantages. We’ll explore three primary methods.

Section titled “Method 1: HTML and CSS (The Recommended Way for UI)”

The most flexible and highest-quality method for creating UI elements like labels and overlays is to use what the browser does best: rendering HTML and CSS. Three.js provides CSS2DRenderer and CSS3DRenderer to help you position regular HTML elements in your 3D scene.

  • Pros: Crisp text at any resolution, full power of CSS for styling and animation, great for accessibility, clickable links and buttons.
  • Cons: Elements are rendered on top of the 3D scene, not within it. They cannot be obscured by 3D objects or affected by scene lighting.
import { CSS2DRenderer, CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer.js';
// 1. Setup the renderer
const labelRenderer = new CSS2DRenderer();
labelRenderer.setSize(window.innerWidth, window.innerHeight);
labelRenderer.domElement.style.position = 'absolute';
labelRenderer.domElement.style.top = '0px';
document.body.appendChild(labelRenderer.domElement);
// 2. Create the HTML element
const p = document.createElement('p');
p.textContent = 'Hello, World!';
p.style.color = 'white';
p.style.fontFamily = 'sans-serif';
p.style.fontSize = '2em';
// 3. Create the CSS2DObject
const label = new CSS2DObject(p);
// 4. Position it and add it to a 3D object or the scene
// Position it on top of a cube, for example
label.position.set(0, 0.75, 0); // Position is relative to the parent
cube.add(label);
// 5. Render in your animation loop
function animate() {
// ...
renderer.render(scene, camera); // WebGL renderer
labelRenderer.render(scene, camera); // CSS2D renderer
}

For text that needs to exist as a true 3D object within your scene—to cast shadows, be affected by lights, and be obscured by other objects—you can use TextGeometry.

  • Pros: Is a real 3D mesh, interacts with lighting and other objects, can have depth and beveling.
  • Cons: Requires loading a special font file (.typeface.json), can be heavy on performance due to high polygon count, text quality can degrade when viewed up close.

This process involves loading a font, then using that font to generate the geometry.

import { FontLoader } from 'three/examples/jsm/loaders/FontLoader.js';
import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry.js';
const fontLoader = new FontLoader();
fontLoader.load(
'/fonts/helvetiker_regular.typeface.json', // Path to a typeface.json font
(font) => {
const textGeometry = new TextGeometry(
'Three.js',
{
font: font,
size: 0.5,
height: 0.2, // Depth
curveSegments: 12,
bevelEnabled: true,
bevelThickness: 0.03,
bevelSize: 0.02,
bevelOffset: 0,
bevelSegments: 5
}
);
// Center the geometry for easier positioning
textGeometry.center();
const textMaterial = new THREE.MeshStandardMaterial({ color: 0xffffff });
const textMesh = new THREE.Mesh(textGeometry, textMaterial);
scene.add(textMesh);
}
);

Note: You can find typeface.json fonts in the Three.js examples repository, or convert your own TTF/OTF fonts using online tools.

This is a hybrid approach. You draw text onto a 2D HTML5 Canvas and then use that canvas as a texture on a 3D plane (PlaneGeometry). It’s a highly performant way to display text that looks like it’s in the 3D world.

  • Pros: Very performant, can be applied to any geometry, high quality text rendering from the 2D canvas API.
  • Cons: Text is flat (part of a texture), requires careful management of texture updates if the text needs to change dynamically.
// 1. Create a canvas element in memory
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.width = 512;
canvas.height = 128;
// 2. Draw text on the canvas
context.fillStyle = '#444444'; // Background
context.fillRect(0, 0, canvas.width, canvas.height);
context.font = 'Bold 48px sans-serif';
context.fillStyle = 'white';
context.textAlign = 'center';
context.textBaseline = 'middle';
context.fillText('Canvas Texture', canvas.width / 2, canvas.height / 2);
// 3. Create a texture from the canvas
const texture = new THREE.CanvasTexture(canvas);
// 4. Apply the texture to a material
const material = new THREE.MeshBasicMaterial({ map: texture });
const geometry = new THREE.PlaneGeometry(4, 1); // Aspect ratio matches canvas
const planeMesh = new THREE.Mesh(geometry, material);
scene.add(planeMesh);

If you need to update the text, you redraw on the canvas and then set texture.needsUpdate = true; to tell Three.js to re-upload the texture to the GPU.

  • For UI, HUDs, and annotations, use HTML/CSS (CSS2DRenderer).
  • For 3D logos or text that must be part of the scene, use TextGeometry.
  • For performant text on signs or surfaces within the world, use a Canvas Texture.