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 TextStyleOptions object — all properties are optional, all have defaults. The TextStyle is stored as text.style. Assign a new style to rebuild the mesh; mutating style fields alone does not rebuild:
import { Color, TextStyle } from '@codexo/exojs';
label.style.fontSize = 32;
label.style.fillColor = Color.tomato;
label.style = label.style; // apply style changes and rebuild
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,
fill: '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
TextStyle exposes these configurable properties:
| Property | Type | Default | Description |
|---|---|---|---|
fontFamily | string | 'Arial' | CSS font family name |
fontSize | number | 20 | Font size in pixels |
fontWeight | string | 'bold' | CSS font weight |
fontStyle | 'normal' | 'italic' | 'normal' | Font style |
fill | string | CanvasGradient | CanvasPattern | 'black' | Stored style property (not currently applied by the renderer) |
fillColor | Color | Color.white | Tint used when building the text mesh |
stroke | string | CanvasGradient | CanvasPattern | 'black' | Stored style property (not currently applied by the renderer) |
strokeThickness | number | 1 | Stored style property (not currently applied by the renderer) |
align | 'left' | 'center' | 'right' | 'left' | Text alignment |
lineHeight | number | 1.2 | Multiplier on fontSize for vertical spacing |
wordWrap | boolean | false | Style flag (currently not applied by the layout engine) |
wordWrapWidth | number | 100 | Style width hint (currently not applied by the layout engine) |
baseline | CanvasTextBaseline | 'alphabetic' | Stored style property (not currently applied by the renderer) |
lineJoin | CanvasLineJoin | 'miter' | Stored style property (not currently applied by the renderer) |
miterLimit | number | 10 | Stored style property (not currently applied by the renderer) |
padding | number | 0 | Stored style property (not currently applied by the renderer) |
Current renderer path applies fontFamily, fontSize, fontWeight, fontStyle, align, lineHeight, and fillColor. Other TextStyle fields are currently stored but not applied.
Glyphs are rasterized in white, and the mesh tint is initialized from fillColor when the mesh is rebuilt.
Multiline text and wrap settings
Multiline text works by embedding \n characters in the string:
const dialog = new Text('Line one\nLine two\nLine three', {
fill: 'white',
fontSize: 18,
lineHeight: 1.5,
});
wordWrap and wordWrapWidth are available on TextStyle, but the current text layout only applies explicit line breaks (\n) and alignment:
const wrapped = new Text(longString, {
fill: 'white',
fontSize: 16,
wordWrap: true,
wordWrapWidth: 400,
});
Alignment
The align property controls horizontal positioning relative to the Text node’s local origin:
const centered = new Text('Centered Title', {
align: 'center',
fill: 'white',
fontSize: 32,
});
centered.setPosition(canvasWidth / 2, 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', { fill: '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 destroys the previous mesh and builds a new one. This means:
- Changing text every frame is fine — the old mesh is discarded and a fresh one is built.
- Mutating
stylefields alone does not rebuild the mesh. - To apply style changes, assign
text.style = ...so the mesh is rebuilt.
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.