Three.js From Zero · Article s0-04

3D Text Playground

3D Text Playground is Article s0-04 of Three.js From Zero, a MasterAllArts free interactive lesson for artists learning creative 3D on the web.

← Three.js From ZeroS0-04 · Quick Wins

Season 0 · Article 04 · Quick Wins

Drop your name into 3D — TextGeometry with the helvetiker font, bevels, extrusion, and a swirling material. The "first 3D thing I made and screenshotted" demo. Type your own text in the box.

— fps

What TextGeometry is and isn't

TextGeometry turns a font file into real 3D geometry — every glyph becomes a mesh you can light, shadow, transform. That's powerful and expensive. For body copy or labels you want SDF text (Article S7-08); for hero text and one-off badges, TextGeometry is the right answer.

Step 1 — Load the font

Three.js fonts are JSON files (typeface.json format). The classic helvetiker ships with Three.js examples and is available from the CDN. Use FontLoader:

import { FontLoader } from 'three/addons/loaders/FontLoader.js';
import { TextGeometry } from 'three/addons/geometries/TextGeometry.js';

const loader = new FontLoader();
loader.load(
  'https://unpkg.com/three@0.170.0/examples/fonts/helvetiker_regular.typeface.json',
  (font) => { /* font ready, build text */ },
);

The callback hands you a Font object. Keep a reference — you'll re-use it every time the user changes their text.

Step 2 — Build the geometry

const geo = new TextGeometry('Three.js', {
  font,
  size: 1,            // glyph height in world units
  depth: 0.25,        // extrusion depth (used to be `height` in older versions)
  curveSegments: 6,   // glyph curve smoothness (4-12 sweet spot)
  bevelEnabled: true,
  bevelThickness: 0.03,
  bevelSize: 0.015,
  bevelSegments: 3,
});
geo.center();         // moves geometry origin to its bounding-box center

That geo.center() at the end is the difference between "text rotates around its corner" and "text rotates around its middle." Always call it for hero text.

Pitfall. In Three.js r163+ the option was renamed heightdepth. Old tutorials use height; both still work on r170 (height is deprecated and warns). Use depth.

Step 3 — Material that earns the 3D

Flat MeshBasicMaterial on TextGeometry looks terrible — the whole point of extrusion is to catch light. Use MeshStandardMaterial with some metalness and roughness:

const material = new THREE.MeshStandardMaterial({
  color: '#ec4899',
  metalness: 0.7,
  roughness: 0.2,
});
const mesh = new THREE.Mesh(geo, material);
scene.add(mesh);

The bevel + metallic surface is what makes the text feel premium. Without the bevel, the edges are razor sharp and look CG-cheap.

Step 4 — Regenerate on change, dispose the old

let textMesh = null;
function buildText(str, opts) {
  if (textMesh) {
    textMesh.geometry.dispose();
    scene.remove(textMesh);
  }
  const geo = new TextGeometry(str || ' ', { font, ...opts });
  geo.center();
  textMesh = new THREE.Mesh(geo, material);
  scene.add(textMesh);
}

Same disposal pattern as the galaxy generator — change-on-input means dispose-on-change. Note the str || ' ' guard: empty strings throw inside TextGeometry's path builder.

Step 5 — Make it move

In the loop, gently sway the text. A small Y rotation gives the bevel light something to reflect off:

renderer.setAnimationLoop((t) => {
  if (textMesh) {
    textMesh.rotation.y = Math.sin(t * 0.0006) * 0.25;
    textMesh.rotation.x = Math.sin(t * 0.0004) * 0.1;
  }
  renderer.render(scene, camera);
});

Common first-time pitfalls

"Text is solid black." No lights. MeshStandardMaterial requires illumination — add an ambient + directional pair (intensity 0.3 / 1.0 is a good starting point).
"Bevel looks chunky." Increase bevelSegments from 3 to 5–8 for smoother bevel curves. Don't go higher — vertex count explodes.
"Text isn't centered." You forgot geo.center(), or you set mesh.position before calling it. Center first, then position.
"Font load fails silently." CDN cross-origin or wrong URL. Open DevTools Network tab — a 404 is silent in the callback otherwise.

Exercises

  1. Per-letter animation. Build one TextGeometry per character, position them sequentially, then stagger their rotations by index for a wave effect.
  2. Material swap on click. Three materials (matte plastic, gold metal, glass via MeshPhysicalMaterial with transmission). Cycle through them on canvas click.
  3. Drop your name as a logo. Replace the placeholder, tighten the camera, screenshot it, ship as your portfolio header. That's the whole point of this article.