Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

troika-three-text

protectwise1.7mMIT0.52.4

SDF-based text rendering for Three.js

readme

Troika Text for Three.js

The troika-three-text package provides high quality text rendering in Three.js scenes, using signed distance fields (SDF) and antialiasing using standard derivatives.

Rather than relying on pre-generated SDF textures, this parses font files (.ttf, .otf, .woff) directly using Typr, and generates the SDF atlas for glyphs on-the-fly as they are used. It also handles proper kerning, ligature glyph substitution, right-to-left/bidirectional layout, joined scripts like Arabic, and will automatically load fallback fonts for full unicode coverage. All font parsing, SDF generation, and glyph layout is performed in a web worker to prevent frame drops.

Once the SDFs are generated, it assembles a geometry that positions all the glyphs, and patches any Three.js Material with the proper shader code for rendering the SDFs. This means you can still benefit from all the features of Three.js's built-in materials like lighting, physically-based rendering, shadows, and fog.

Demos

With Other Frameworks

Screenshots

Text Rendering

Zoomed-in

Font with ligatures

Text with a texture

Unicode coverage

Installation

Get it from NPM:

npm install troika-three-text

You will also need to install a compatible version of Three.js; see the notes on Three.js versions in the Getting Started docs for details.

npm install three

Usage

import {Text} from 'troika-three-text'

You can then use the Text class like any other Three.js mesh:

// Create:
const myText = new Text()
myScene.add(myText)

// Set properties to configure:
myText.text = 'Hello world!'
myText.fontSize = 0.2
myText.position.z = -2
myText.color = 0x9966FF

// Update the rendering:
myText.sync()

It's a good idea to call the .sync() method after changing any properties that would affect the text's layout. If you don't, it will be called automatically on the next render frame, but calling it yourself can get the result sooner.

When you're done with the Text instance, be sure to call dispose on it to prevent a memory leak:

myScene.remove(myText)
myText.dispose()

Supported properties

Instances of Text support the following configuration properties:

text

The string of text to be rendered. Newlines and repeating whitespace characters are honored.

Default: none

anchorX

Defines the horizontal position in the text block that should line up with the local origin. Can be specified as a numeric x position in local units, a string percentage of the total text block width e.g. '25%', or one of the following keyword strings: 'left', 'center', or 'right'.

Default: 0

anchorY

Defines the vertical position in the text block that should line up with the local origin. Can be specified as a numeric y position in local units (note: down is negative y), a string percentage of the total text block height e.g. '25%', or one of the following keyword strings: 'top', 'top-baseline', 'top-cap', 'top-ex', 'middle', 'bottom-baseline', or 'bottom'.

Default: 0

clipRect

If specified, defines the [minX, minY, maxX, maxY] of a rectangle outside of which all pixels will be discarded. This can be used for example to clip overflowing text when whiteSpace='nowrap'.

Default: none

color

This is a shortcut for setting the color of the text's material. You can use this if you don't want to specify a whole custom material and just want to change its color.

Use the material property if you want to control aspects of the material other than its color.

Default: none - uses the color of the material

curveRadius

Defines a cylindrical radius along which the text's plane will be curved. Positive numbers put the cylinder's centerline (oriented vertically) that distance in front of the text, for a concave curvature, while negative numbers put it behind the text for a convex curvature. The centerline will be aligned with the text's local origin; you can use anchorX to offset it.

Since each glyph is by default rendered with a simple quad, each glyph remains a flat plane internally. You can use glyphGeometryDetail to add more vertices for curvature inside glyphs.

Default: 0

depthOffset

This is a shortcut for setting the material's polygonOffset and related properties, which can be useful in preventing z-fighting when this text is laid on top of another plane in the scene. Positive numbers are further from the camera, negatives closer.

Be aware that while this can help with z-fighting, it does not affect the rendering order; if the text renders before the content behind it, you may see antialiasing pixels that appear too dark or light. You may need to also change the text mesh's renderOrder, or set its z position a fraction closer to the camera, to ensure the text renders after background objects.

Default: 0

direction

Sets the base direction for the text. The default value of "auto" will choose a direction based on the text's content according to the bidi spec. A value of "ltr" or "rtl" will force the direction.

Default: 'auto'

fillOpacity

