PandaJS, a modular game framework

Today I’ll be reviewing PandaJS, a really nice html5 game framework developed by Eemeli Kelokorpi. I’ll also be contrasting it to Phaser, currently the most popular HTML5 game framework. Ready? Let’s dive in!

###The panda mindset

Before using PandaJS, let’s understand how it is structured. It is built in a modular fashion, meaning that instead of creating huge blocks of code you will create smaller pieces that you then require in one place or another. This pieces of code are called modules and are created like this:

game.module( //We use this callback with a string argument to create the module
    'game.assets' //the file name is assets and it lives in the game folder
)
//Everything the module can do goes within the function
//that is passed to the .body callback
.body(function () {
    //As an example, here I'm loading some images
    game.addAsset('paddle.png');
    game.addAsset('game_logo.png');
});

This module is then required in main.js, like so:

game.module(
    'game.main' //This is the first file to be called by the engine
)
.require(
    'game.assets' //This enables us to use anything from assets.js in this module
)
.body(function () {
    game.createScene('Main', {
        backgroundColor: 0xffaa00
    });
});

As you can see, it is a bit different but all in all, it is a solid design pattern that is effective for object oriented game design. I believe that for teams and for newbies, this strictness avoids some nasty problems.

###Entities & Classes

PandaJS uses a class system based on John Resig’s class implementation, meaning you can create and extend classes as to re-use certain functionality. The idea with PandaJS is developing classes of entities, putting them inside of modules and then requiring the modules as needed.

For example, suppose that we are making a Pong game, using the assets that we have loaded on assets.js, we could do the following to create the Paddle class:

game.module(
    //I saved this file inside the entities folder
    'game.entities.paddle'
)
.body(function() {
game.createClass('Paddle', {
    //Init is called when an object is created
    //You can pass custom arguments to it
    init: function(x, y) {
        this.sprite = new game.Sprite('paddle.png');
        
        //Since PandaJS uses PixiJS, 
        //you get all of Pixi's methods and properties for sprites
        this.sprite.position.x = x;
        this.sprite.position.y = y;
        this.sprite.anchor.set(0.5, 0.5);
        
        //We then need to add the sprite to the stage, else it won't render
        game.scene.stage.addChild(this.sprite);
    }
});

});

Not bad huh? Back in our main.js, we could create a new paddle like this:

game.module(
    'game.main'
)
.require(
    'game.assets',
    'game.entities.paddle',
)
.body(function () {
    game.createScene('Main', {
        backgroundColor: 0xffaa00,
        init: function () {
            var paddle = new game.Paddle(200,200);
        }
    });
});

Pretty simple and reusable!

###Performance

While elegant, Resig’s approach to classes consumes quite a bit of memory, triggering JavaScripts garbage collector more often. We don’t want that. PandaJS alleviates this quite a bit with the built-in pooling manager which, instead of creating new entities, allows you to reuse them.

In a shooter game, for example, you might want to start with 100 bullet entities created. As they are fired, hit something and get removed from the game, they are simply moved offscreen and added to a list of “dead” entities. When you fire again, instead of adding a new entity, they are simple flipped into an “alive” state and repositioned. Cool right?

As far as the drawing part goes, PandaJS uses the excellent PixiJS renderer so expect top notch performance!

A small note for Linux Firefox users: PandaJS won’t work properly with it due to a bug with Firefox’s Cairo renderer. If you change the renderer to Skia, it should work fine.

###Modularity & plugins

By it’s very nature, PandaJS is a small engine. It can do a lot out of the box, sure, but many things are better left for plugins - essentially special modules that can be easily shared and added to any game. You can add Box2D physics or opt for P2 for example, simply by downloading the corresponding plugin and requiring it.

Modular & pluggable

###Configuration

You can set the game size and options from any of the PandaJS classes in a special file name config.js. This greatly simplifies things. An example of a config file:

pandaConfig = {
    name: 'MyPandaGame',
    version: '0.0.0',
    sitelock: 'invrse.co',

    // Attributes for game.System
    system: {
        width: 1024,
        height: 768,
        scaleToFit: true,
        webGL: true,
    },

    // Attributes for game.Audio
    audio: {
        musicVolume: 0.5
    },

    // Attributes for game.Storage
    storage: {
        id: 'net.pandajs.mygame'
    }
};

This decouples code that handles things like resolution scaling from your game logic, cleaning up your codebase and making modifications trivial.

###Documentation

The source code is quite easy to read which is great because it doesn’t have as much documentation as I’d personally like.

Granted, it is still a young engine and the author has gone great lengths in providing excellent basics. Between the cheatsheet, the documentation and this review, you are probably able to start doing basic games with it.

I fully expect to see more people doing tutorials on it, further decreasing the barrier of entry.

###Build tools

One really great thing about PandaJS is that it has awesome build tools by default. Sure, you can download the engine from GitHub or the PandaJS website but the best way, by far, is to use Pandatool from npm.

    npm install -g pandatool

Then you can start a new project from the command line like this:

    panda install

And you can build it for distribution like this:

    panda build

I personally find this wonderful. You can also do things like update your projects PandaJS version and checking your code for issues. Neat!

###The future of PandaJS

The engine will continue to improve but the one of the cool things is the effort, both by the community and Eemeli, in creating new plugins. Box2D, P2, AABB, basic AI… there is a lot of potential in here.

The crown jewel is, without question, the editor that Eemeli is developing, aptly named “Bamboo”.

Pretty freaking cool huh?

###Comparison with Phaser

I chose Phaser for comparison due to its popularity. They both use PixiJS for rendering and they both allow for easy pooling of entities, particles, tweening and sprite animation.

While PandaJS is built on the basis of modularity, Phaser is an engine built around the idea of convenience - you type in a few lines and you are done. You don’t have to remember to add sprites to the stage and you have three different physics engines available from the get go.

Being very complete, non-modular and covering many edge-cases means that Phaser’s codebase is monolithic, weighting in at 2mb unminified, so it definitely takes a lot of time to comb through and understand it. PandaJS is less than half that at 700kb.

Phaser is non-opinionated in how you work with it. It has no build system in place so you can re-use what you have. Gulp for some, Grunt for others and so on. The same is true for classes, entities, components and other design patterns - you can adjust it to what works best for you.

Phaser doesn’t have an official editor (yet, at least) and while community efforts like MightyEditor are pretty cool, they enforce a way of working that is different from average Phaser code, which makes me feel like I’m learning two entirely different frameworks. Bamboo is being built with cleaner integration.

###Closing thoughts

I honestly believe that PandaJS’s strength lies in its modularity, small codebase, enforced design pattern and built-in tools. It is really easy to understand and it is a very turn-key solution.

I believe that, with time, PandaJS will show itself as one of the best engines for HTML5 game development.

comments powered by Disqus