Skip to main content

Comparing Phaser and PixiJS for making 2D games

Phaser and PixiJS are popular open-source JavaScript WebGL libraries that are used to create 2D browser games. They render graphics inside an HTML canvas element. Games can be developed using JavaScript or TypeScript. These libraries use WebGL for creating high-performance, complex visual effects. You can create simple browser games using vanilla JavaScript, but if you want to have performant rendering or advanced features in your game such as physics or realistic lighting, vanilla JavaScript is not a good choice. These libraries speed up game development by providing useful functionality and they improve performance.

Let's compare Phaser and PixiJS for making 2D games. We'll use code snippets of a simple version of Flappy Bird, created using each library, as practical examples of the differences and similarities between the two libraries.

Here are the links to the Flappy Bird games:

To move the bird, press up, click or touch the screen. There is no restart; you'll need to refresh the page to play again.

Here are the links to the code for the Flappy Bird games:

Phaser basics

Phaser is a beginner-friendly 2D game framework that is used to create HTML5 games. It uses WebGL for rendering, and it has a Canvas fallback for when WebGL is not supported. It has all the functionality that you need to build a game. Its features include:

  • A pre-loader to load game assets such as images and sounds.
  • Three physics systems to choose from. They allow you to add gravity and collision detection to games.
  • Animation, including animation using sprite sheets.
  • A particle system for creating effects such as moving stars.
  • User input functions to control user input.
  • Sound: Web Audio with legacy HTML Audio support.
  • Scale Manager to control responsive game layout.
  • Multi-camera support.
  • Plugin system for extra functionality.

To see some examples of games created using Phaser, check this link.

The following code example shows the basic structure of a Phaser game:

const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
physics: {
default: "arcade",
arcade: {
gravity: { y: 500 },
debug: true,
},
},
scene: {
preload: preload,
create: create,
update: update,
},
};

const game = new Phaser.Game(config);

function preload() {}

function create() {}

function update() {}

To make a game, a Phaser.Game instance is created and a config object is passed in as an argument. There are many configuration options available; this example only shows a few of them. The type property is the rendering context, the recommended value is Phaser.AUTO. This uses WebGL for rendering and if the browser or device does not support it, it uses Canvas as a fallback.

Three functions are used to create a game scene and update it:

  • preload() - Load assets such as images and sounds.
  • create() - Set up a scene: create sprites and position them. Set up user input event listeners and collision detection.
  • update() - Game loop: update the scene on every frame. For example, moving sprites and checking their current position.

PixiJS basics

PixiJS is a fast, lightweight 2D graphics library that is used to create interactive graphics and games. It's not a game framework; it's a library for 2D rendering. It does not have many of the built-in game functions that Phaser has, such as collision detection. To create a game, you need to build a lot of the game functionality yourself, unless additional libraries are used. The features of PixiJS, which focus on rendering graphics, include:

To see some examples of 2D graphics created using PixiJS, check this link.

The following code example shows the basic structure of a PixiJS game:

const app = new PIXI.Application({
width: 800,
height: 600,
antialias: true,
transparent: false,
resolution: 1,
});

document.body.appendChild(app.view);

app.loader.add("assets/images/cat.png").load(setup);

function setup() {
// Using a game state helps modularize your code
state = play;

// Start the game loop
app.ticker.add((delta) => gameLoop(delta));
}

function gameLoop(delta) {
// Update the current game state
state(delta);
}

function play(delta) {
// The game logic goes here
}

function end() {
// All the code that should run at the end of the game
}

// Add your game helper functions:
// such as a collision detecting function

To make a game, a PIXI.Application instance is created and an options object is passed in as an argument. The options are the optional renderer parameters such as the width and height of the renderer's view. The renderer uses the canvas element, app.view, that PixiJS automatically creates for you. It is added to the HTML document using the appendChild method. The assets for the game are loaded using the loader instance. This is like the preload function used in Phaser. Once the assets are loaded, the setup function is called.

Four functions are used to create a game scene and update it, after the game assets are loaded:

  • setup() - Set up a scene: Create sprites and position them. Set up user input event listeners and collision detection. Set up the game state and start the game loop. The method ticker.add starts the gameLoop.
  • gameLoop() - Update the scene on every frame. The play() function is called to handle the game logic.
  • play() - Handle game updates, for example moving sprites and checking their current position.
  • end() - All the code that should run at the end of the game, such as changing the game state.

Documentation and ease of use

Phaser and PixiJS have good documentation for their APIs. They also provide examples and tutorials for making games. Phaser has more game-specific examples, whereas the PixiJS examples focus on graphics rendering. It is easier to make a game using Phaser as it is a game framework. It provides useful functionality for games and the code structure is more organized for games by default. PixiJS is less opinionated about how you structure a game.

