three-text-geometry
v4.0.0
Published
Renders BMFont files in ThreeJS with word-wrapping
Readme
three-text-geometry
The port of the JavaScript versions of three-bmfont-text, layout-bmfont-text, load-bmfont, and word-wrapper to Pure Typescript, this library enables fast text rendering with Three.js and bitmap font. The difference in rendering speed is noticeable when animations are enabled, and it runs 10x faster than canvas texture based text rendering.
v4.0 introduces WebGPU support with TSL (Three Shading Language) based node materials that work with both WebGLRenderer and WebGPURenderer.
Requirements
- Three.js 0.172.0 or later
- React 19 or later
- React Three Fiber 9 or later
- Node.js 22 or later
Installation
NPM
$ npm install three-text-geometry three react @react-three/fiberPNPM
$ pnpm add three-text-geometry three react @react-three/fiberYarn
$ yarn add three-text-geometry three react @react-three/fiberNote:
three,react, and@react-three/fiberare peer dependencies and must be installed alongsidethree-text-geometry.
Usage
For detailed information, read the documentation and check the demo.
TextGeometry supports word wrapping, text aligning, letter spacing, kerning. See the list for all available options.
React Three Fiber
three-text-geometry provides first-class support for React Three Fiber.
Setup
TextGeometry is automatically registered with the R3F catalog when the library is imported. No manual setup is required.
import 'three-text-geometry'
// <textGeometry ... /> is now availableFactory pattern (R3F v9)
Alternatively, use R3F v9's extend to create a component directly.
import { extend } from '@react-three/fiber'
import TextGeometry from 'three-text-geometry'
const TextGeometryEl = extend(TextGeometry)
// Now you can use <TextGeometryEl ... />Example
The useFont hook loads a BMFont file and its texture asynchronously. Combined with <textGeometry>, you can render bitmap text in just a few lines.
import * as THREE from 'three'
import { Canvas } from '@react-three/fiber'
import { TextAlign, useFont } from 'three-text-geometry'
function TextMesh() {
const { font, texture, isLoading } = useFont(
'https://example.com/font.json',
'https://example.com/font.png'
)
if (isLoading || !font || !texture) return null
return (
<mesh rotation={[Math.PI, 0, 0]}>
<textGeometry args={['Hello World', { font, align: TextAlign.Left, width: 800, flipY: texture.flipY }]} />
<meshBasicMaterial map={texture} transparent side={THREE.DoubleSide} />
</mesh>
)
}
function App() {
return (
<Canvas camera={{ fov: 45, position: [0, 0, 500] }}>
<TextMesh />
</Canvas>
)
}Type definitions
Importing three-text-geometry automatically augments R3F's ThreeElements interface, enabling type completions for <textGeometry> in TypeScript projects.
import 'three-text-geometry' // Adds textGeometry to ThreeElementsVanilla Three.js
You can also use TextGeometry directly without React Three Fiber.
import * as THREE from 'three'
import TextGeometry, { BMFont, BMFontJsonParser, TextAlign } from 'three-text-geometry'
// Load font and texture
const res = await fetch('https://example.com/font.json')
const font: BMFont = new BMFontJsonParser().parse(await res.json())
const texture = await new THREE.TextureLoader().loadAsync('https://example.com/font.png')
// Create geometry and mesh
const geometry = new TextGeometry('Hello World', {
font,
align: TextAlign.Left,
width: 800,
flipY: texture.flipY,
})
const material = new THREE.MeshBasicMaterial({
map: texture,
side: THREE.DoubleSide,
transparent: true,
})
const mesh = new THREE.Mesh(geometry, material)
.rotateY(Math.PI)
.rotateZ(Math.PI)
scene.add(mesh)Materials
three-text-geometry provides TSL-based node materials for text rendering. These materials work with both WebGLRenderer and WebGPURenderer.
BasicTextNodeMaterial
Samples a font texture and multiplies by color and opacity. Suitable for standard bitmap fonts.
import { BasicTextNodeMaterial } from 'three-text-geometry'
const material = new BasicTextNodeMaterial({
map: texture,
color: new THREE.Color(0xffffff),
opacity: 1,
})SDFTextNodeMaterial
For SDF (Signed Distance Field) fonts. Uses screen-space derivatives for anti-aliased edges.
import { SDFTextNodeMaterial } from 'three-text-geometry'
const material = new SDFTextNodeMaterial({
map: texture,
color: new THREE.Color(0xffffff),
})MSDFTextNodeMaterial
For MSDF (Multi-channel Signed Distance Field) fonts. Computes a median from RGB channels for high-quality rendering.
import { MSDFTextNodeMaterial } from 'three-text-geometry'
const material = new MSDFTextNodeMaterial({
map: texture,
color: new THREE.Color(0xffffff),
negate: true, // Invert RGB channels (default: true)
})MultiPageTextNodeMaterial
For multi-texture fonts. Selects the correct texture atlas per vertex using a page attribute. Requires multipage: true in TextGeometryOption.
import { MultiPageTextNodeMaterial } from 'three-text-geometry'
const material = new MultiPageTextNodeMaterial({
textures: [texture0, texture1, texture2],
color: new THREE.Color(0xffffff),
})
const geometry = new TextGeometry('Hello', {
font,
multipage: true,
})Migration from v3
v4 removes the legacy GLSL shader modules. If you were using MultiPageShaderMaterial or the GLSL shader sources from three-text-geometry/shaders, migrate to the new TSL-based node materials:
| v3 (Removed) | v4 (Replacement) |
| --- | --- |
| shaders/basic | BasicTextNodeMaterial |
| shaders/sdf | SDFTextNodeMaterial |
| shaders/msdf | MSDFTextNodeMaterial |
| MultiPageShaderMaterial | MultiPageTextNodeMaterial |
How to run the demo
The demo is built with React Three Fiber. Clone the repository and run:
git clone https://github.com/gumob/three-text-geometry.git
cd three-text-geometry/demo
corepack enable
pnpm install
pnpm devScreen coordinate system and Three.js coordinate system
TextGeometry places text based on the screen coordinate system.
Therefore, when THREE.Mesh is added to the scene, the text will be placed inverted when viewed from the positive direction of the Z axis.
To make the text visible from the positive z-axis, you need apply transformation.

