Skip to content
PixiJS Text & Fonts — Text Objects, Bitmap Fonts, and Rich Text Styling

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

DodaTech Updated Jun 6, 2026 11 min read

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.

Prerequisites: Complete https://tutorials.dodatech.com/frontend/libraries/pixijs/pixijs-getting-started/ and the animation tutorial https://tutorials.dodatech.com/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

ApproachPerformanceQualityFlexibility
PixiJS TextMedium — re-rasterizes on changeHigh — smooth vector textHigh — any style, font, size
PixiJS BitmapTextVery fast — pre-rendered glyphsMedium — pixelated at high scaleLow — fixed glyph set
DOM overlay (HTML/CSS)FastHighest — native text renderingHighest — 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

PropertyTypeDefaultWhat it does
fontFamilystring'Arial'Font name or comma-separated fallback list
fontSizenumber26Size in pixels
fillnumber/string/array0x000000Color, gradient string, or array for gradient stops
strokenumber0x000000Outline color
strokeThicknessnumber0Width of the outline
dropShadowbooleanfalseEnable drop shadow effect
dropShadowBlurnumber0How blurry the shadow is
dropShadowDistancenumber5How far the shadow is offset
letterSpacingnumber0Space between letters (positive expands, negative compresses)
wordWrapbooleanfalseBreak long lines automatically
wordWrapWidthnumber100Maximum width before wrapping
alignstring'left'Horizontal alignment: 'left', 'center', or 'right'
lineHeightnumber0Override the default line height
fontStylestring'normal''normal', 'italic', or 'oblique'
fontWeightstring'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?

AspectTextBitmapText
How it rendersGenerates a canvas texture each timeUses pre-rendered glyph textures
PerformanceSlower — re-rasterizes on content/style changeVery fast — no rasterization needed
ScalingSmooth at any sizePixelated beyond source resolution
Best forDynamic text, small amountsHigh-volume updates, scores, labels
File sizeNo extra assetsXML + PNG (larger initial download)
Character setAny font glyphLimited 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

TagEffect
<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

MistakeWhy it happensHow to fix it
Creating Text objects every frameEach creation re-rasterizes the canvas textureCache the Text object; reuse instead of recreating
Web font not applied on first renderPixiJS falls back to system font before web font loadsWait for document.fonts.ready before creating text
Bitmap font missing charactersGenerated with wrong character rangeInclude chars: PIXI.BitmapFont.ASCII or a custom string
BitmapText looks pixelated at large sizesBitmap font has fixed resolutionGenerate at the intended display size
Rich text tags not workingTags must be lowercase and properly formattedUse <b> not <B>, and style="color: red;" not style='color: red;'
Text blurry on Retina displaysMissing resolution and autoDensity in Application configSet 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

TopicLink
Performance and best practiceshttps://tutorials.dodatech.com/frontend/libraries/pixijs/pixijs-performance/
Animation and interactivityhttps://tutorials.dodatech.com/frontend/libraries/pixijs/pixijs-animation-interactivity/
CSS and HTML text overlaysCSS Text Styling
Professional font loadingJavaScript 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