Navigation

Phaser 3 & 4 Dev Log - April 2021

Published on 11th May 2021

Greetings human people! An awful lot has been going on in the Phaser world recently, so it's time for me to sum things up in another Dev Report. I'll start off talking about the work we did on Web Monetization and then dive straight into the progress I've been making with Phaser 4.

The Game Web Monetization Plugin

Back in mid-2020, we were lucky enough to be awarded a $49,000 Grant for the Web. It is their mission to fund "open, fair and inclusive standards and innovation in Web Monetization". With this in mind, clearly, our focus was always going to be how to use Web Monetization for games. So that's what we set out to do. I spent a good while experimenting with Web Monetization and getting to grips with how it worked and how it could be utilized within Phaser.

Then earlier this year, Francisco continued this work, using the Web Monetization API to create a plugin that all web game developers could use, regardless of their choice of framework. The end result is the Game Web Monetization Plugin: "An easy-to-use Web Monetization Plugin for HTML5 game frameworks, including Phaser and Pixi."

This plugin is available now on GitHub and is cross-framework, meaning it will happily work with any web-based framework of your choice, including Phaser 2, Phaser 3, and Pixi. It's fully documented and available in both ESM and ES5/UMD bundles, with full TypeScript Definitions as well. We also created a game called 3 Candies to showcase how the API works. The full source of the game is available in the plugin repository.

As well as the plugin and game we also jointly authored a 7-part tutorial series. This tutorial has been published on the Phaser website, but the content is open-source and also included in the repository. The tutorial covers everything from what Web Monetization is, how to create a Coil and Uphold account, a deep dive into the plugin API, using probabilistic income distribution, implementing the API in a game and some further thoughts on how you can use it. The tutorial is available to read now and is also fully translated into Spanish and Chinese.

I truly hope that the Web Monetization API becomes a ratified W3C standard, implemented natively in browsers. Until then, the Coil browser plugin gives us a glimpse of what the future may hold, and hopefully, the hard work we've put into these resources will allow you to realize this in your own games today.

Compressed Texture Support

I'm very pleased to announce that Club Penguin Rewritten, one of the largest Club Penguin recreation sites, has sponsored a new Phaser feature. In order to aid them in improving mobile performance, they have funded the development of compressed texture support. I'm going to paraphrase Brandon Jones's article on compressed textures to help explain what they are and why you should care about them.

Compressed textures have existed in 3D games on desktop and mobile for many years and also made it into the WebGL1 spec. Traditionally, when dealing with textures in web-based games we tend to either opt for PNG or, if alpha and pixel fidelity isn't a concern, JPG. JPGs can often be nice and small file sizes, right? So surely that's a compressed texture?

While it's true that JPG compression significantly aids with download times, it's the process of what happens when that JPG is converted into a WebGL Texture that is of concern. Your GPU doesn't understand what a JPG is. The way it has been encoded isn't conducive to fetching texels from it. So, internally, WebGL will take the JPG and fully decompress it and then send this decompressed version to your graphics card. The decompressed version is a giant array of RGB/A values, with each color channel taking up 1 byte. Every texel of an RGB texture is 24-bits (or 32-bits for an RGBA texture), so using this we can calculate how much memory a texture will consume based on its resolution. Assuming the JPG was 1024 x 1024 in size it will use:

1024 (height) * 1024 (width) * 3 (bytes per pixel) = 3,145,728 bytes (3MB!)

So, all of a sudden that tiny little JPG has just eaten 3MB of GPU memory. It doesn't take too long, especially on memory-constrained mobile devices, before you get dangerously close to filling up the video memory. Plus, the more pixels there are in the texture, the longer it takes to upload the data to the GPU in the first place.

This is where texture compression comes in.

Your GPU has algorithms built into it that allow them to decompress specific texture formats automatically. This results in the texture remaining compressed _in video memory_ and it's only decompressed when a shader does a texture look-up (such as texturing a quad).

Different formats offer different rates of compression. For example, DXT5 is a fixed 4:1 ratio, meaning the 3MB 1024 x 1024 texture will now occupy only 768k of memory instead. However, there are trade-offs, as several of the formats are lossy.