BMFontParser interface supports JSON, XML, ACII, and Binary fromat
Parse font data in JSON format
import { BMFontJsonParser } from 'three-text-geometry'
const font: BMFont = new BMFontJsonParser().parse(/** `string` or `object` data JSON format */)Parse font data in XML format
import { BMFontXMLParser } from 'three-text-geometry'
const font: BMFont = new BMFontXMLParser().parse(/** `string` data in XML format */)Parse font data in ASCII format
import { BMFontAsciiParser } from 'three-text-geometry'
const font: BMFont = new BMFontAsciiParser().parse(/** `string` data in ASCII format */)Parse font data in Binary format
import { BMFontBinaryParser } from 'three-text-geometry'
const font: BMFont = new BMFontBinaryParser().parse(/** `string` data in ASCII Binary */)The value list of TextGeometryOption
| key | type | description | default | required |
| ----------------- | :-----------------------------------------------------------------------------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------: | :------: |
| font | BMFont | The BMFont definition which holds chars, kernings, etc | undefined | ✔ |
| width | number | The desired width of the text box, causes word-wrapping and clipping in WordWrapMode mode. Leave as undefined to remove word-wrapping (default behaviour) | undefined | |
| mode | WordWrapMode | A mode for word-wrapper; can be WordWrapMode.Pre (maintain spacing), or WordWrapMode.NoWrap (collapse whitespace but only break on newline characters), otherwise assumes normal word-wrap behaviour (collapse whitespace, break at width or newlines) | undefined | |
| align | TextAlign | This can be TextAlign.left, TextAlign.center or TextAlign.right | TextAlign.left | |
| letterSpacing | number | The letter spacing in pixels | 0 | |
| lineHeight | number | The line height in pixels | font.common.lineHeight | |
| tabSize | number | The number of spaces to use in a single tab | 4 | |
| start | number | The starting index into the text to layout | 0 | |
| end | number | The ending index (exclusive) into the text to layout | text.length | |
| flipY | boolean | Whether the texture will be Y-flipped | true | |
| multipage | boolean | Whether to construct this geometry with an extra buffer containing page IDs. This is necessary for multi-texture fonts | false | |
Generate Bitmap Font
The following tools can be used to convert fonts to a bitmap font:
- msdf-bmfont-web (Online Tool)
- msdf-bmfont-xml (Commandline Tool)
- Hiero (Desktop App, Windows Only)
Read the Three.js documentation about a bitmap font.
Using msdf-bmfont-xml
Install msdf-bmfont-xml
$ npm install msdf-bmfont-xml -gGenerate a bitmap font
$ msdf-bmfont \
--output-type json \
--filename 'OdudoMono-Regular-128' \
--font-size 128 \
--texture-size 1024,1024 \
--field-type 'sdf' \
'OdudoMono-Regular.otf'Copyright
Punycode is released under MIT license, which means you can modify it, redistribute it or use it however you like.