When you publish a game, it's best to bundle your files to make them as small as possible. Both Phaser and PixiJS provide starter templates to bundle your game using Webpack.

Adding game physics

Phaser comes with built-in game physics, whereas PixiJS does not. Phaser has three types of built-in physics: Arcade Physics, Impact Physics, and Matter.js. For example, if you wanted to add Arcade Physics, which has dynamic and static bodies, you add it to the config as can be seen in the code example in the Phaser basics section above. A dynamic body is a body that can move around via forces such as velocity and acceleration. These bodies can bounce and collide. There is also a debug property that, when set to true, displays the outline of physics bodies and shows the effect of forces as can be seen in the Phaser Flappy Bird game. There is a performance cost in drawing debug displays. It should not be used in production. The gravity property allows us to set up the gravitational force.

The collider method is used to detect a collision between the bird and the pipes in the Flappy Bird game:

collider = this.physics.add.collider(bird, pipes, collisionCallback);

The collisionCallback function is run when a collision is detected. It can be used to run all of the code that should run when the game is over:

function collisionCallback() {
gameOver = true;
collider.active = false; // turn off so that the bird falls to the ground on collision
bird.setFlipY(true);
game.sound.play("crashSound");
}

Setting up physics in PixiJS requires building the gravity and collision detection yourself. Making the bird fall in Flappy Bird by simulating gravity with PixiJS only requires two lines of code added to the play() loop:

bird.vy += 0.25;
bird.y += bird.vy;

The bird's velocity is increased on each game loop and then its y position is updated. Collision detection requires writing your own collision detector, for example the hitTestRectangle function in the PixiJS Flappy Bird game.

Another option is to add a third-party physics library such as Matter.js, although setting up and integrating the physics library can be tricky. If you need physics in your game, especially more complex physics effects, Phaser is a better choice.

Adding animation

PixiJS and Phaser have basic animation functionality for sprite sheet animation, tweens for smooth animation and they have particle systems to create effects such as an explosion. The particle system needs to be installed as a separate library for PixiJS.

For more advanced graphics effects, PixiJS is a better choice as it includes many examples of how to create advanced graphics features such as WebGL filters and shaders. The PixiJS website shows some real-world examples of websites that were created using PixiJS. It has also been used by many well-known global brands.

Adding user input

Phaser provides many methods to handle user input, including more advanced use cases such as drag and drop. PixiJS requires more manual setup of user input. For example, in the Phaser Flappy Bird game, making the bird fly up when the user presses the up arrow, clicks the screen or touches the screen requires three lines of code.

In the create function, the createCursorKeys method creates and returns an object containing four hotkeys for up, down, left, and right as well as space bar and shift:

cursors = this.input.keyboard.createCursorKeys();

In the update function, we listen for an arrow up press, a mouse click or a screen touch and then move the bird up.

