How to create light effects in Phaser 3

Light demo

This demo shows how you can enhance a 2d sprite with normal maps to create real time light effects.

Move your mouse over the demo to change the light position.

Prerequisites

You already have normal maps for your sprites.

To learn how to create normal maps have a look on this tutorial.
For more details about the SpriteIlluminator tool visit its documentation page.

Sprite sheets require a corresponding normal map sheet

The sprite sheet containing the normal maps must be packed with the same layout as the sheet containing the sprite images. TexturePacker has a dedicated mode to support this. This tutorial describes normal map packing in detail.

Throw light on your sprite

To load a sprite image together with its normal map, the file names of the image and its normal map are passed to this.load.image() as an array. In the following example the normal maps are marked with a _n suffix.

After loading the images, the sprite can be placed as usual. To enable dynamic lighting the corresponding shaders are activated using setPipeline('Light2D'). Important: Light effects require a browser with WebGL support.

function preload()
{
    this.load.image('character', ['assets/character/01.png', 'assets/character/01_n.png']);
}

function create()
{
    var character = this.add.sprite(250, 300, 'character');
    character.setPipeline('Light2D');
}

As we haven't added any light source yet, the scene remains black. Add these to lines to the create() function to switch on a light:

    var light  = this.lights.addLight(500, 250, 200);
    this.lights.enable().setAmbientColor(0x555555);

This adds a point light at position (500,250) with radius 200. The ambient light increases the brightness of all pixels of the scene, independent of the position of the point light.

How to use sprite sheets with normal maps

For sprite sheets the normal maps must also be packed on a sheet. TexturePacker automatically stores the file names of the textures in the generated JSON file, a simple call of load.multiatlas() loads both and links the normal map sheet to the sprite image sheet:

function preload()
{
    this.load.setPath('assets/');
    this.load.multiatlas('character_sheet', 'character.json');
}

Adding sprites and animations works as usual, it's just the setPipeline('Light2D') call which is additionally needed to enable lighting:

    // add sprite from sheet
    var character_anim = this.add.sprite(550, 300, 'character_sheet', '01');
    character_anim.setPipeline('Light2D');

    // play animation
    var frameNames = this.anims.generateFrameNames('character_sheet', { start: 1, end: 8, zeroPad: 2 });
    this.anims.create({ key: 'walk', frames: frameNames, frameRate: 10, repeat: -1 });
    character_anim.anims.play('walk');

Play around with it

To illuminate the sprites from different directions we attach the light position to the mouse pointer:

    this.input.on('pointermove', function (pointer) {
        light.x = pointer.x;
        light.y = pointer.y;
    });

Finally

That's all for this tutorial. You should now have learned how you use normal maps for dynamic lighting and how to use sprite sheets with normal maps.

Did you like the tutorial? Please share!

Source code available for download

The source code is available on GitHub. Clone it using git:

git clone https://github.com/CodeAndWeb/Phaser3-Lighting.git

or download one of the archives:

Phaser3-Lighting.zip Phaser3-Lighting.tar.gz