PixiJS Text & Fonts — Text Objects, Bitmap Fonts, and Rich Text Styling
PixiJS text rendering ranges from simple Text objects to high-performance BitmapText for counters. This tutorial covers both approaches and when to use each.
What You’ll Learn
- How to create
Textobjects and style them withTextStyle - How to load and use web fonts with the Font Loading API
- What bitmap fonts are and why they’re faster
- How to compare
TextvsBitmapTextfor different use cases - How to use HTML-like rich text tags for inline styling
- How to generate bitmap fonts programmatically
Why Text & Fonts Matter
Text is everywhere in interactive applications — UI labels, score displays, tooltips, dialog boxes. But rendering text on a GPU is surprisingly expensive because the browser has to rasterize glyphs every time they change.
In Durga Antivirus Pro, we use a mix of both approaches. The security dashboard’s title text and tooltips use Text objects for quality and style flexibility. The real-time threat counter — which updates multiple times per second — uses BitmapText for consistent 60fps performance. Choosing the right text rendering strategy directly impacts how smooth your UI feels.
Learning Path
flowchart LR
GS["Getting Started"] --> GT["Graphics & Textures"]
GT --> AI["Animation & Interactivity"]
AI --> TF["Text & Bitmap Fonts ⬅ You Are Here"]
TF --> PB["Performance & Best Practices"]
style TF fill:#4a90d9,stroke:#fff,color:#fff
linkStyle default stroke:#4a90d9,stroke-width:2
PixiJS Text vs BitmapText vs DOM Text Overlay
| Approach | Performance | Quality | Flexibility |
|---|---|---|---|
| PixiJS Text | Medium — re-rasterizes on change | High — smooth vector text | High — any style, font, size |
| PixiJS BitmapText | Very fast — pre-rendered glyphs | Medium — pixelated at high scale | Low — fixed glyph set |
| DOM overlay (HTML/CSS) | Fast | Highest — native text rendering | Highest — full CSS |
When to use PixiJS text: When text must be part of the rendered scene (behind other graphics, transformed with the scene, or rendered to a texture). For UI overlays, HTML/CSS text is often simpler and sharper.
Text Objects — The Standard Approach
The Text object renders a string of text using a TextStyle configuration. Think of it like a label maker — you type text, pick a style, and it prints a sticker you can place anywhere.
const text = new PIXI.Text('Hello, PixiJS!', {
fontFamily: 'Arial',
fontSize: 48,
fill: 0xff6600,
stroke: 0x000000,
strokeThickness: 4,
});
app.stage.addChild(text);Why it works this way: PixiJS renders the text to an offscreen canvas texture first, then uses that texture as a sprite. This means text can be rotated, scaled, and filtered just like any other sprite.
TextStyle Properties in Detail
| Property | Type | Default | What it does |
|---|---|---|---|
fontFamily | string | 'Arial' | Font name or comma-separated fallback list |
fontSize | number | 26 | Size in pixels |
fill | number/string/array | 0x000000 | Color, gradient string, or array for gradient stops |
stroke | number | 0x000000 | Outline color |
strokeThickness | number | 0 | Width of the outline |
dropShadow | boolean | false | Enable drop shadow effect |
dropShadowBlur | number | 0 | How blurry the shadow is |
dropShadowDistance | number | 5 | How far the shadow is offset |
letterSpacing | number | 0 | Space between letters (positive expands, negative compresses) |
wordWrap | boolean | false | Break long lines automatically |
wordWrapWidth | number | 100 | Maximum width before wrapping |
align | string | 'left' | Horizontal alignment: 'left', 'center', or 'right' |
lineHeight | number | 0 | Override the default line height |
fontStyle | string | 'normal' | 'normal', 'italic', or 'oblique' |
fontWeight | string | 'normal' | 'normal', 'bold', 'bolder', or numeric '700' |
Gradient Fill
You can create gradient text by passing an array of colors:
const text = new PIXI.Text('Gradient', {
fontSize: 64,
fill: ['#ff6600', '#9933ff'], // Array = gradient
fillGradientType: 0, // 0 = vertical, 1 = horizontal
});Why gradients matter: A gradient from orange to purple gives text a premium, modern look — perfect for titles and branded UI elements.
Loading Web Fonts
Web fonts let you use custom typefaces beyond system fonts. The key rule: load the font before creating text.
Using FontFace API
const font = new FontFace('Roboto', 'url(https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu4mxP.ttf)');
font.load().then(() => {
const text = new PIXI.Text('Loaded Web Font', {
fontFamily: 'Roboto, sans-serif',
fontSize: 40,
fill: 0xffffff,
});
app.stage.addChild(text);
});Using CSS + document.fonts.ready
<style>
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap');
</style>
<script>
document.fonts.ready.then(() => {
// Safe to create PixiJS text with Roboto
});
</script>Why wait? If you create text before the web font loads, PixiJS falls back to Arial (or your next fontFamily entry). The text measures and renders with the wrong font, and updating the fontFamily later still works with the wrong metrics.
Bitmap Fonts — Speed Over Flexibility
A bitmap font is a pre-rendered set of glyphs (letters, numbers, symbols) stored as a PNG spritesheet with an XML descriptor. Each glyph is a tiny image.
Think of it like alphabet stickers — every letter is already cut out and ready to use. No rasterization needed.
app.loader
.add('desyrel', 'assets/desyrel.xml')
.load((loader, resources) => {
const text = new PIXI.BitmapText('Bitmap Font!', {
fontName: 'Desyrel',
fontSize: 48,
align: 'center',
});
app.stage.addChild(text);
});BitmapText vs Text — Which One Should You Use?
| Aspect | Text | BitmapText |
|---|---|---|
| How it renders | Generates a canvas texture each time | Uses pre-rendered glyph textures |
| Performance | Slower — re-rasterizes on content/style change | Very fast — no rasterization needed |
| Scaling | Smooth at any size | Pixelated beyond source resolution |
| Best for | Dynamic text, small amounts | High-volume updates, scores, labels |
| File size | No extra assets | XML + PNG (larger initial download) |
| Character set | Any font glyph | Limited to generated characters |
Rule of thumb: If text updates every frame (FPS counter, score display), use BitmapText. If text changes rarely (titles, descriptions), use Text.
Generating BitmapFont Programmatically
You can create a bitmap font from any TextStyle at runtime:
const fontData = PIXI.BitmapFont.from('MyFont', {
fontFamily: 'Arial',
fontSize: 32,
fill: 0xffffff,
stroke: 0x000000,
strokeThickness: 2,
}, {
chars: PIXI.BitmapFont.ASCII, // Character range to include
});
const bmText = new PIXI.BitmapText('Hello!', { fontName: 'MyFont' });Why generate at runtime? This is useful for prototyping — you can switch between Text and BitmapText without creating external assets. For production, pre-generate and load the XML + PNG for faster startup.
Rich Text with HTML-like Tags
Text objects support basic inline styling using HTML-like tags:
const text = new PIXI.Text(
'<b>Bold</b> <i>Italic</i> <span style="color: #ff6600;">Orange</span> normal',
{ fontSize: 32, fontFamily: 'Arial' }
);Supported Tags
| Tag | Effect |
|---|---|
<b> / <strong> | Bold text |
<i> / <em> | Italic text |
<span style="..."> | Inline styles (color, font-size) |
<br> | Line break |
Performance note: Rich text is re-parsed every time the text changes. For frequently updated dynamic text with markup, consider using separate Text objects for each styled segment.
Common Mistakes
| Mistake | Why it happens | How to fix it |
|---|---|---|
| Creating Text objects every frame | Each creation re-rasterizes the canvas texture | Cache the Text object; reuse instead of recreating |
| Web font not applied on first render | PixiJS falls back to system font before web font loads | Wait for document.fonts.ready before creating text |
| Bitmap font missing characters | Generated with wrong character range | Include chars: PIXI.BitmapFont.ASCII or a custom string |
| BitmapText looks pixelated at large sizes | Bitmap font has fixed resolution | Generate at the intended display size |
| Rich text tags not working | Tags must be lowercase and properly formatted | Use <b> not <B>, and style="color: red;" not style='color: red;' |
| Text blurry on Retina displays | Missing resolution and autoDensity in Application config | Set resolution: window.devicePixelRatio and autoDensity: true |
Practice Questions
When should you use
BitmapTextinstead ofText?- For high-frequency updates like score counters and FPS meters.
BitmapTextuses pre-rendered glyphs, avoiding expensive re-rasterization.
- For high-frequency updates like score counters and FPS meters.
Why does web font text sometimes appear in the wrong font initially?
- PixiJS creates the text before the web font loads, falling back to a system font. Always wait for
document.fonts.readyor useFontFace.load().
- PixiJS creates the text before the web font loads, falling back to a system font. Always wait for
What characters does a bitmap font include by default?
- Only the characters that were in the source text during generation. Specify
charsto control the glyph set.
- Only the characters that were in the source text during generation. Specify
How do you add a drop shadow to a Text object?
- Set
dropShadow: true,dropShadowBlur, anddropShadowDistancein the TextStyle.
- Set
Can you use emoji in PixiJS text?
Textsupports emoji if the font does.BitmapTexttypically doesn’t include emoji glyphs unless generated with them.
Challenge: Create an animated score counter that increments from 0 to 9999 using BitmapText. The display should update every frame and hold a stable 60fps.
FAQ
Try It Yourself
Open this text styler in your browser — type text, pick fonts and colors, toggle effects, and compare Text vs BitmapText side by side.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>PixiJS Text Styler</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: #1a1a2e; font-family: system-ui, sans-serif; display: flex; justify-content: center; padding: 20px; }
.app { display: flex; gap: 20px; flex-wrap: wrap; }
canvas { border-radius: 12px; box-shadow: 0 8px 32px rgba(0,0,0,0.4); }
.panel { background: #16213e; border-radius: 12px; padding: 20px; color: #fff; width: 260px; display: flex; flex-direction: column; gap: 12px; }
.panel h2 { font-size: 18px; }
.panel label { font-size: 13px; color: #a8b2d1; }
.panel input[type="range"] { width: 100%; accent-color: #4a90d9; }
.panel input[type="color"] { width: 100%; height: 36px; border: none; border-radius: 6px; cursor: pointer; }
.panel input[type="text"] { width: 100%; padding: 8px; border-radius: 6px; border: none; background: #0f3460; color: #fff; }
.panel select { width: 100%; padding: 8px; border-radius: 6px; border: none; background: #0f3460; color: #fff; }
.toggle { display: flex; align-items: center; gap: 10px; }
.toggle input[type="checkbox"] { width: 18px; height: 18px; accent-color: #4a90d9; }
.stats { font-size: 12px; color: #8892b0; }
</style>
</head>
<body>
<div class="app">
<div id="canvas-container"></div>
<div class="panel">
<h2>Text Styler</h2>
<label>Text</label>
<input type="text" id="textInput" value="Hello PixiJS!" />
<label>Font Family</label>
<select id="fontFamily">
<option value="Arial">Arial</option>
<option value="Georgia">Georgia</option>
<option value="Courier New">Courier New</option>
<option value="Verdana">Verdana</option>
</select>
<label>Font Size <span id="sizeVal">48</span>px</label>
<input type="range" id="fontSize" min="12" max="120" value="48" />
<label>Fill Color</label>
<input type="color" id="fillColor" value="#ff6600" />
<label>Stroke Color</label>
<input type="color" id="strokeColor" value="#000000" />
<label>Stroke <span id="strokeVal">2</span></label>
<input type="range" id="strokeThickness" min="0" max="12" value="2" />
<div class="toggle">
<input type="checkbox" id="chkShadow" />
<label for="chkShadow">Drop Shadow</label>
</div>
<label>Letter Spacing <span id="spacingVal">0</span></label>
<input type="range" id="letterSpacing" min="-5" max="20" value="0" />
<div class="toggle">
<input type="checkbox" id="chkBold" />
<label for="chkBold">Bold</label>
</div>
<div class="toggle">
<input type="checkbox" id="chkItalic" />
<label for="chkItalic">Italic</label>
</div>
<div class="stats">Bitmap: <span id="bitmapStats">off</span></div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/pixi.js@7.x/dist/pixi.min.js"></script>
<script>
const app = new PIXI.Application({ width: 600, height: 500, backgroundColor: 0x1a1a2e, antialias: true });
document.getElementById('canvas-container').appendChild(app.view);
let textObj = null, bitmapTextObj = null;
function rebuildText() {
const content = document.getElementById('textInput').value || ' ';
const fontFamily = document.getElementById('fontFamily').value;
const fontSize = parseInt(document.getElementById('fontSize').value);
const fill = parseInt(document.getElementById('fillColor').value.slice(1), 16);
const stroke = parseInt(document.getElementById('strokeColor').value.slice(1), 16);
const strokeThickness = parseFloat(document.getElementById('strokeThickness').value);
const dropShadow = document.getElementById('chkShadow').checked;
const letterSpacing = parseFloat(document.getElementById('letterSpacing').value);
const bold = document.getElementById('chkBold').checked;
const italic = document.getElementById('chkItalic').checked;
const style = {
fontFamily, fontSize, fill, stroke, strokeThickness, dropShadow,
dropShadowBlur: 4, dropShadowDistance: 4, dropShadowColor: 0x000000,
letterSpacing, fontWeight: bold ? 'bold' : 'normal', fontStyle: italic ? 'italic' : 'normal',
};
if (textObj) { app.stage.removeChild(textObj); textObj.destroy(); }
if (bitmapTextObj) { app.stage.removeChild(bitmapTextObj); bitmapTextObj.destroy(); }
textObj = new PIXI.Text(content, style);
textObj.x = 300; textObj.y = 180; textObj.anchor.set(0.5);
app.stage.addChild(textObj);
try {
const bmName = `BM_${fontFamily}_${fontSize}`;
if (!PIXI.BitmapFont.available[bmName]) {
PIXI.BitmapFont.from(bmName, style, { chars: PIXI.BitmapFont.ASCII });
}
bitmapTextObj = new PIXI.BitmapText(content, { fontName: bmName, fontSize });
bitmapTextObj.x = 300; bitmapTextObj.y = 360; bitmapTextObj.anchor.set(0.5);
if (bitmapTextObj.text !== content) {
app.stage.removeChild(bitmapTextObj); bitmapTextObj.destroy(); bitmapTextObj = null;
document.getElementById('bitmapStats').textContent = 'unsupported chars';
} else {
app.stage.addChild(bitmapTextObj);
document.getElementById('bitmapStats').textContent = 'rendered';
}
} catch(e) {
document.getElementById('bitmapStats').textContent = 'error';
}
}
document.getElementById('textInput').addEventListener('input', rebuildText);
document.getElementById('fontFamily').addEventListener('change', rebuildText);
document.getElementById('fontSize').addEventListener('input', e => { document.getElementById('sizeVal').textContent = e.target.value; rebuildText(); });
document.getElementById('fillColor').addEventListener('input', rebuildText);
document.getElementById('strokeColor').addEventListener('input', rebuildText);
document.getElementById('strokeThickness').addEventListener('input', e => { document.getElementById('strokeVal').textContent = e.target.value; rebuildText(); });
document.getElementById('chkShadow').addEventListener('change', rebuildText);
document.getElementById('letterSpacing').addEventListener('input', e => { document.getElementById('spacingVal').textContent = e.target.value; rebuildText(); });
document.getElementById('chkBold').addEventListener('change', rebuildText);
document.getElementById('chkItalic').addEventListener('change', rebuildText);
rebuildText();
window.addEventListener('resize', () => {
const w = Math.min(window.innerWidth - 320, 600); const h = Math.min(window.innerHeight - 40, 500);
app.renderer.resize(Math.max(w, 200), Math.max(h, 200));
});
</script>
</body>
</html>What’s Next
| Topic | Link |
|---|---|
| Performance and best practices | https://tutorials.dodatech.com/frontend/libraries/pixijs/pixijs-performance/ |
| Animation and interactivity | https://tutorials.dodatech.com/frontend/libraries/pixijs/pixijs-animation-interactivity/ |
| CSS and HTML text overlays | CSS Text Styling |
| Professional font loading | JavaScript Font Loading API |
Related Tutorials
- https://tutorials.dodatech.com/frontend/libraries/pixijs/pixijs-getting-started/ — review the basics
- https://tutorials.dodatech.com/frontend/libraries/pixijs/pixijs-graphics-textures/ — textures for bitmap font glyphs
- WebGL Performance — text rendering optimizations
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro. Durga Antivirus Pro uses BitmapText for real-time threat counters that update every second without a single frame drop.
What’s Next
Congratulations on completing this Pixijs Text Fonts tutorial! Here’s where to go from here:
- Practice daily — Consistency is more important than long study sessions
- Build a project — Apply what you learned by building something real
- Explore related topics — Check out other tutorials in the same category
- Join the community — Discuss with other learners and share your progress
Remember: every expert was once a beginner. Keep coding!
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro