Three.js Text: 3D Text, SDF Text & troika-three-text
Three.js has two main ways to render real text in a 3D scene: TextGeometry, which extrudes a font into actual 3D geometry (good for chunky, extruded logo-style text), and troika-three-text, a community library that renders crisp, resolution-independent text using signed distance fields (SDF), which is the better choice for anything readable like labels, UI text or paragraphs. For text that should behave like normal HTML (selectable, accessible, styled with CSS), an HTML overlay positioned over the canvas is often simpler than any 3D text approach at all.
TextGeometry: real extruded 3D text
TextGeometry (from the examples/addons) converts a loaded font (a Three.js-specific JSON font format, converted from a TTF/OTF via a conversion tool) into actual extruded 3D geometry: real depth, real bevels, a real mesh you can light and shadow like any other object. This is the right tool specifically when text needs to look and behave like a 3D object: a logo, an extruded sign, text that catches light and casts shadows.
The tradeoffs: geometry-heavy for long text (every character is real triangles), doesn't scale cleanly to small sizes (extruded bevels can look muddy at a distance), and re-generating the geometry for dynamically changing text is relatively expensive.
troika-three-text: crisp, scalable SDF text
troika-three-text renders text using signed distance fields: a texture-based technique that stays crisp at any scale or distance, similar to how vector fonts render sharply at any zoom level on the web, unlike a raster image. It's dramatically cheaper than TextGeometry for text that needs to update frequently (a score counter, a live data label) and supports standard typography features (word wrap, alignment, custom fonts via a normal font file) that TextGeometry doesn't.
For any text meant to be read rather than admired as a 3D object (labels, HUD text, captions in a 3D scene), this is almost always the better choice over TextGeometry.
When an HTML overlay is simpler than either
If text needs to be selectable, screen-reader accessible, styled with CSS, or interactive in ways 3D text makes needlessly hard (a real <button>, a text input), positioning a regular HTML element over the canvas (computed from a 3D world position projected to 2D screen coordinates) is often the pragmatic answer. React Three Fiber's drei library ships an <Html> component for exactly this. It's not "true" 3D text (it won't be occluded by 3D geometry correctly without extra work), but for UI-style text it avoids solving problems 3D text libraries weren't designed for.
Code
Learn this properly
Learn Practical TSL
Your First Node Material
Style extruded 3D text with a node material for effects like animated gradients.
Frequently asked questions
What's the best way to add text in Three.js?
For readable labels or UI-style text, troika-three-text (crisp SDF rendering, cheap to update). For extruded, logo-style 3D text that should catch light and cast shadows, TextGeometry. For fully interactive or accessible text, an HTML overlay positioned over the canvas is often simplest.
Why does TextGeometry text look blurry or muddy at a distance?
TextGeometry is real extruded polygon geometry, and its bevels and detail don't hold up well at small screen sizes or a distance. This is one of the main reasons troika-three-text (SDF-based, resolution-independent) is usually a better fit for readable text.
Can Three.js text update dynamically without a performance cost?
troika-three-text handles frequent text updates (like a live score or timer) cheaply. TextGeometry requires regenerating the mesh geometry on every text change, which is comparatively expensive for frequently updating text.
Keep reading
UI & HUD
Most Three.js UI and HUD elements (score displays, menus, tooltips, labels) are built as regular HTML/CSS positioned ove…
Materials
For most realistic objects, use MeshStandardMaterial: physically-based, responds correctly to lights, and the sensible d…
GLTFLoader
GLTFLoader is the standard way to load 3D models into Three.js: instantiate it, call loader.load(url, onLoad), and add g…
All Three.js Guides
Back to the guides hub.