PixiJS Accessibility — Making Canvas Content Screen Reader Friendly
In this tutorial, you will learn about PixiJS Accessibility. We cover key concepts, practical examples, and best practices to help you master this topic.
PixiJS accessibility integration allows developers to add ARIA labels, manage keyboard focus, and provide fallback content so screen reader users can interact with canvas-based 2D applications and games.
What You'll Learn
By the end of this tutorial, you will implement PixiJS accessibility hooks, add ARIA attributes to canvas elements, manage focus order for interactive sprites, and test with screen readers.
Why Accessibility Matters
Canvas content is invisible to assistive technology by default. Adding accessibility transforms your PixiJS app from a visual-only experience into one usable by everyone, including users who rely on screen readers or keyboard navigation.
Setting Up Accessibility
PixiJS provides a built-in accessibility plugin that manages focusable elements.
var app = new PIXI.Application({ width: 800, height: 600 });
document.body.appendChild(app.view);
// Enable accessibility plugin
app.accessibility = new PIXI.AccessibilityManager(app.renderer);
The AccessibilityManager intercepts pointer events and dispatches them as keyboard-friendly interactions.
Adding ARIA Labels to Sprites
Use the accessible property and accessibleTitle on interactive sprites.
var button = new PIXI.Graphics();
button.beginFill(0x4ecdc4);
button.drawRoundedRect(0, 0, 200, 50, 10);
button.endFill();
button.interactive = true;
button.buttonMode = true;
// Accessibility
button.accessible = true;
button.accessibleTitle = 'Start Game button';
button.accessibleHint = 'Double-click to begin the game';
app.stage.addChild(button);
button.on('pointertap', function() {
console.log('Game started');
});
Screen readers announce "Start Game button, double-click to begin the game" when focus lands on this element.
Focus Management
Control tab order with accessibleChildren and manage focus with the tabindex attribute on the canvas.
app.view.setAttribute('tabindex', '0');
app.view.setAttribute('role', 'application');
app.view.setAttribute('aria-label', 'Interactive game canvas');
Order child elements by setting their position in the display list. PixiJS traverses children in render order.
var menuItems = [];
for (var i = 0; i < 5; i++) {
var item = new PIXI.Text('Option ' + (i + 1));
item.y = i * 40;
item.interactive = true;
item.accessible = true;
item.accessibleTitle = 'Menu option ' + (i + 1);
app.stage.addChild(item);
menuItems.push(item);
}
The first child receives focus first. Use accessibleChildren to skip decorative elements.
Keyboard Navigation
Wire keyboard events to replicate pointer interactions.
document.addEventListener('keydown', function(e) {
if (e.key === 'Enter' || e.key === ' ') {
var focused = document.activeElement;
if (focused && focused._pixiSprite) {
focused._pixiSprite.emit('pointertap');
}
}
if (e.key === 'ArrowRight') {
moveFocus(1);
}
if (e.key === 'ArrowLeft') {
moveFocus(-1);
}
});
var currentFocusIndex = 0;
function moveFocus(direction) {
var interactive = app.stage.children.filter(function(c) {
return c.accessible && c.interactive;
});
currentFocusIndex = (currentFocusIndex + direction + interactive.length) % interactive.length;
interactive[currentFocusIndex].emit('pointerover');
}
This lets keyboard users navigate sprites without a mouse.
Fallback Content
Provide HTML fallback inside the canvas container for screen readers.
<div id="game-container" aria-label="Interactive 2D game">
<canvas id="game"></canvas>
<div aria-live="polite" id="sr-announcements"></div>
<noscript>
<p>This game requires JavaScript. Please enable JavaScript in your browser.</p>
</noscript>
</div>
var announcer = document.getElementById('sr-announcements');
function announce(text) {
announcer.textContent = '';
setTimeout(function() {
announcer.textContent = text;
}, 100);
}
// Usage
announce('Score updated: 100 points');
The aria-live="polite" region announces changes without interrupting the user.
Accessible Color Contrast
Ensure text and interactive elements meet WCAG AA contrast ratios.
function meetsContrastRatio(foreground, background) {
var fl = relativeLuminance(foreground);
var bl = relativeLuminance(background);
var lighter = Math.max(fl, bl);
var darker = Math.min(fl, bl);
return (lighter + 0.05) / (darker + 0.05) >= 4.5;
}
function relativeLuminance(hex) {
var r = parseInt(hex.slice(1, 3), 16) / 255;
var g = parseInt(hex.slice(3, 5), 16) / 255;
var b = parseInt(hex.slice(5, 7), 16) / 255;
r = r <= 0.03928 ? r / 12.92 : Math.pow((r + 0.055) / 1.055, 2.4);
g = g <= 0.03928 ? g / 12.92 : Math.pow((g + 0.055) / 1.055, 2.4);
b = b <= 0.03928 ? b / 12.92 : Math.pow((b + 0.055) / 1.055, 2.4);
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
console.log(meetsContrastRatio('#ffffff', '#333333')); // true
Use tools like axe DevTools to audit your PixiJS canvas accessibility.
Common Mistakes
1. No ARIA Labels on Interactive Elements
Without accessibleTitle, screen readers announce nothing or generic "canvas" — users cannot distinguish buttons.
2. Missing Keyboard Support
Pointer events alone exclude keyboard-only users. Always wire keydown handlers.
3. Skip Non-Interactive Decorations
Set sprite.accessible = false on background sprites, particles, and decorative elements to avoid focus noise.
4. Poor Focus Order
Relying on display list order without testing tab flow confuses users. Verify logical focus order manually.
5. No Live Regions for Dynamic Content
Score changes, alerts, and new elements must announce via aria-live regions. Silent updates are invisible to screen readers.
Practice Questions
Q1: How do you make a PixiJS sprite accessible?
A: Set sprite.accessible = true and sprite.accessibleTitle = 'description'.
Q2: What does the AccessibilityManager do? A: It intercepts pointer events and maps them to keyboard-accessible interactions.
Q3: How do you handle keyboard focus order?
A: Arrange children in the display list in logical tab order and use tabindex on the canvas.
Q4: What is an aria-live region used for? A: It announces dynamic content changes to screen readers without user action.
Q5: How do you check contrast ratio Compliance? A: Calculate relative luminance of foreground and background and verify the ratio is at least 4.5:1 for AA.
Challenge: Build a PixiJS menu with three buttons (Start, Settings, Credits). Make each button keyboard-focusable with arrow key navigation, ARIA labels, and a live region that announces the currently focused option.
FAQ
What's Next
Now that you have made PixiJS content accessible, explore performance optimization.
PixiJS Performance — Optimize rendering performance.
PixiJS Getting Started — Review the basics.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro