PixiJS Getting Started — Complete Beginner's Guide to 2D WebGL
PixiJS is a fast 2D WebGL renderer that creates rich interactive graphics using hardware acceleration. In this guide you’ll build your first scene from scratch.
What You’ll Learn
- What PixiJS is and why it beats plain Canvas for 2D graphics
- How to install PixiJS via npm or CDN
- How the
Application, renderer, and stage work together - How to create sprites from textures and position them
- How to build a responsive interactive scene builder
Why PixiJS Matters
Modern web applications need smooth, hardware-accelerated 2D graphics. PixiJS is the most popular WebGL 2D framework, used by thousands of games, data dashboards, and security visualization tools.
At DodaTech, we use PixiJS in Durga Antivirus Pro to render real-time threat maps, live network traffic flows, and animated security dashboards. When a threat is detected, the UI animates a heatmap overlay using PixiJS sprites — all at 60fps. This is the kind of performance you can only get with GPU-accelerated rendering.
Learning Path
flowchart LR
GS["Getting Started ⬅ You Are Here"] --> GT["Graphics & Textures"]
GT --> AI["Animation & Interactivity"]
AI --> TF["Text & Bitmap Fonts"]
TF --> PB["Performance & Best Practices"]
style GS fill:#4a90d9,stroke:#fff,color:#fff
linkStyle default stroke:#4a90d9,stroke-width:2
PixiJS vs Canvas API vs Raw WebGL
| Feature | PixiJS | Canvas 2D API | Raw WebGL |
|---|---|---|---|
| Ease of use | Easy — scene graph API | Moderate — imperative drawing | Hard — shaders & buffers |
| GPU acceleration | Yes (WebGL) | Software rasterization | Yes |
| Draw calls | Automatic batching | Manual | Manual |
| Filters & effects | Built-in | None natively | You write shaders |
| Best for | Games, dashboards, tools | Simple 2D drawings | 3D or custom rendering |
When to use PixiJS: You need smooth 60fps rendering with hundreds of sprites, interactive elements, and visual effects — without writing shader code.
How PixiJS Works — The Big Picture
Think of PixiJS like a movie projector and your code like the film reel:
- The renderer is the projector — it takes your scene and draws it on screen
- The stage is the screen where everything appears
- Sprites are paper cutouts you place on the screen
- Textures are the stickers you stick onto those cutouts
Every frame, the projector (renderer) scans the screen (stage), checks where all the cutouts (sprites) are, and draws them. The magic is that the projector uses the GPU to do this extremely fast.
Setting Up PixiJS
Via npm (for build tools)
npm install pixi.jsThen in your JavaScript file:
import * as PIXI from 'pixi.js';Via CDN (for HTML pages)
<script src="https://cdn.jsdelivr.net/npm/pixi.js@7.x/dist/pixi.min.js"></script>After this, the global PIXI object is available. No build step needed — perfect for prototyping and tutorials.
The Application — Your Starting Point
The Application class is like a starter kit that creates three things at once:
- A renderer (the projector) — connected to a
<canvas>element - A stage (the screen) — a root container for all objects
- A ticker (the clock) — runs your animation code every frame
const app = new PIXI.Application({
width: 800,
height: 600,
backgroundColor: 0x1099bb,
resolution: window.devicePixelRatio || 1,
autoDensity: true,
});
document.body.appendChild(app.view); // app.view is the <canvas>
Let’s break this down:
| Option | What it does | Why it matters |
|---|---|---|
width / height | Canvas size in CSS pixels | Defines your drawing area |
backgroundColor | Hex color of the background | 0x1099bb is a nice blue |
resolution | Pixel density multiplier | devicePixelRatio makes text sharp on Retina displays |
autoDensity | Adjust CSS size automatically | Prevents blurry rendering on HiDPI screens |
Why set resolution? Without it, your crisp vector graphics look blurry on Retina Macs and high-end phones. The devicePixelRatio tells PixiJS to render at 2x or 3x the CSS size, then shrink it back — giving you razor-sharp visuals.
Renderer Types — What Runs Under the Hood
PixiJS tries WebGL2 first, then WebGL1, then falls back to Canvas 2D. You don’t need to pick — it just works.
// Force Canvas mode if needed
const app = new PIXI.Application({ forceCanvas: true });
// Check at runtime
console.log(app.renderer.type); // 1 = WebGL, 2 = Canvas
Why does the renderer type matter? WebGL runs on the GPU, which is optimized for parallel drawing operations. Canvas 2D runs on the CPU, which is slower. For a threat visualization dashboard in Durga Antivirus Pro, WebGL means we can animate hundreds of threat nodes simultaneously without dropping frames.
The Stage — Your Root Container
The stage is a Container that acts as the root of your scene graph. Think of it like the root folder on your computer — everything you create lives inside it.
const container = new PIXI.Container();
app.stage.addChild(container);Scene graph means: when you move a parent container, all its children move with it. This is like picking up a tray of drinks — all the cups move together.
Sprites & Textures — Paper Cutouts with Stickers
A sprite is a display object that shows an image. A texture is the image data that the sprite displays.
The analogy: a texture is a sticker, and a sprite is the paper cutout you put the sticker on. You can move the cutout around, rotate it, or scale it — the sticker moves with it.
Loading a texture from a URL
const texture = PIXI.Texture.from('https://pixijs.com/assets/bunny.png');
const bunny = new PIXI.Sprite(texture);
app.stage.addChild(bunny);Three steps:
Texture.from()— load the image and wrap it as a texture (the sticker)new PIXI.Sprite(texture)— create a sprite (the cutout) with that stickerapp.stage.addChild(bunny)— place the cutout on the screen
Loading from a local file (Webpack / Vite)
import bunnyImg from './assets/bunny.png';
const texture = PIXI.Texture.from(bunnyImg);Positioning, Rotation & Scale
Once you have a sprite, you can control its position, rotation, and scale like this:
const sprite = new PIXI.Sprite(texture);
sprite.x = 100; // move 100px right
sprite.y = 200; // move 200px down
sprite.rotation = Math.PI / 4; // rotate 45 degrees
sprite.scale.set(2); // make it 2x bigger
sprite.anchor.set(0.5); // center the rotation point
The anchor is important. By default, anchor is at (0,0) — the top-left corner. This means rotation and scale happen around the top-left corner, which looks weird. Setting anchor.set(0.5) centers the pivot point, so rotation spins the sprite around its middle — like a spinning coin.
Responsive Auto-Resize
Make your canvas adapt to window size changes:
window.addEventListener('resize', () => {
app.renderer.resize(window.innerWidth, window.innerHeight);
});For a fixed aspect ratio (e.g., 16:9):
const resize = () => {
const ratio = 16 / 9;
let w = window.innerWidth;
let h = window.innerHeight;
if (w / h > ratio) w = h * ratio;
else h = w / ratio;
app.renderer.resize(w, h);
};
window.addEventListener('resize', resize);
resize();Common Mistakes
| Mistake | Why it happens | How to fix it |
|---|---|---|
| Sprites appear blurry | Missing autoDensity and resolution | Always set resolution: window.devicePixelRatio and autoDensity: true |
| Rotation looks wrong | Anchor is at (0,0) instead of center | Call sprite.anchor.set(0.5) before rotating |
| Image not showing | Texture URL is wrong or CORS blocked | Check the console for 404 errors; use same-origin or CORS-enabled URLs |
| Width/height is 0 | You checked dimensions before first render | Make sure the sprite is added to stage and a frame has passed |
| Canvas is tiny or huge | Forgot to call document.body.appendChild(app.view) | Always append the canvas to the DOM |
| Code runs before texture loads | Texture.from() is async for remote URLs | Use app.loader or ensure the image is cached |
Practice Questions
What does
anchor.set(0.5)do and why is it useful?- It sets the pivot point to the center of the sprite. Without it, rotation and scale happen around the top-left corner.
What is the difference between WebGL and Canvas mode in PixiJS?
- WebGL uses the GPU for hardware-accelerated rendering. Canvas mode renders on the CPU and is significantly slower.
Why does
app.renderer.resize()matter for responsive design?- Without resize, the canvas stays at its initial size when the browser window changes, causing clipping or empty space.
What three things does the
Applicationconstructor create?- A renderer (projector), a stage (screen), and a ticker (clock).
How do you share the same image across multiple sprites efficiently?
- Create one
Textureand pass it to multipleSpriteinstances. PixiJS batches them into a single draw call.
- Create one
Challenge: Build a scene with 5 sprites positioned around a circle. Each sprite should face the center. Hint: use Math.sin and Math.cos with anchor.set(0.5).
FAQ
Try It Yourself
Open the HTML below in your browser to run a fully interactive PixiJS scene builder. No build tools needed — just save and open.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>PixiJS Getting Started — Scene Builder</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: 240px; display: flex; flex-direction: column; gap: 14px; }
.panel h2 { font-size: 18px; margin-bottom: 4px; }
.panel label { font-size: 13px; color: #a8b2d1; }
.panel input[type="range"] { width: 100%; accent-color: #4a90d9; }
.panel input[type="color"] { width: 100%; height: 40px; border: none; border-radius: 6px; cursor: pointer; }
.btn { padding: 10px; border: none; border-radius: 8px; background: #4a90d9; color: #fff; font-weight: 600; cursor: pointer; }
.btn:hover { background: #357abd; }
.btn.danger { background: #e63946; }
.stats { font-size: 12px; color: #8892b0; }
</style>
</head>
<body>
<div class="app">
<div id="canvas-container"></div>
<div class="panel">
<h2>Scene Builder</h2>
<label>Rotation <span id="rotVal">0</span>°</label>
<input type="range" id="rotation" min="0" max="360" value="0" />
<label>Background Color</label>
<input type="color" id="bgColor" value="#1099bb" />
<label>Opacity</label>
<input type="range" id="opacity" min="0" max="1" step="0.05" value="1" />
<button class="btn" id="addBtn">+ Add Sprite</button>
<button class="btn danger" id="clearBtn">Clear All</button>
<div class="stats">Sprites: <span id="spriteCount">0</span></div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/pixi.js@7.x/dist/pixi.min.js"></script>
<script>
const ASSETS = [
'https://pixijs.com/assets/bunny.png',
'https://pixijs.com/assets/eggHead.png',
'https://pixijs.com/assets/flowerTop.png',
'https://pixijs.com/assets/goblin.png',
'https://pixijs.com/assets/helix.png',
];
const app = new PIXI.Application({
width: 800, height: 600,
backgroundColor: 0x1099bb,
antialias: true,
resolution: window.devicePixelRatio || 1,
autoDensity: true,
});
document.getElementById('canvas-container').appendChild(app.view);
let sprites = [];
let selectedIndex = 0;
function addSprite() {
const url = ASSETS[selectedIndex % ASSETS.length];
const sprite = new PIXI.Sprite(PIXI.Texture.from(url));
sprite.anchor.set(0.5);
sprite.x = Math.random() * 700 + 50;
sprite.y = Math.random() * 500 + 50;
sprite.scale.set(Math.random() * 1.5 + 0.5);
sprite.rotation = Math.random() * Math.PI * 2;
app.stage.addChild(sprite);
sprites.push(sprite);
document.getElementById('spriteCount').textContent = sprites.length;
selectedIndex++;
}
document.getElementById('rotation').addEventListener('input', e => {
const deg = parseFloat(e.target.value);
document.getElementById('rotVal').textContent = deg;
sprites.forEach(s => { s.rotation = (deg * Math.PI) / 180; });
});
document.getElementById('bgColor').addEventListener('input', e => {
app.renderer.backgroundColor = parseInt(e.target.value.slice(1), 16);
});
document.getElementById('opacity').addEventListener('input', e => {
sprites.forEach(s => { s.alpha = parseFloat(e.target.value); });
});
document.getElementById('addBtn').addEventListener('click', addSprite);
document.getElementById('clearBtn').addEventListener('click', () => {
sprites.forEach(s => app.stage.removeChild(s));
sprites = [];
document.getElementById('spriteCount').textContent = 0;
});
addSprite(); addSprite(); addSprite();
window.addEventListener('resize', () => {
const w = Math.min(window.innerWidth - 280, 800);
const h = Math.min(window.innerHeight - 40, 600);
app.renderer.resize(Math.max(w, 200), Math.max(h, 200));
});
</script>
</body>
</html>What’s Next
| Topic | Link |
|---|---|
| Drawing shapes and managing textures | https://tutorials.dodatech.com/frontend/libraries/pixijs/pixijs-graphics-textures/ |
| Animation and interactivity | https://tutorials.dodatech.com/frontend/libraries/pixijs/pixijs-animation-interactivity/ |
| WebGL rendering fundamentals | WebGL Guide |
| Canvas 2D API basics | Canvas API Tutorial |
Related Tutorials
- JavaScript Fundamentals — refresh your JS skills
- GSAP Animation Library — alternative animation approach
- https://tutorials.dodatech.com/frontend/libraries/pixijs/pixijs-graphics-textures/ — next: Graphics & Textures
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro. PixiJS powers real-time threat visualization in Durga Antivirus Pro’s security dashboard.
What’s Next
Congratulations on completing this Pixijs Getting Started 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