if (cursors.up.isDown || this.input.activePointer.isDown) {
bird.setVelocityY(-200);

To make the bird move up in the PixiJS Flappy Bird game requires a lot more code. A keyboard helper function is created, which is used in the setup function to listen for the specific input events and set the bird's velocity when the event occurs.

Implementing a responsive layout

Browser games can be played on desktop or mobile devices; they may also be compiled to native mobile apps by using third-party tools such as Cordova. It's important that your 2D game has a responsive layout. Phaser has a Scale Manager that handles scaling, resizing, and alignment; it also has a full-screen mode. When using PixiJS, you need to create a responsive layout manually. This can be tricky with more complex layouts.

Let's compare the responsive layout implementation of the Phaser and PixiJS Flappy Bird games. For both games, the game canvas width is set at 800px, the height is set at 600px and it is centered in the window. The canvas is scaled to fill the browser window as much as possible while maintaining its aspect ratio.

In the Phaser implementation, this requires setting some scale properties in the config:

  scale: {
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH,
width: CONFIG_WIDTH,
height: CONFIG_HEIGHT,
},

In the PixiJS implementation, this requires more code. In the setup function, a "resize" event listener was created with a callback function called resize:

window.addEventListener("resize", resize);

// call it manually once - make sure the stage is correctly sized on page load
resize();

The resize function is a helper function that determines the current screen size and what scale factor is needed to make the game canvas fill the screen while maintaining its aspect ratio. It then uses the scale factor to change the CSS styling of the game canvas to scale the canvas size up or down:

function resize() {
// current screen size
const screenWidth = Math.max(
document.documentElement.clientWidth,
window.innerWidth || 0
);
const screenHeight = Math.max(
document.documentElement.clientHeight,
window.innerHeight || 0
);

// scale factor for our game canvas
const scale = Math.min(
screenWidth / CONFIG_WIDTH,
screenHeight / CONFIG_HEIGHT
);

// scaled width and height
const enlargedWidth = Math.floor(scale * CONFIG_WIDTH);
const enlargedHeight = Math.floor(scale * CONFIG_HEIGHT);

// margins for centering
const horizontalMargin = (screenWidth - enlargedWidth) / 2;
const verticalMargin = (screenHeight - enlargedHeight) / 2;

// CSS to set the sizes and margins
app.view.style.width = `${enlargedWidth}px`;
app.view.style.height = `${enlargedHeight}px`;
app.view.style.marginLeft = app.view.style.marginRight = `${horizontalMargin}px`;
app.view.style.marginTop = app.view.style.marginBottom = `${verticalMargin}px`;
}

Adding sounds

Both Phaser and PixiJS provide support for sound functionality. They provide a beginner-friendly interface for the Web Audio API. Phaser also supports legacy HTML Audio. For PixiJS, the PixiJS Sound library needs to be added. This library is a Web Audio API playback library that is built for modern browsers. It has a small bundle size (8.7 kB minified and GZipped) with no dependencies and is tree-shakable so you can remove unused modules in your production build.

Let's compare how sound was added to the Phaser and PixiJS Flappy Bird games. You'll see that the implementation is similar.

In the Phaser Flappy Bird game, the "woosh" sound that the bird makes when it moves up is loaded into the game in the preload function:

this.load.audio("birdSound", "./assets/sounds/woosh.mp3");

The sound.play method is called to play the bird sound:

game.sound.play("birdSound");

In the PixiJS Flappy Bird game, the PixiJS Sound library is imported using a script tag in the index.html file:

<script src="https://unpkg.com/@pixi/sound/dist/pixi-sound.js"></script>

The sound is loaded into the game using the add method in the script.js file:

.add("bird-sound", "assets/sounds/woosh.mp3")

The sound.play method is called to play the bird sound:

PIXI.sound.play("bird-sound");

Bundle size comparison

The Flappy Bird game examples import the libraries using a <script> tag for ease of use. When building a production-ready game, you can use the Webpack starter templates that are available for each of the libraries to bundle the game files so that the file size is as small as possible. To compare the bundle sizes, the two Flappy Bird games were bundled using the following Webpack starter templates:

The following table compares the bundle sizes. The bundle size of PixiJS is less than half the size of the bundle size of Phaser.

PhaserPixiJS
npm package minified bundle size1 MB478.6 kB
npm package minified and GZipped bundle size274.1 kB125.4 kB
Flappy Bird game minified bundle size1.02 MB424 kB

PixiJS is also tree-shakable, so you can remove unused modules in your production build. This will further reduce the bundle size. The PixiJS Flappy Bird game includes the PixiJS Sound library. The Flappy Bird game bundle size can be further reduced in the browser using GZip compression.

If bundle size is critical, for example if you are building a mobile game for users with a poor internet connection, then PixiJS is a good choice, as long as you are willing to build a lot of the game functionality yourself.

Conclusion - which one is the best?

The following table shows a comparison between Phaser and PixiJS for 2D browser game development. It shows which library is the best for each category.

PhaserPixiJS
Beginner-friendly✔️
Documentation✔️✔️
Game physics✔️
Animations / visual effects✔️
Handling user input✔️
Responsive layout✔️
Sounds✔️✔️ *
Bundle size✔️
Accessibility✔️

* needs an extra library

Phaser is a framework for making games and is more beginner-friendly than PixiJS. If you compare the code for the Phaser and PixiJS Flappy Bird games, you'll see that the number of lines in the PixiJS implementation is more than double the number of lines in the Phaser implementation. This is because some functionality such as collision detection and containing the bird in the visible area of the game canvas required creating custom functions.

PixiJS can also be good for beginners looking to understand game development more deeply as they will have to implement a lot of functionality themselves.

For more complex games, Phaser is a better choice because of the built-in game functionality. Although you can add extra libraries to PixiJS to compensate for its lack of built-in functionality, the problem is that the bundle size and codebase complexity will increase, which may make the bundle size advantage that PixiJS has insignificant.

If your game needs to be screen-reader accessible or if you want to create complex interactive graphics for games or other types of digital content, PixiJS is the best option.

For 2D browser games, Phaser is the best choice as it has many useful game functions built in. If the bundle size of your game is critical and you can build a lot of the game functionality yourself, then PixiJS is a better choice.

Another option for a beginner-friendly game framework is Kaboom.js. Here's a tutorial where you can learn how to build Flappy Bird with Kaboom.js.