Text
Control layout, styling, and visual effects for runtime text.
Text
Text renders GPU-accelerated text strings as nodes in the scene graph. Each Text instance rasterizes its glyphs into the engine’s shared glyph atlas and draws them as a single Mesh — one draw call per text node, regardless of string length. The node extends Container and inherits full transform, filter, blend, and mask support.
A minimal text node
import { Color, Text } from '@codexo/exojs';
const label = new Text('Hello', {
fillColor: Color.white,
fontSize: 24,
fontFamily: 'Arial',
});
The second argument is a TextOptions object — a flat merge of TextStyleOptions (visual appearance) and LayoutOptions (flow and overflow). All properties are optional and have defaults. The live TextStyle is stored as text.style; mutating its fields is cheap and is applied automatically before the next render pass — no manual rebuild call is needed:
import { Color } from '@codexo/exojs';
label.style.fontSize = 32; // rebuilds the glyph mesh on the next draw
label.style.fillColor = Color.tomato; // updates only the mesh tint — no atlas work
Font loading
System fonts (Arial, Times New Roman, etc.) work immediately. For custom web fonts, load a FontFace in the scene’s load hook:
async load(loader) {
await loader.load(FontFace, {
myFont: 'font/MyFont.woff2',
}, { family: 'MyFont' });
}
init(loader) {
this.title = new Text('Custom Font', {
fontFamily: 'MyFont',
fontSize: 48,
fillColor: Color.white,
});
}
The FontFace asset is registered with the document’s document.fonts set. Once loaded, it becomes available to all Text instances via the fontFamily style property.
Style properties
The visual appearance comes from TextStyleOptions. All properties are optional:
| Property | Type | Default | Description |
|---|---|---|---|
fontFamily | string | 'Arial' | CSS font family name |
fontWeight | FontWeight | 'normal' | CSS font weight ('normal', 'bold', or '100'–'900') |
fontStyle | 'normal' | 'italic' | 'normal' | Font style |
fontSize | number | 20 | Font size in pixels |
fillColor | Color | Color.white | Glyph fill colour |
outlineColor | Color | Color.black | SDF outline colour |
outlineWidth | number | 0 | Outline width in SDF units (0–0.5); 0 disables the outline |
align | 'left' | 'center' | 'right' | 'left' | Horizontal alignment |
lineHeight | number | 1.2 | Line-height multiplier on fontSize |
leading | number | 0 | Extra pixel gap between lines |
shadowColor | Color | Color.black | Drop-shadow colour |
shadowOffsetX | number | 0 | Horizontal shadow offset in pixels |
shadowOffsetY | number | 0 | Vertical shadow offset in pixels |
shadowAlpha | number | 0 | Shadow opacity (0–1); 0 disables the shadow |
shadowBlur | number | 0 | Shadow blur softness (0–1) |
gradientColors | [Color, Color] | null | null | Two-stop fill gradient [top, bottom]; overrides fillColor |
gradientAxis | 'vertical' | 'horizontal' | 'vertical' | Gradient orientation |
Text flow and overflow come from LayoutOptions, merged into the same options object:
| Property | Type | Default | Description |
|---|---|---|---|
maxWidth | number | — | Word-wrap boundary in pixels; longer lines break at word boundaries |
maxHeight | number | — | Clip boundary in pixels |
overflow | 'visible' | 'clip' | 'ellipsis' | 'visible' | Behaviour when text exceeds maxHeight |
letterSpacing | number | 0 | Extra pixel gap between glyphs |
breakWords | boolean | false | Break words wider than maxWidth at character boundaries |
whiteSpace | 'normal' | 'pre' | 'pre-line' | 'pre-line' | Whitespace handling |
Glyphs are rasterized into the shared SDF atlas; the mesh tint is initialized from fillColor when the mesh is built.
Multiline text and wrap settings
Multiline text works by embedding \n characters in the string:
import { Color, Text } from '@codexo/exojs';
const dialog = new Text('Line one\nLine two\nLine three', {
fillColor: Color.white,
fontSize: 18,
lineHeight: 1.5,
});
To wrap long runs automatically, set maxWidth (in pixels) — lines that exceed it break at word boundaries. Add breakWords: true to also split individual words that are wider than maxWidth:
import { Color, Text } from '@codexo/exojs';
const longString = 'A long run of text that wraps automatically once it exceeds the layout width.';
const wrapped = new Text(longString, {
fillColor: Color.white,
fontSize: 16,
maxWidth: 400,
breakWords: true,
});
Alignment
The align property controls horizontal positioning relative to the Text node’s local origin:
import { Color, Text } from '@codexo/exojs';
const centered = new Text('Centered Title', {
align: 'center',
fillColor: Color.white,
fontSize: 32,
});
centered.setPosition(400, 20);
A center-aligned text node at (400, 20) centers its glyphs horizontally around x=400 in local space.
Text in the scene graph
Text extends Container, so it carries position, rotation, scale, origin, and anchor. You can rotate text, tint the whole node, apply filters, and nest it inside other containers:
this.ui = new Container();
this.scoreLabel = new Text('Score: 0', { fillColor: Color.white, fontSize: 24 });
this.scoreLabel.setPosition(10, 10);
this.ui.addChild(this.scoreLabel);
this.addChild(this.ui);
The text node’s internal mesh is a Container child. Assigning to text.text rebuilds the glyph mesh. This means:
- Changing text every frame is fine — the mesh is rebuilt on demand, at most once per frame.
- Mutating
stylefields (e.g.text.style.fillColor = ...) is also picked up automatically before the next draw — no manual rebuild needed.
Text constraints
- The glyph atlas is shared across all
Textinstances and has a fixed default size. Large sets of unique glyph/style combinations can exhaust atlas space. Textdoes not expose per-character styling. Use separateTextinstances for mixed-style strings.Textdoes not measure or report its pixel dimensions through the public API — the mesh bounds reflect the glyph quad size but measured width/height in pixels is not exposed as a public getter.- Loader-based
FontFaceloading is the built-in path for custom fonts; any font family available to the browser’s canvas text engine can be used once loaded.
Examples
A text node with a custom web font, updating its string each frame.
Multiline text with alignment and line-height control.
Where to go next
The next chapter, Animation, covers frame-based sprite animation, tweens for interpolated motion, and how to combine manual frame-loop updates with automated tween-driven timing.