These formats have been designed from the outset to be as fast and memory-efficient as possible, sacrificing file size in the process. Plus, as you may expect, not all GPUs support all formats. This is why, much like audio, when it comes to supporting the widest possible range of devices we need to provide our textures in multiple formats in order to cover all bases. Thankfully, software such as Texture Packer can automate this process for us.

We will be bringing compressed texture support to both Phaser 4 and Phaser 3. The core development will be done in Phaser 4 and then merged with Phaser 3. We'll develop new file format loaders and texture parsers for the extra formats and recode the WebGL texture generation functions, too. This shouldn't take too long to implement as it's well documented and we've already partly done it before in Phaser 2, too. I intend for this feature to drop in either the 3.55 or 3.56 release. Keep an eye out for it if you're suffering from GPU memory issues.

Phaser 4 Development Update

I'm very pleased to be working again on Phaser 4 and this part of the Dev Log will be all about how you can start playing with it too if you feel so inclined :)

Revisiting Phaser 4 again after such a long time working on 3.50 was like a breath of fresh air. I had forgotten just how much progress I had made with it. While there is certainly lots left to do, it was a sheer joy to dive back in. The cleanliness of the API still gets me giddy with joy.

However, I know this is likely to be one of the first mentions of Phaser 4 for a lot of you, while equally lots of you are just wanting to know what I've been working on. So, for those who need to get up to speed on what Phaser 4 is, please read the following section and check the links provided. If you already know the history behind it, feel free to skip to the part titled "Phaser 4 in 2021".

The History of Phaser 4

In July 2019 I made the first announcement post about Phaser 4. This was a short press release explaining how Phaser 4 would be the next major version and that we'd be moving to TypeScript and a more modular build process.

Due to lots of questions from the community, I then wrote a really long piece called the Phaser 4 Development Process, in which I talk in length about how I want to build Phaser 4. I talk about modern and changing web trends, my experience in the past with other game-making software, and about how I felt the move from Phaser 2 to 3 had gone. For me, this is the one single post that sums up the best what Phaser 4 is all about.

From that point on, development started. You can track the first 8 months of R&D through the 8 published Dev Logs below. At the end of June 2020, I decided it was time to re-invest a lot of the things I had learned and built for Phaser 4 back into v3, and the development of the 3.50 release began. It took me the rest of 2020 to complete this release and I'm very happy with how it turned out. As a result, Phaser 4 was put on hold while it was finished.

The following Dev Logs give you lots of details about how things have gone so far. The older they get, the more out of date the material gets, but the core concepts mostly remain the same:

Phaser 4 Dev Log 8 - June 2020
The new RenderPass and World 3D objects.

Phaser 4 Dev Log 7 - May 2020
Multiple Entry Points, Renderer Updates, Render Layers, Effect Layers, and more.

Phaser 4 Dev Log 6 - April 2020
Structural changes, mono packages, Farewell God classes, and Scenes.

Phaser 4 Dev Log 5 - March 2020
The new Render Cache, Input Mapping, tiny bundle sizes.

Phaser 4 Dev Log 4 - February 2020
Canvas support and the evolution of the WebGL Renderer.

Phaser 4 Dev Log 3 - December 2019
This dev log is mostly about Phaser 3 Matter Physics updates, but does have v4 updates, too :)

Phaser 4 Dev Log 2 - November 2019
Experimenting with Pico.gl, drawcalls and texture fills.

Phaser 4 Dev Log 1 - September 2019
Discussing how coding with v4 will be very different compared to before.

Phaser 4 Development Process - August 2019
A long and exhaustive look at how Phaser 4 will be developed.

Phaser 4 Announcement Post - July 2019
The original announcement press release.

Phaser 4 in 2021

So now you're up to speed on Phaser 4 it's time to take a look at the most recent changes, as well as answer some common questions I get on Discord.

In order to restart development after 9 months away, the first thing I had to do was update all of the dependencies. Thankfully, this didn't break quite as much as I was expecting, despite some massive version jumps.

The biggest change was going from TypeScript 3.9.7 to 4.2.4. This upgrade alone meant I had to refactor a few classes to handle some language deprecations. It wasn't as extensive as I had feared, though, and after a few hours of googling and reading, the actual changes needed were very minor. All of my build tools were upgraded, including ESBuild, Rollup, ESLint, and more, which required me to spend time updating my build scripts as well.

