Lightning 3 Renderer supports two types of fonts for rendering text: Canvas-based web fonts and SDF (Signed Distance Field) fonts. This guide covers how to load, install, and use both font types.
The Renderer's Stage exposes a Font Manager that handles font installation and management. You can install fonts at application startup to ensure they're ready when needed.
| Font Type | Renderer | Benefits | Use Case |
|---|---|---|---|
| Canvas Web Fonts | WebGL ✅ Canvas ✅ | Universal web font format support (.ttf, .woff, .woff2, etc.) | General text rendering, standard typography |
| SDF Fonts | WebGL ✅ Canvas ❌ | Better performance for scaled text, supports visual effects, crisp at any scale | Advanced typography, effects, large-scale text |
Note: The Canvas renderer only supports Canvas web fonts. The WebGL renderer supports both types.
Canvas web fonts use standard web font formats and provide universal compatibility with any font you can use on the web.
import { RendererMain } from '@lightningjs/renderer';
import { WebGlCoreRenderer } from '@lightningjs/renderer/webgl';
import { CanvasTextRenderer } from '@lightningjs/renderer/canvas';
const renderer = new RendererMain(
{
appWidth: 1920,
appHeight: 1080,
renderEngine: WebGlCoreRenderer,
fontEngines: [CanvasTextRenderer], // Enable Canvas font rendering
// ...Other Renderer Config
},
'app',
);
// Load a Canvas web font at startup
const stage = renderer.stage;
await stage.loadFont('canvas', {
fontFamily: 'MyFont',
fontUrl: '/fonts/my-font.ttf',
});await stage.loadFont('canvas', {
// Required
fontFamily: string; // Name for the font (used in text rendering)
fontUrl: string; // URL to the font file (.ttf, .woff, .woff2, etc.)
// Optional
metrics?: {
ascender: number; // Height above baseline (in font units)
descender: number; // Depth below baseline (in font units)
lineGap: number; // Gap between lines (in font units)
unitsPerEm: number; // Font's units per em (typically 1000 or 2048)
};
});// Load a standard web font
await stage.loadFont('canvas', {
fontFamily: 'RobotoRegular',
fontUrl: '/fonts/Roboto-Regular.ttf',
});
// Load with custom metrics for precise layout
await stage.loadFont('canvas', {
fontFamily: 'RobotoWithMetrics',
fontUrl: '/fonts/Roboto-Regular.ttf',
metrics: {
ascender: 800,
descender: -200,
lineGap: 0,
unitsPerEm: 1000,
},
});
// Use the loaded font in your scene
const text = new TextNode(stage, {
fontFamily: 'RobotoRegular',
text: 'Hello, World!',
fontSize: 24,
});.ttf– TrueType Font (most compatible).woff– Web Open Font Format (compressed).woff2– Web Open Font Format 2 (best compression).otf– OpenType Font.eot– Embedded OpenType (legacy)
SDF (Signed Distance Field) fonts offer superior performance for scaled text and support advanced visual effects. They require pre-generated atlas files but deliver excellent quality at any scale.
- Better Performance: Optimized for WebGL rendering
- Scalability: Crisp rendering at any text size without re-rasterizing
- Effects Support: Supports outlines, shadows, and other effects
- Consistent Sizing: No font rendering inconsistencies across browsers
import { RendererMain } from '@lightningjs/renderer';
import {
WebGlCoreRenderer,
SdfTextRenderer,
} from '@lightningjs/renderer/webgl';
const renderer = new RendererMain(
{
appWidth: 1920,
appHeight: 1080,
renderEngine: WebGlCoreRenderer,
fontEngines: [SdfTextRenderer], // Enable SDF font rendering
// ...Other Renderer Config
},
'app',
);
// Load an SDF font at startup
const stage = renderer.stage;
await stage.loadFont('sdf', {
fontFamily: 'MySdfFont',
atlasUrl: '/fonts/my-font-atlas.png',
atlasDataUrl: '/fonts/my-font-data.json',
});await stage.loadFont('sdf', {
// Required
fontFamily: string; // Name for the font (used in text rendering)
atlasUrl: string; // URL to the SDF atlas image (.png)
atlasDataUrl: string; // URL to the SDF glyph data (.json)
// Optional
metrics?: {
ascender: number; // Height above baseline
descender: number; // Depth below baseline
lineGap: number; // Gap between lines
unitsPerEm: number; // Font's units per em
};
});// Load an SDF font
await stage.loadFont('sdf', {
fontFamily: 'RobotoSDF',
atlasUrl: '/fonts/roboto-sdf-atlas.png',
atlasDataUrl: '/fonts/roboto-sdf-data.json',
});
// Use the loaded SDF font
const text = new TextNode(stage, {
fontFamily: 'RobotoSDF',
text: 'Hello with effects!',
fontSize: 32,
effects: [
{
type: 'outline',
width: 2,
color: 0xff0000ff,
},
],
});To generate SDF atlas files from a TrueType font, use tools like:
- Hiero – BMFont tool (Windows/Mac/Linux)
- msdfgen – Multi-channel SDF generator (command-line)
- FontForge – Font editor with SDF export
Example using msdfgen:
# Generate MSDF (multi-channel signed distance field)
msdfgen -font myfont.ttf -charset charset.txt -size 32 -o atlas.png -json data.jsonIt's recommended to load and install fonts at application startup before rendering:
import { RendererMain } from '@lightningjs/renderer';
import {
WebGlCoreRenderer,
SdfTextRenderer,
} from '@lightningjs/renderer/webgl';
import { CanvasTextRenderer } from '@lightningjs/renderer/canvas';
async function initializeApp() {
const renderer = new RendererMain(
{
appWidth: 1920,
appHeight: 1080,
renderEngine: WebGlCoreRenderer,
fontEngines: [SdfTextRenderer, CanvasTextRenderer],
// ...Other Renderer Config
},
'app', // id of div to insert Canvas
);
const stage = renderer.stage;
// Load all fonts before rendering
await Promise.all([
// Canvas web fonts
stage.loadFont('canvas', {
fontFamily: 'OpenSans',
fontUrl: '/fonts/OpenSans-Regular.ttf',
}),
stage.loadFont('canvas', {
fontFamily: 'OpenSansBold',
fontUrl: '/fonts/OpenSans-Bold.ttf',
}),
// SDF fonts for high-quality text
stage.loadFont('sdf', {
fontFamily: 'RobotoSDF',
atlasUrl: '/fonts/roboto-atlas.png',
atlasDataUrl: '/fonts/roboto-atlas.json',
}),
]);
// Fonts loaded; ready for rendering
console.log('Fonts ready!');
// Create your scene here
// ...
}
initializeApp().catch(console.error);Custom font metrics allow you to fine-tune text layout and sizing for specific fonts that may have non-standard metrics.
If you don't specify metrics, the Renderer uses default values based on the font format. Most fonts work well with defaults.
// Load font with custom metrics for precise control
await renderer.stage.loadFont('canvas', {
fontFamily: 'CustomFont',
fontUrl: '/fonts/custom.ttf',
metrics: {
ascender: 900, // Height above baseline
descender: -225, // Depth below baseline
lineGap: 100, // Space between lines
unitsPerEm: 1024, // Font's coordinate system units
},
});To determine correct metrics:
- Use your font editor (FontForge, etc.) to check design metrics
- Check the font's OpenType specification
- Test rendering and adjust experimentally
- Use browser DevTools to measure rendered text
- Pros: Universal format support, no pre-processing required
- Cons: Slower at large scales, potential inconsistencies across browsers
- Best for: Variable text, standard typography
- Pros: Optimized rendering, consistent quality, supports effects
- Cons: Requires pre-generated atlas files, larger file size
- Best for: App titles, large text, text with effects
- Load fonts early: Install all fonts before rendering starts
- Use appropriate font type:
- Canvas fonts for dynamic, varying text
- SDF fonts for large-scale, effects-heavy text
- Limit font variety: Fewer unique fonts = better performance
- Compress font files: Use .woff2 format for smaller downloads
- Cache fonts: Browser will cache font files; leverage this
Error: Font failed to load at URL
Solutions:
- Verify font file URL is correct and accessible
- Check browser console for CORS issues
- Ensure font format is supported
- Try a different font format (.ttf instead of .woff2)
Text appears blurry or pixelated
Solutions:
- For Canvas fonts: Use SDF fonts for better quality at large scales
- Check font size and device pixel ratio
- Verify metrics are correct for your font
SDF atlas or data file not found
Solutions:
- Verify atlas and data file URLs are correct
- Ensure both PNG and JSON files are present
- Check that files are synchronized (generated from same font)
Rendering is slow, especially with many text nodes
Solutions:
- Use SDF fonts instead of Canvas fonts for better performance
- Reduce number of unique fonts
- Batch text updates instead of rendering individually
- Profile with browser DevTools to identify bottleneck
// Load additional fonts on demand
async function loadAdditionalFont(fontName: string, url: string) {
await renderer.stage.loadFont('canvas', {
fontFamily: fontName,
fontUrl: url,
});
console.log(`Font ${fontName} loaded successfully`);
}
// Later in your app
await loadAdditionalFont('DynamicFont', '/fonts/dynamic.ttf');// Define multiple fonts for fallback scenario
const fonts = ['PreferredFont', 'FallbackFont', 'SystemFont'];
// Try loading preferred font; fall back if unavailable
for (const fontName of fonts) {
try {
const text = new TextNode(stage, {
fontFamily: fontName,
text: 'Some text',
});
console.log(`Using font: ${fontName}`);
break;
} catch (e) {
console.warn(`Font ${fontName} not available, trying next...`);
}
}- Platform Architecture – Understanding renderer platforms and font support
- Browser Support – Font support by browser version
- Getting Started – Installation and setup
- API Documentation – Complete Font API reference
- LightningJS Typography Guide – Application-level font usage