Controls the opacity of just the glyph's fill area, separate from any configured strokeOpacity, outlineOpacity, and the material's opacity. A fillOpacity of 0 will make the fill invisible, leaving just the stroke and/or outline.

Default: 1

font

The URL of a custom font file to be used. Supported font formats are:

  • .ttf
  • .otf
  • .woff (.woff2 is not supported)

Default: The Roboto font loaded from Google Fonts CDN

fontSize

The em-height at which to render the font, in local world units.

Default: 0.1

fontStyle

Either "italic" or "normal". Currently only used to select the preferred style for the fallback Unicode fonts.

Default: "normal"

fontWeight

A numeric font weight, "normal", or "bold". Currently only used to select the preferred weight for the fallback Unicode fonts.

Default: "normal"

glyphGeometryDetail

The number of vertical/horizontal segments that make up each glyph's rectangular plane. This can be increased to provide more geometrical detail for custom vertex shader effects, for example.

Default: 1

gpuAccelerateSDF

When true, the SDF generation process will be GPU-accelerated with WebGL when possible, making it much faster especially for complex glyphs, and falling back to a JavaScript version executed in web workers when support isn't available. It should automatically detect support, but it's still somewhat experimental, so you can set it to false to force it to use the JS version if you encounter issues with it.

Default: true

letterSpacing

Sets a uniform adjustment to spacing between letters after kerning is applied, in local world units. Positive numbers increase spacing and negative numbers decrease it.

Default: 0

lineHeight

Sets the height of each line of text. Can either be 'normal' which chooses a reasonable height based on the chosen font's ascender/descender metrics, or a number that is interpreted as a multiple of the fontSize.

Default: 'normal'

material

Defines a Three.js Material instance to be used as a base when rendering the text. This material will be automatically replaced with a new material derived from it, that adds shader code to decrease the alpha for each fragment (pixel) outside the text glyphs, with antialiasing.