This is, honestly, quite a tiresome part of web dev. I mean, I guess 9 months is quite a long time in some respects, especially for something like a fetus :) but clearly, in the web dev world, it's a lifetime, too. I now make it part of my daily workflow to check the project dependencies and update them accordingly, no matter what.

To do this, I use a superb package called npm-check-updates. I have this installed globally on all my machines, so I can just issue the command `ncu` and let it do its stuff:

Lovely :)

I haven't written about this in a Dev Log before, but I've been using ESBuild to build Phaser 4 for quite a while now. This is after suffering months of pain struggling with Webpack and then Rollup. Let me make myself perfectly clear: ESBuild saved Phaser 4. It was an absolute God-send at a time when I honestly wasn't sure how the development would proceed.

This is because of the way I'm building Phaser 4. After a lot of time wasted with a monorepo approach (see the April 2020 Dev Log for more) it became clear that what I wanted was for Phaser 4 to use Multiple Entry Points, also known as Direct Imports. It's a pattern gaining real ground that is especially important for libraries that provide many independent utility functions, as it allows you to import independent parts of the library from separate files.

A year ago, I spent a few days creating my own Rollup plugin to handle this for me, as it didn't support Multiple Entry Points at the time. Even with that plugin, build times still were not that great. And then I heard about ESBuild - and wow, it was like night vs. day! Running a full build of Phaser 4, which has 1160 entry points, went from 2 mins to 2 seconds.

It was so remarkable I honestly thought ESBuild had crashed the first time I experienced it. Now, it's my go-to weapon of choice. Currently, when I run a full build of Phaser 4 it takes 1.8 seconds to build all of the modules and ES6 bundle, 10 seconds to generate all of the TypeScript defs and then a 2-minute wait for Rollup to limp along creating the UMD bundle. Thankfully, I only need to do this when publishing a new version to npm. Actual day-to-day development is, to all intents and purposes, instantaneous.

With the Phaser 4 build tools spruced up it was time to sort out all of the repositories. I spent some time removing lots of them and making others private, so the only two visible repositories are now Phaser 4 and one called Dev. You can find these at https://github.com/phaserjs.

I did this to avoid any confusion. I have hidden all of the old template and example repositories, and even the Examples repo, just to keep things tidy. I will introduce them back in as and when I update them to work properly with the current build. The Dev repo is where I'm conducting all of my tests from. Think of this as being like a greenhouse, where fresh little tests are grown and nurtured before either being composted or transplanted into the Examples repo to enjoy a long healthy life of developers peering inquisitively at them.

As such, I've spent several days tidying this repo up and making it work the way I need for my day-to-day development. It also serves as a fun little sandbox to demonstrate things in, too, as it's fully hooked-up with GitHub Pages, meaning you can launch any of my tests from its shiny new interface:

Neat, huh? :)

You can access the sandbox here https://phaserjs.github.io/dev/examples/live/ and I know I've just lost nearly all of you, so I'll wait patiently for you to come back :)

Every time I publish a new example, it'll be added there automatically, which is pretty handy. And I'll make good use of this in the coming months to showcase things before the main Examples repo is rebuilt.

The icons across the top of the windows perform the following tasks, from left to right:

1. Play / Pause Toggle. This will make a call to the Phaser instance running in the window and pause, or resume it.
2. Open the example code on GitHub, so you can pull down a copy.
3. View the example source code within the sandbox. Because sometimes that's handy, too.
4. Get a link to the example. This allows you to re-open the sandbox with a specific example already running.
5. Minimise the window down to the bottom of the browser.
6. Maximise the window to fill the browser (also, double-click the title bar to do the same)
7. Fullscreen. Takes the running example into full-screen mode.
8. Close the example, destroying the iframe and that Phaser instance.

You can open multiple windows at once, drag them around, resize them and just have a play. It can get pretty fun :) and the more examples I add in the coming weeks, the more fun it'll be.

If you wish to checkout the Dev Repo to run it locally, you can of course do that, too. Just clone it from https://github.com/phaserjs/dev/. No web server is included, so you'll have to provide your own.

You can also fork the repo, allowing you to build your own examples safely. If you do this, there are several commands you can use. The first allows you to build a single example and get an idea of how large it will be in production. The command for this is:

npm run dev --src "examples/src/3d/rotating cube.ts"

Remember to use quotes around the example name if it has spaces in it.

