Phaser World Issue 207
In this issue, we introduce a free file hosting service, showcase two great games, dive into Phaser Editor tutorials, and have a massive dev log from the team. Let’s go …
🔥 Black Friday - 30% Off!
Get 30% off our monthly or annual subscriptions for a limited time.
Use the code BF30 during checkout to get 30% off.
Click here to see the Pricing page.
This code expires on December 1st 2024.
The discount applies to first-time customers and is for the first payment only. For the largest discount, use it on an annual payment.
💾 Game File Hosting
Free, easy drag-and-drop game file hosting for all Phaser users!
We're super excited to announce that our Game File Hosting Service is now available for everyone. Register an account on the Phaser site, and you'll have immediate access to our new file hosting service.
Because of the way browser security works, it's often quite complex to simply put a file somewhere and get a URL that you can use in your games. Direct linking to files, such as those stored in Imgur or GitHub, is nearly always blocked via restrictive CORs policies. And if you want to test something quickly, using our Phaser Sandbox is perfect for that — but again, how do you access your files from within it?
Previously, you'd need to run your own server and manage your security settings manually to achieve this. Now, you can use our free drag-and-drop file hosting service!
Read More about Game File Hosting
🧩 Reddit Games and Puzzles Hackathon
Create a new word or puzzle game and win a share of $116,000 prize money.
The Reddit Games and Puzzles Challenge kicked off today and gives Phaser developers a great chance at winning a slice of the $116,000 prize money on offer. The challenge? To create a word or puzzle game using Reddit's Developer Platform.
They are looking for apps that "leverage interactive posts", making this an exciting opportunity for developers to showcase their creativity and skills.
Read more about the Reddit Hackathon
🎮 The Crazy Hyper-Dungeon Chronicles
A fresh take on roguelite crawlers where procedural dungeons meet witty chaos.
In an era saturated with dungeon crawlers, The Crazy Hyper-Dungeon Chronicles stands out by refusing to take itself too seriously. This upcoming pixel-art RPG, slated for 2025, brings a refreshing blend of classic dungeon-crawling mechanics and modern roguelite elements, all wrapped in a layer of eccentric humor.
With 50 planned levels, multiple heroes, and three combat styles, The Crazy Hyper-Dungeon Chronicles promises substantial content. But its true appeal lies in how it balances accessibility with depth – easy to pick up, yet offering enough complexity to keep veterans engaged.
Whether you're following in the footsteps of the legendary Lando Lore or simply trying to keep King Dandelion entertained enough to avoid war, this quirky dungeon crawler looks set to carve out its own unique niche in the genre.
Everyone at Phaser Studio wishes the developers all the best for their full release. In the meantime, download the entertaining demo version from Steam and be sure to add it to your wishlist!
Read more about the Steam demo
🧠 Phaser Editor Script Nodes Tutorial
Learn how to manage custom code and modular development in Phaser Editor.
Scott Westover has published a new YouTube tutorial that goes in dept into one of the most powerful features of Phaser Editor v4 - Script Nodes. This tool opens up a whole new way to manage custom code, making game development faster and more flexible.
🎮 Murder
Creep up behind the king and take him out quietly in this 100M+ played game.
One of the things about Phaser being open-source is that anyone can come along, freely take the library and release a game without ever telling us. We absolutely love it when they do tell us! Yet there is, of course, no obligation to do so. Which means that we'll often come across a game that may have been released years ago, that we have never played before. And that is the case here, with Murder!
Phaser Studio Developer Logs
Here is what the Phaser Studio team has been up to this week:
👨💻 Rich - CTO & Founder
As usual, my week was a mixed bag of delights! On the Box2D front, I configured JSDoc and have it generating all of the documentation we’ve created for the v3 functions. We had to write it all ourselves, and there are hundreds of functions! But now this is done, it’s easy to scan through in HTML form.
However, we also realized that you need tutorials as well as function documentation. So we’ve attacked this on two fronts. The first is to create our own, and the second is that we agreed with Chris Campbell of iForce2D to license and repurpose his excellent Box2D tutorials. His are all for v2, and all code is in C, so we have a bunch of work to update them - but once complete, this will be a solid bundle: A modern, compact library, a whole bunch of examples to learn from (including both Phaser and ‘pure’ examples), a large set of tutorials and finally generated documentation. It’s been a lot of work to get the product to this point, involving weeks of hard graft from the team - and ultimately, this is why it can’t be a free release. I’m well aware we’re going to get criticized for this. I’m also well aware of how much it cost us to produce.
In other news, Ilija has completed the Phaser Beam logo! There are light and dark versions, and I absolutely love it. It’s everything I wanted it to be and more. Here is an exclusive peek at it:
Totally awesome 🤩
👨💻 Pete - Box2D
The week has been busy on several different fronts...
Code Optimization
We've achieved package size reductions by identifying and removing unused SIMD-related functions. Additionally, we've improved performance by optimizing even more heavily-used functions that were creating temporary b2Vec2
objects – inline mathematics operations now reduce garbage collection overhead and provide minor performance gains.
While maintaining close alignment with the official Box2D implementation, we made a decision to improve user experience: mouse joints now automatically wake sleeping objects upon attachment. We're tracking recent official patches regarding sleep/wake states and will evaluate their integration soon.
Code Quality
An ESLint pass has helped us clean up both the library and examples. During this process, I addressed nearly all of my outstanding TODO items, leaving only one complex design question for further discussion.
We've resolved display issues on high DPI screens, benefiting Mac users who were experiencing scaling problems with the debug drawing in the examples. The core library doesn’t include any drawing functions at all, but it’s always helpful to see what is going on in the examples, so there is both a canvas debugger and a Phaser one.
New Examples and Features
This week's new demonstrations include:
A dynamic jar simulation featuring capsule-shaped pills stirred by a rotating paddle
A side-scrolling car driving demo with an extensive chain-based terrain
Physics-driven explosion effects triggered by mouse input
We've also refined existing examples and extracted the Earcut Constrained Delauney Triangulation code into a standalone helper file, temporarily housed in the Examples folder pending a permanent location decision.
Documentation and Tutorials
Additional help is available following the creation of first-draft tutorials covering 13 example projects. Pending initial feedback, we might extend this effort to most remaining examples.
Performance Testing
Our new stress test example dynamically maintains 58fps (±1) by adding or removing physics objects as needed. This benchmark helps us understand performance across different hardware configurations:
Powerful but aging Windows desktop: ~2,300 objects
Mid-range systems: ~1,900 objects
High-end hardware (iMac M3): 6,000+ objects
This test dumps thousands of objects into one giant heap, so isn’t representative of an actual game, as it’s dealing with literally thousands of contact resolutions per frame. If you were to island the objects, so groups of them could achieve going to sleep, you can expect dramatically larger numbers. Even so, ‘stress’ is the important part of this test here: Huge piles of objects that never start overlapping or ‘squishing’.
Ongoing Development
We're actively following Erin's improvements to the official Box2D v3, carefully evaluating and incorporating the most critical updates into our JavaScript implementation. We've secured an agreement with Chris from iForce2D to adapt his excellent Box2D v2 educational content for Phaser Box2D. This adaptation work has just begun and promises to provide comprehensive, practical guidance for our implementation.
👨💻 Francisco - Phaser Launcher
Hi everyone,
This week, I’ve been exploring how to add drag-and-drop functionality to our small editor.
Initially, I ran into several challenges. My first approach was to use Tauri’s events to handle file drops. However, this method didn’t emit any events to the client, which caused issues when trying to display what was happening on the front end of the application.
The solution turned out to be simpler than expected. In the end, I disabled Tauri’s drag-and-drop events and leveraged HTML5’s DataTransfer API instead. This allowed me to copy files to the user’s selected path seamlessly.
Step by step, our application is growing and gaining potential. I’m excited to see where this journey takes us!
👨💻 Arian - Phaser Editor
Hello friends.
This week we finally completed the UI for the editor settings. Basically, we finished the tab for redefining command key bindings. This is something that many of you have been asking for for a while, and it's finally done. Here's a picture:
During the week, a few of us on the team were tasked with implementing small examples for the new Box2D JavaScript library they're cooking up in Phaser. This was quite stressful for me, as I had very little knowledge of how these kinds of physics libraries work. The first day I spent most of my time reading the Box2D documentation and even learning some physics concepts. Physics was never my strong suit in school. But finally on the second day I was able to complete two examples. Here are some pictures.
Spiderman movement example:
Retro volleyball gameplay:
Next week we're adding support for web fonts to Phaser Editor, and we'll start working on publishing a release. See you!
👨💻 Zeke - Box2D
Another week, another Box2D game demo completed and another in progress. Inspiration can come from anywhere, and this week’s demo is inspired by BenJames171’s Escape Pod game. Although it’s no longer available to play, here’s a screenshot:
The aim of the game is to reach your homeworld in an escape pod fired from a base on the other side of various planetary systems. The escape pod works as a projectile so you only control angle and power when firing off; you need to find the right trajectory that makes use of the other planets gravity along the way.
Demo 1
And here are the steps taken to create a basic implementation of planetary gravity in Phaser’s Box2D plugin.
Create a Box2D circular sensor zone and have objects touching the area drawn towards the centre. The planet itself is a solid circle body so the objects are able to land on it.
Add an aiming mechanism that controls the angle and power
Add collision detection for the planets
Add ending animation for the final planet
Just place the start and end positions, adjust the size, positions and gravity strength of each planet and you can easily create multiple levels.
Demo 2
This next demo is inspired by Andreas Illiger’s Tiny Wings.
The goal of the game is to control a bird while using hills in order to gain speed and fly as far as possible.
Although the demo is incomplete at this point, the basic mechanics are already in place.
The first step is to draw procedurally generated hills using a cosine function and enable physics bodies on the curve.
Next to add input controls to enable the ability to dive.
Camera pan and zoom effects are added to follow the player.
The last step in this demo that will be worked on next week is to add continuous terrain generation to make this demo an endless journey for the player. Of course, more features could be added but this is a good enough starting point if you’re interested in creating games with a similar mechanic.
Although we’ve already got a tonne of ideas for 2D physics demos on our plate, feel free to get in touch with the dev team on Discord if you have ideas or suggestions you’d like to see implemented.
Have a good week ahead!
Tales from the Pixel Mines
November 23rd 2024, Ben Richards
This week: Beta feedback!
Last week, we released Phaser 4.0.0 beta 1. As a beta test, we're trying to find problems, both in the code and in the way it gets used. With the help of the community, we're succeeding at both!
Bug Fixes
Beta 2 was quickly released with several fixes.
roundPixels
quality enhancementFixes to DynamicTexture and Mask
Fixes to shader compilation on diverse systems
Fix Extern
Separate TileSprite dimension defaults
BaseFilterShader now accesses loaded shader cache keys correctly.
RoundPixels Quality Enhancement
The roundPixels
setting, available in the game config and on individual cameras, causes the vertices of sprites to snap to integer values. This helps keeping pixel art crisp by avoiding antialiasing.
For the best pixel art results, use the new
smoothPixelArt
config option for antialiased, unfiltered texels, or the existingpixelArt
option for aliased texels.
We've taken a few approaches to this setting in the past. They all have trade-offs and weak points. Phaser v3 has used a couple of techniques, including vertex shader rounding and CPU-side vertex rounding. CPU-side rounding got good results, but because the CPU runs one linear computation thread, it's slower. It also touches lots of regions of code, and such complexity is usually fragile.
So vertex shader rounding is probably the best choice. Unfortunately, there are a few problems which aren't immediately obvious.
The first problem: there's no round
function in the OpenGL ES Shading Language 1.0 specification. All we have is floor
, which will change 1.999 to 1.0. This is easy to hack: floor(value + 0.5)
is pretty much the same.
The second problem: no it's not. Under almost all circumstances, floor(value + 0.5)
returns the expected value. However, what happens when value == 0.5
? You might think that 0.5 + 0.5 == 1.0
, i.e. value
is rounded up. Unfortunately, that's not always true. The GPU handles floating-point numbers with limited precision. In fact, the precision can be quite low, especially if the shader is set to mediump
precision. Given different values, e.g. 1.5 versus 1000.5, we might round either up or down, without any way to predict it.
This might seem like a very unlikely problem. How often is a sprite placed at precisely 0.5 pixels? And what's the worst that could happen?
Unfortunately, it's very common, and quite visible. A sprite's position is, by default, midway between its corners. If its dimensions are odd, e.g. 27x53, "midway" means its corners are 13.5x26.5 pixels away. Their fractional component is precisely 0.5 - the problem value. And because we can't predict which way this value rounds, the corners may be up to 1 pixel off.
This means that the sprite may be slightly distorted, skipping or duplicating some pixels. Which is bad!
After some experimentation, we settled on a simple fix: adding bias. By adding a tiny quantity to the value, we can tip it into the precision range where 0.5 always rounds up - the desirable outcome. The former hack becomes floor(value + 0.5001)
. This is just as fast, but it produces perfect results in this very common use case.
Could this cause distortions in other scenarios? Probably, but they're far less likely. Consider that it's very easy to get 0.5, just by loading a texture with an odd dimension and positioning it at an integer. But it's very unlikely to get within the precision range of 0.5001 unless you're moving objects incredibly slowly.
Further, the position is calculated in "window space": it's always rounded to the nearest pixel on the screen. This limits any distortion to a single pixel. Even if you zoom in 1,000,000,000 times, you'll never see more than a pixel of distortion.
The value actually being rounded is computed on the CPU. JavaScript uses 64-bit floating point numbers, which have a great deal of precision, but some calculations use 32-bit buffers so the precision is not that high. Just avoid values in the quadrillions and everything will be fine.
Fixes to DynamicTexture and Mask
When we merged development of the Beam renderer with the latest Phaser code base, we inadvertently invalidated the way we create DynamicTextures. The base Texture naming rules were tightened, invalidating our original strategy. The Mask filter uses a DynamicTexture, so it stopped working too.
The fix was to standardize DynamicTexture naming to use UUIDv4 strings.
Fixes to Shader Compilation on Diverse Systems
Here's the truth about shaders: they work differently on different devices. In fact, sometimes something that works fine on one machine is an error on another: not due to different capabilities, but simply due to the way the shader compiler works. This can even differ between browsers on the same machine.
You can see where this is going. Shaders that worked fine on my machine failed to work on other devices. This is why community testing is good.
The most egregious example was a case where one vertex attribute was not used by a shader. On most systems, the attribute was still exposed; it just did nothing. On one Linux system, however, the attribute was removed by the shader, because it did nothing. So our code crashed when it tried to access it.
I've made changes to prevent these issues. Things seem to be working better.
Fix Extern
The Extern game object allows you to use other renderers within the Phaser render process. For example, you can use ThreeJS to render 3D graphics.
Our Extern handling retained a reference to removed code, which prevented it from working. This is fixed in beta 2.
Incidentally, with a bit of knowledge of the render system, you can now integrate external renders with Phaser systems like filters. The Extern.render
method receives the current DrawingContext
, which you can use.
const view = this.add.extern();
view.render = (renderer, drawingContext, calcMatrix) => {
// ... Set up extern renderer.
// Force update the current framebuffer.
renderer.glWrapper.updateBindingsFramebuffer({
bindings: {
framebuffer: drawingContext.framebuffer
}
}, true);
// ... Other rendering.
};
This might break things in external renderers, but that's no longer my department!
Separate TileSprite Dimension Defaults
If TileSprite received width or height 0, it would default both dimensions to those of the sprite texture. We changed it so that only the dimension that is 0 will default; if the other is valid, it won't be changed.
BaseFilterShader Shader Cache Keys
Fixed an internal bug with cache access.
Upcoming Changes
The main change is the RenderFilters
object. After much discussion, we've decided this is going to become a component on GameObjects instead. It will retain all existing functionality; it's just where it sits in a game that will change.
RenderFilters
is a wrapper around a GameObject. This made sense when thinking about rendering processes. The child GameObject is not rendered to the game; it's passed through filters, and those are what eventually renders out.
Unfortunately, this means that the wrapper has to duplicate a lot of information from the child. And after discussing use cases with expert user rex, we concluded that the duplication was too complex. If the child wants to manage its own properties, as is often the case, it would have to check if it was inside a wrapper, and do more complex work that isn't its own fault.
Phaser Beam works on the principle of "context agnostic rendering": objects should render the same in different contexts. This makes it flexible and fixes many bugs and incompatibilities from previous versions. So we don't want to bring in more context awareness.
The best solution is to turn RenderFilters
into a component on a GameObject. This means that the GameObject remains in the same context, and doesn't need to care about filters at all. There's no data duplication. It's much neater.
However, the Filters component is still a wrapper! It just wraps the render method of the object, rather than the entire object. This preserves the intent of the render process. It does make invoking the render method a bit more complex, but I'm working on keeping that invisible right now. In fact, this work should make it possible to fully control the render process of any object from the outside, which is cool.
Big thanks to rex for talking through the use cases with me.
Next Week
I'll be finishing up the new Filters component, and we should see a beta 3 release very soon. Then it's more testing and fixing.
Thanks for checking this out. We couldn't get Phaser this solid without your help.