Skip to content

PixiJS Text & Fonts — Text Objects, Bitmap Fonts, and Rich Text Styling

DodaTech Updated 2026-06-06 12 min read

In this tutorial, you'll learn about PixiJS Text & Fonts. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

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 Text objects and style them with TextStyle
  • How to load and use web fonts with the Font Loading API
  • What bitmap fonts are and why they're faster
  • How to compare Text vs BitmapText for 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.

â„šī¸ Info

Prerequisites: Complete {{< ref "/frontend/libraries/pixijs/pixijs-getting-started" >}} and the animation tutorial {{< ref "/frontend/libraries/pixijs/pixijs-animation-interactivity" >}}. You need to understand the Application, stage, and ticker.

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

  1. When should you use BitmapText instead of Text?

    • For high-frequency updates like score counters and FPS meters. BitmapText uses pre-rendered glyphs, avoiding expensive re-rasterization.
  2. 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.ready or use FontFace.load().
  3. What characters does a bitmap font include by default?

    • Only the characters that were in the source text during generation. Specify chars to control the glyph set.
  4. How do you add a drop shadow to a Text object?

    • Set dropShadow: true, dropShadowBlur, and dropShadowDistance in the TextStyle.
  5. Can you use emoji in PixiJS text?

    • Text supports emoji if the font does. BitmapText typically 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

When should I use `BitmapText` over `Text`?

: Use BitmapText for high-frequency updates (score counters, FPS meters, UI labels). Use Text for infrequent updates that need smooth scaling and rich styling.

Can I use emoji in PixiJS text?

: Text supports emoji if the font does. BitmapText typically does not include emoji glyphs.

How do I add a custom font file (.ttf / .woff)?

: Load it via CSS @font-face or the FontFace API. Wait for load completion before creating PixiJS text.

Why is my text blurry on Retina displays?

: Set resolution: window.devicePixelRatio in your Application config with autoDensity: true.

Does PixiJS support right-to-left text?

: Text objects respect Unicode directionality. For proper RTL layout, set align: 'right' and ensure the string uses RTL markers.

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 {{< ref "/frontend/libraries/pixijs/pixijs-performance" >}}
Animation and interactivity {{< ref "/frontend/libraries/pixijs/pixijs-animation-interactivity" >}}
CSS and HTML text overlays CSS Text Styling
Professional font loading JavaScript Font Loading API
  • {{< ref "/frontend/libraries/pixijs/pixijs-getting-started" >}} — review the basics
  • {{< ref "/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