Voila! A new example has been built and can now be run from the sandbox.

If you're working on an example a lot, then it's more useful to use the watch mode:

npm run watch

If you enter `npm run watch` on its own it'll ask you to "Choose a file". Press TAB and it will present a list of folders to choose from. Use the cursor keys to navigate and enter to select a folder, until you navigate to the example you wish to 'watch'. Alternatively, you can do:

node watch.mjs "examples/src/gameobjects/text/fill style.ts"

to directly watch a specific file without using the folder navigation. Both routes will enter you into watch mode, where I've created a custom watcher based on ESBuild and Terminal Kit:

As the build times are in the order of a couple of hundred ms, it's quite safe to work in your editor, modify the example, save it, and by the time you've even switched to the browser to refresh it, it'll be rebuilt. I may investigate auto-reloading if I feel it requires it at a later stage, but for now, this works perfectly for me.

There are several ways to create examples in the Dev repo. The first is by using the @phaserjs/phaser package via node modules. This is also how you'd do it if you want to test Phaser 4 out for yourself in your own repo. Assuming you have run npm i @phaserjs/phaser, or are running from a fork of the Dev repo, you can code using the following format:

Here you can see that classes and functions are imported from the @phaserjs/phaser package. This approach is great for most people reading this. I am publishing new versions on an almost daily basis, so it's generally safe to assume that whatever I've been working on recently will be in the most current version in npm.

However, you can also import the source directly from the Phaser repo itself. See this part of the README for more details. This is for those who want to try a part of the API that I didn't surface fully yet, or if you feel like contributing towards Phaser 4 development and want to test a feature out before submitting a pull request :) (I can but hope!)

Common Phaser 4 Questions

At this stage, you now know how to build examples and how to use the sandbox to run them, including those I've been working on. I'll now cover some common Phaser 4 questions.

Q. Do I need to use TypeScript to use Phaser 4?

A. No, but we'd strongly recommend it. The type-safety, code completion, and code insight it provides you just make development so much easier. Phaser 4 is written in TypeScript, which means everything is strongly typed, finally preventing you from accidentally passing the wrong objects to functions, or forgetting a parameter name or argument order. It gives you unparalleled insight into how Phaser works. That alone is worth the price of mental admission.

Q. Is it production-ready?

Q. Where is the source code?

A. All of the source code can be found in the new PhaserJS repo and on npm under the @phaserjs/phaser package.

Q. Is Feature X added yet?

A. Maybe, but most likely not. I'm currently working towards getting the render pass finished and water-tight, ready for Compressed Textures, then I'll take stock and decide what to focus on next.

My plan is to launch with a reduced subset of features, compared to Phaser 3, and ensure they work well, before jumping into anything else. I've said it before, and I'll repeat it here: I'm more than happy to release Phaser 4 with just a handful of Game Objects, a solid renderer, and World life cycle, and little else. No physics, sound, or even tweens. Just the essential building blocks to get stuff on-screen and to get it there fast and efficiently. I consider that the minimum viable product. Everything else can be added once this is done.

Q. Can I help?

Actually, yes. For a starter, if you're not a Patron, becoming one is of massive assistance to us. Secondly, if you feel like contributing modules towards the core API, I'm happy to look at those. Follow our ESLint and Editor Configs before issuing PRs, and if the contribution is significant, check with me on Discord first, but I'm definitely open to receiving them.

The same goes for examples created in the Dev repo. If you look in the wip folder you'll see a lot of examples not yet updated to the latest version. It'd be good to start helping update those.

Right now we're investigating creating unit tests. Once we've settled on our approach, help creating those would be wonderful, too. Finally, if you've ideas re: API structure, then I'm open to listening to them, either as a GitHub issue or via Discord.

Q. What's next?

I'm going to carry on working on Phaser 4 for now. I will need to switch back to release 3.55 at some point, plus of course, the Compressed Texture work will need integrating into Phaser 3, too. However, my focus is fully on v4 development, including educating you about what I'm doing. To that end, I intend to publish a new Dev Log in a couple of weeks. Because when development is as rapid as it is right now, I need to make sure I write about it often and keep everyone informed.

Until then, please play with the sandbox. Please pick through the source code, maybe even try to create an example or two and let me know how you get on.

I'll be waiting for your feedback on Discord :)