By default it will derive from a simple white `MeshBasicMaterial, but you can use any of the other mesh materials to gain other features like lighting, texture maps, etc.

Also see the color shortcut property.

Note that because your material instance is replaced by a derived material instance, any changes you make to your original material will not be reflected in the derived version. If you need to modify properties of the material afterward, be sure you get a new reference to the derived version:

// Bad:
text.material = myOrigMaterial
myOrigMaterial.opacity = 0.5

// Good:
text.material = myOrigMaterial
text.material.opacity = 0.5

Default: a MeshBasicMaterial instance

maxWidth

The maximum width of the text block, above which text may start wrapping according to the whiteSpace and overflowWrap properties.

Default: Infinity, meaning text will never wrap

outlineBlur

Specifies a blur radius applied to the outer edge of the text's outlineWidth. If the outlineWidth is zero, the blur will be applied at the glyph edge, like CSS's text-shadow blur radius. A blur plus a nonzero outlineWidth can give a solid outline with a fuzzy outer edge.

The blur radius can be specified as either an absolute number in local units, or as a percentage string e.g. "12%" which is treated as a percentage of the fontSize.

Default: 0

outlineColor

The color to use for the text outline when outlineWidth, outlineBlur, and/or outlineOffsetX/Y are set. Accepts a ThreeJS Color object, or a number/string accepted by Color#set.

Default: black

outlineOffsetX, outlineOffsetY

These define a horizontal and vertical offset of the text outline. Using an offset with outlineWidth: 0 creates a drop-shadow effect like CSS's text-shadow; also see outlineBlur.

The offsets can be specified as either an absolute number in local units, or as a percentage string e.g. "12%" which is treated as a percentage of the fontSize.

Default: 0

outlineOpacity

Sets the opacity of a configured text outline, in the range 0 to 1.

Default: 1

outlineWidth

The width of an outline/halo to be drawn around each text glyph using the outlineColor and outlineOpacity. This can help improve readability when the text is displayed against a background of low or varying contrast.

The width can be specified as either an absolute number in local units, or as a percentage string e.g. "10%" which is interpreted as a percentage of the fontSize.

Default: 0

overflowWrap

Defines how text wraps if the whiteSpace property is 'normal'. Can be either 'normal' to break at whitespace characters, or 'break-word' to allow breaking within words.

Default: 'normal'

sdfGlyphSize

Allows overriding the default size of each glyph's SDF (signed distance field) used when rendering this text instance. This must be a power-of-two number. Larger sizes can improve the quality of glyph rendering by increasing the sharpness of corners and preventing loss of very thin lines, at the expense of increased memory footprint and longer SDF generation time.

Default: 64

strokeColor

The color of the text stroke, when strokeWidth is nonzero. Accepts a ThreeJS Color object, or a number/string accepted by Color#set.

Default: grey

strokeOpacity

The opacity of the text stroke, when strokeWidth is nonzero. Accepts a number from 0 to 1.

Default: 1

strokeWidth

Sets the width of a stroke drawn inside the edge of each text glyph, using the strokeColor and strokeOpacity.

The width can be specified as either an absolute number in local units, or as a percentage string e.g. "10%" which is interpreted as a percentage of the fontSize.

Default: 0

textAlign

The horizontal alignment of each line of text within the overall text bounding box. Can be one of 'left', 'right', 'center', or 'justify'.

Default: 'left'

textIndent

An indentation applied to the first character of each hard newline. Behaves like CSS text-indent.

Default: 0

unicodeFontsUrl

A custom hosting location for unicode-font-resolver data index and font files, if you want to self-host them rather than using the default CDN. See Unicode Fallback Fonts below for details.

whiteSpace

Defines whether text should wrap when a line reaches the maxWidth. Can be either 'normal', to allow wrapping according to the overflowWrap property, or 'nowrap' to prevent wrapping.

Note that 'normal' in this context does honor newline characters to manually break lines, making it behave more like 'pre-wrap' does in CSS.

Default: 'normal'

Unicode Fallback Fonts

When a character is not covered by the configured font file, unicode-font-resolver will be used to query for an appropriate fallback font. All Unicode character ranges supported by the Google Noto Fonts are covered this way.

By default, the unicode-font-resolver data index and font files are loaded from the jsDelivr CDN. If you wish to self-host those files you can do so; however be aware the full set of data and fonts is nearly 300MB.

To self-host the files:

  • Go to the unicode-font-resolver Github tags page
  • Find the tag matching the version of @unicode-font-resolver/client declared in troika-three-text/package.json's devDependencies.
  • Download the source code .zip or .tar.gz for that release and unpack it.
  • Copy the contents of the packages/data/ directory to your server where you want to host it.
  • Configure troika-three-text to load from that server URL:

    • Per Text instance:
      text.unicodeFontsURL = 'https://my.host/unicode-fonts-data'
    • Globally:

      import {configureTextBuilder} from 'troika-three-text'
      
      configureTextBuilder({
        unicodeFontsURL: 'https://my.host/unicode-fonts-data'
      })

Handling Asynchronous Updates

Since the text processing occurs in a web worker, it is by definition asynchronous. This means that you can't rely on the text being visible or having a complete geometry immediately. If you need to do things like access the geometry's boundingSphere or the textRenderInfo, you will have to listen for completion. You can do this two ways:

  1. Pass a callback function when you call the sync method:

     myText.sync(() => {
       // code to execute after sync completes...
     })

    This is best when you want to only react to that specific sync call. Keep in mind that the callback will not execute if the text is already fully synced.

  2. Add a listener for the synccomplete event:

     myText.addEventListener('synccomplete', () => {
       // code to execute after sync completes...
     })

    This will fire after every sync, no matter who invoked it. This is best if you need to react to all syncs, for example to trigger a manual canvas render.

    You can also listen for the syncstart event if you need to react to the initiation of a sync call, e.g. to set some sort of "waiting" state while the text is being processed.

Disabling web worker

Some environments don't allow evaluating JS strings for security reasons, like those with a restrictive CSP, which blocks how troika-three-text creates its web worker. Or you may want to avoid using a worker for some other reason. You can force it to run on the main thread instead (although still asynchronously) by configuring it prior to creating any Text instances:

import {configureTextBuilder} from 'troika-three-text'

configureTextBuilder({
  useWorker: false
})

Preloading

To avoid long pauses when first displaying a piece of text in your scene, you can preload fonts and optionally pre-generate the SDF textures for particular glyphs up front:

import {preloadFont} from 'troika-three-text'

myApp.showLoadingScreen()

preloadFont(
  {
    font: 'path/to/myfontfile.woff', 
    characters: 'abcdefghijklmnopqrstuvwxyz'
  },
  () => {
    myApp.showScene()
  }
)

The arguments are:

  • options

    • options.font - The URL of the font file to preload. If null is passed, this will preload the default font.

    • options.characters - A string or array of string character sequences for which to pre-generate glyph SDF textures. Note that this will honor ligature substitution, so you may need to specify ligature sequences in addition to their individual characters to get all possible glyphs, e.g. ["t", "h", "th"] to get the "t" and "h" glyphs plus the "th" glyph.

    • options.sdfGlyphSize - The size at which to prerender the SDFs for the characters glyphs. See the sdfGlyphSize config property on Text for details about SDF sizes. If not specified, will use the default SDF size.

  • callback - A function that will be called when the preloading is complete.

Postprocessing

It is possible to use Text within scenes that utilize the postprocessing library for applying image effects. However, you must enable a special mode in that library that allows Text's custom material to be honored. Just do the following once somewhere in your code:

import { OverrideMaterialManager } from 'postprocessing'

OverrideMaterialManager.workaroundEnabled = true

Carets and Selection Ranges

In addition to rendering text, it is possible to access positioning information for caret placement and selection ranges. To access that info, use the getCaretAtPoint and getSelectionRects utility functions. Both of these functions take a textRenderInfo object as input, which you can get from the Text object's textRenderInfo property after sync has completed. See "Handling Asynchronous Updates" above for how to react to sync completion events.

getCaretAtPoint(textRenderInfo, x, y)

This returns the caret position nearest to a given x/y position in the local text plane. This is useful for placing an editing caret based on a click or ther raycasted event. The return value is an object with the following properties:

  • x - x position of the caret
  • y - y position of the caret's bottom
  • height - height of the caret, based on the current fontSize and lineHeight
  • charIndex - the index in the original input string of this caret's target character. The caret will be for the position before that character. For the final caret position, this will be equal to the string length. For ligature glyphs, this will be for the first character in the ligature sequence.

getSelectionRects(textRenderInfo, start, end)

This returns a list of rectangles covering all the characters within a given character range. This is useful for highlighting a selection range. The return value is an array of objects, each with {left, top, right, bottom} properties in the local text plane.

changelog

Change Log

All notable changes to this project will be documented in this file. See Conventional Commits for commit guidelines.

0.52.4 (2025-04-02)

Bug Fixes

  • error in Three r175 with custom(Depth|Distance)Material not having setters (78e00b5), closes #357

0.52.3 (2024-12-19)

Bug Fixes

  • troika-three-text: Fix error in Safari in BatchedText (2260ba0)

0.52.2 (2024-11-21)

Bug Fixes

  • BatchedText: Fix removing text instances (8bf4bca)
  • BatchedText: Fix texture memory leak (908fe31)

0.52.1 (2024-11-21)

Bug Fixes

  • BatchedText: Fix removing text instances via .remove() (d99549f)

0.52.0 (2024-11-11)

Features

  • BatchedText: Support remaining visual properties (60c5e93)

0.51.1 (2024-11-11)

Note: Version bump only for package troika-three-text

0.51.0 (2024-11-10)

Bug Fixes

  • BatchedText: Fix texel read for taller data texture (57f3b0b)
  • troika-three-text: Fix error when first character is whitespace #313 (f71679e)

Features

  • BatchedText: support fillOpacity, and color now sets the diffuse uniform (8f83424)
  • troika-three-text: Add config to disable web worker (86023ec), closes #337

0.50.3 (2024-11-01)

Bug Fixes

  • ensure high precision sampling of batched text data texture (f8bd307)

0.50.2 (2024-10-21)

Bug Fixes

  • troika-three-text: BatchedText: increase number of batch members (449957d)

0.50.1 (2024-10-12)

Bug Fixes

  • troika-three-text: fix error when using BatchedText material color (7f8c845)
  • troika-three-text: remove DoubleSide double draw hack in favor of forceSinglePass flag (7818d05)

0.50.0 (2024-10-11)

Features

  • troika-three-text: allow overriding text material derivation (9a48e0c)
  • troika-three-text: ALPHA: BatchedText for batching many Text instances in a single draw call (79c9c50)

0.49.1 (2024-04-09)

Bug Fixes

  • troika-three-text: Fix anchorY bottom-baseline. Closes #309 (3457b03)

0.49.0 (2023-10-08)

Bug Fixes

  • support opentype ccmp substitutions (glyph compositions) (1e358b2)

Features

  • add support for positioning of diacritic marks (e.g. Thai) (301c34c)

0.48.1 (2023-10-02)

Bug Fixes

  • improve font resolution in CJK (8d9ac64)
  • korean/japanese fonts now resolve correctly (207a5e0)

0.48.0 (2023-09-09)

Features

  • troika-three-text: add fallback font loading for full Unicode support (#279) (6fb8061), closes #13 #65

0.47.2 (2023-05-15)

Note: Version bump only for package troika-three-text

0.47.1 (2022-12-15)

Bug Fixes

  • troika-three-text: update Typr.ts to get kerning fix (#228) (a45db5d)

0.47.0 (2022-12-15)

Bug Fixes

  • troika-three-text: include lineGap value from the font in 'normal' line-height calculation (c278118)
  • troika-three-text: remove IIFEs for tree-shaking (#224) (2e688f0)
  • troika-three-text: update Typr to fix doubled kerning in some fonts (#196) (1fab9a2)
  • troika-three-text: use sTypoAscender/Descender from OS/2 font table if available (f5c244c)
  • troika-three-text: work around Safari<15 bug using SDF canvas as a texture (#199) (fca9aae)

Features

  • raise three min version to r125 and fix BufferGeometry references (#225) (f2ef803)
  • troika-three-text: add 'top-cap' and 'top-ex' as keywords for anchorY (#193) (c6a10ae)
  • troika-three-text: include capHeight and xHeight font metrics in textRenderInfo (3459fd5)
  • troika-three-text: remove deprecated totalBounds/totalBlockSize properties from textRenderInfo (2b87308)
  • remove custom Thenable polyfill in favor of native promises (7af402e)
  • troika-three-text: remove long-deprecated anchor array property (752e302)

0.46.3 (2022-03-11)

Bug Fixes

  • troika-three-text: update Typr to fix doubled kerning in some fonts (#196) (080119a)

0.46.2 (2022-03-06)

Bug Fixes

  • troika-three-text: fix SDF texture resizing in Three r136+ (0fab679)

0.46.1 (2022-03-05)

Bug Fixes

0.46.0 (2022-03-05)

Features

  • troika-three-text: add a gpuAccelerateSDF property for opting out of webgl sdf generation (d436ffd)
  • troika-three-text: integrate webgl-sdf-generator for GPU-accelerated SDF generation (b5c9138)

0.45.0 (2022-01-02)

Features

  • troika-three-text: SDFs for all fonts are now stored in the same texture (7e871f7)

Performance Improvements

  • troika-three-text: avoid extra draw call on double sided materials as of Three r130 (6222ef3)
  • troika-three-text: make the glyphColors buffer transferable (c8c92fa)

0.44.0 (2021-11-14)

Bug Fixes

  • troika-three-text: fill in missing caret positions when the final glyph is a ligature - #165 (ad2eda6)

0.43.1-alpha.0 (2021-10-24)

Bug Fixes

  • troika-three-text: fix font parsing failures in iOS Safari (a542b42)

0.43.0 (2021-09-20)

Features

  • troika-three-text: allow line wrapping after some common non-whitespace chars like hyphens (1b20e34), closes #136

Performance Improvements

  • troika-three-text: parallelize SDF generation with multiple worker threads (c2bf886)

0.42.0 (2021-05-17)

Bug Fixes

  • add three to peerDependencies in all leaf packages (0a11ab6)

Features

  • open up 'three' peer dependency to include future versions (d4a5b23)

0.41.2 (2021-05-05)

Bug Fixes

  • update bidi-js for its ES5 build files (49ce5f2)

0.41.1 (2021-04-26)

Bug Fixes

  • update bidi-js for fix removing type:module from package.json (394c371)

0.41.0 (2021-04-19)

Bug Fixes

  • troika-three-text: fix Arabic word position letter forms (480ee97)
  • troika-three-text: formatting characters no longer produce visible glyphs (c0d28e8)
  • troika-three-text: more correct impl for character joining types (2ce519a)
  • troika-three-text: prevent mutation of input to worldPositionToTextCoords method (d487b8a)

Features

  • troika-three-text: add full bidi text support (3fde850)
  • troika-three-text: simple bidi layout support, using explicit LRO/RLO/PDF chars only (d511655)
  • troika-three-text: very basic support for right-to-left text layout (ce887be)

Performance Improvements

  • prune some unused functions out of the Typr build (26e669f)

0.40.0 (2021-02-28)

Bug Fixes

  • troika-three-text: fix boundingBox, boundingSphere, and raycasting with curveRadius (7cc7c82), closes #103

0.39.2 (2021-02-18)

Bug Fixes

  • troika-three-text: fix shader error in WebGL1 (cdbc7dc), closes #108

0.39.1 (2021-02-17)

Bug Fixes

  • troika-three-text: selection rects no longer clip off trailing whitespace (158305c), closes #78

0.39.0 (2021-02-15)

Features

  • troika-three-text: add curveRadius for applying cylindrical curvature (6fdfbbf)
  • troika-three-text: export a function for debugging SDF textures (3fb0c23)
  • troika-three-text: pack SDFs using all 4 color channels, to increase max glyphs in a texture (d236caf)

0.38.1 (2021-02-03)

Bug Fixes

  • troika-three-text: prevent copy() from sharing geometry between instances (8c3ba2d)

0.38.0 (2021-01-24)

Bug Fixes

  • troika-three-text: allow negative percentages for outlineOffsetX/Y (3a274f0), closes #100

0.37.0 (2021-01-18)

Features

  • troika-three-text: added inner stroke and outline blur capabilities (e004b9d)

Performance Improvements

  • troika-three-text: swap tiny-inflate to fflate for minor speed boost on woff fonts (2ae29fa)

0.36.1 (2020-12-16)

Bug Fixes

  • troika-three-text: soften Typr.ts console warnings to debug level (50d951f)

0.36.0 (2020-12-04)

Bug Fixes

  • troika-three-text: fix wrong caret position for collapsed ligature characters (f220035)

Features

  • troika-three-text: fix kerning by updating from Typr.js to Typr.ts (43144cf), closes #70

0.35.0 (2020-11-16)

Note: Version bump only for package troika-three-text

0.34.2 (2020-11-09)

Bug Fixes

  • troika-three-text: dispose the outline material when the base material is disposed (68bd2c8)
  • troika-three-text: fix error when disposing the base material with outlines enabled (73a51f5)

0.34.1 (2020-10-20)

Note: Version bump only for package troika-three-text

0.34.0 (2020-10-19)

Bug Fixes

  • troika-three-text: clipRect is no longer clamped to the text block's bounds (15edbd9)
  • troika-three-text: fix text baseline being positioned too low (596d8ca)

Features

  • troika-three-text: expose blockBounds and visibleBounds in textRenderInfo (f3340ec)
  • troika-three-text: text outline and better antialiasing at small sizes (3836809)

Performance Improvements

  • micro-optimization of sdf texture insertion loop (995c2a6)

0.33.1 (2020-10-02)

Note: Version bump only for package troika-three-text

0.33.0 (2020-10-02)

Bug Fixes

  • add "sideEffects":false to package.json files to assist treeshaking (61109b2)
  • add PURE annotations to make troika-three-text treeshakeable (8e76b5c)
  • remove redundant "browser" and defunct "jsnext:main" fields from package.json files (0abec40)
  • troika-three-text: make color prop only apply to that instance when sharing a base material (da0f995)

Features

  • troika-three-text: modifications to the base material are now picked up automatically (fc81d3a)

0.32.0 (2020-09-16)

Bug Fixes

  • mutate boundingBox and set depth to 0 (1f9b6be)

Features

  • added boundingBox calculation (140e9e8)

0.31.0 (2020-08-11)

Note: Version bump only for package troika-three-text

0.30.2 (2020-07-22)

Bug Fixes

0.30.1 (2020-07-19)

Bug Fixes

  • troika-three-text: fix changing text length in ThreeJS r117+ (a7ef945), closes #69

0.30.0 (2020-07-16)

Features

  • troika-three-text: add support for textIndent (b689c0c)

0.29.0 (2020-07-06)

Features

  • troika-three-text: promote standalone text to a new troika-three-text package (995f2eb), closes #47