Recently I announced that I’m starting development on q5play (p5play version 4). Check out these results from early testing: a 10x performance improvement!

Switching from planck.js to Box2D v3 via wasm for physics is like swapping engines in a car. Most of the work I’ll be doing is internal, behind the scenes stuff.
Yet, as with any major version update, creating q5play also offers an opportunity to make a few breaking changes to the API. I’m writing this article far in advance of q5play’s release to give y’all a heads up on issues I hope to amend.
New Frame Cycle
One issue with p5play is that the physics simulation update runs after the user defined `draw` function and after automatic sprite drawing. This essentially adds a one frame input delay between when the user takes an action and when they see results on screen.
1/60th of second is not much, but you can feel the difference in responsiveness.
In p5play v3.26, I added support for a new frame cycle ordering.
handle user input & game logic: `update()`
physics simulation: `world.physicsUpdate()`
rendering:`drawFrame()`
post-processing: `postProcess()`
To prepare users for v4, I’ve updated the p5play Learn page examples to use `update`.
https://p5play.org/learn/sprite.html?page=0
In version 4, `draw()` will be run at the rendering phase, so there will be no `drawFrame()` function. I’d also like q5play to better support running with frame rates other than 60hz.
For more info on game loops check out this article:
https://gameprogrammingpatterns.com/game-loop.html
sprite.tileSize
will be removed
The goal of the `sprite.tileSize` functionality was to make it easy to create tile based games (think moving pieces on a chess board).
However, it’s a bit strange to have separate units for normal drawing vs positioning sprites. It's also inefficient to always use the tileSize multiplier internally, whether it’s actually in use or not. Also this function did too much behind the scenes stuff— even for my liking. I think it’s better to require that users understand what’s actually going on.
In p5play v3, I already added explicit controls for everything that goes into this effect: `world.meterSize`, `p5play.snapToGrid`, and `p5play.gridSize`.
Check out the new Learn page on “World Sizing”.
https://p5play.org/learn/world.html?page=5
In version 4, the deprecated `sprite.tileSize` will be removed.
Ani
objects will be more lightweight
(Most p5play v3 users probably aren’t familiar with these legacy features, since I’ve never showcased them on the p5play Learn pages.)
I’d like `animation` and `ani.draw` the functions for displaying `Ani` objects, to not apply any transformations: making it more like the `image` function.
animation(ani, dx, dy, dw, dy);
I want to make these changes because currently, displaying sprites with animations is inefficient because `push`, `pop`, `translate`, `rotate`, and `scale` are used inside `sprite.draw` and run again inside `animation.draw`.
In version 4, `ani.rotation` and `ani.update` will be removed. `ani.offset` and `ani.scale` will only be applied if the ani is displayed by a sprite.
New way to ignore contact between sprites
In p5play, I made a simple contact system, with `collides` vs `overlaps` contact relationships. It’s an abstraction layer that I worked really hard to refine and optimize. I’m particularly proud of it. :)
But for better performance, in cases in which developers want contact between sprites to simply be ignored, their really ought to be a third relationship. I’m thinking of calling it `pass`/`passes`. The `overlap` relationship can be used in this manner, but inefficiently so, since it makes sensors on the sprites to check for overlaps.
New behavior for all move and rotate functions
(Note that `sprite.moveTo` is used by `sprite.move` internally.)
Warning this one’s a long story/ramble.
My original idea with `sprite.moveTo` was to give p5play developers a simple way to do point and click MMO style player movement (like in Club Penguin or League of Legends).
From the start, there was confusion around the fact that unlike `sprite.moveTowards`, which can be used continuously in the update loop, the other two move functions are meant to be used after an event “impulse”, such as on the frame a player clicks the mouse or presses a key.
My original idea was that these functions would pre-calculate approximately the amount of frames it’d take for a sprite to reach its destination, then a `setInterval` based loop would start that would check on each frame if the movement was actually finished. Yet, all that is pointless if the user merely wants to continuously move a sprite to a position at a specific speed, but perhaps never quite reach the position. To provide an easy way to do that, I made `sprite.angleTo` which can be set to a sprite’s direction.
Later on, a user on Discord wanted `sprite.move` and `sprite.moveTo` to return a Promise that fulfills when the sprite reaches its destination. I liked the idea of being able to do movement sequences this way and took the suggestion.
But what to do if the sprite’s movement is obstructed? What if the sprite is still going the right way but it’s moving too slow? What if the sprite goes just slightly off course, but visually seems to still be moving in the correct direction? I try to make the Promise fail in these cases, but the resulting system is too brittle for my liking. In practice, it’s annoying to use the sprite move functions if physics are involved, ie the world has gravity, the sprite has drag, or when moving a sprite across a surface that has friction. They just feel broken, especially `sprite.moveTowards`, when the speed provided is not enough to move a sprite towards a destination.
The move functions are basically only good for what I originally intended them to be for: unobstructed, top down style, character movement. So while these functions may work well in some games, I don’t feel satisfied with their existence as library level functions, which should be highly optimized and useful in a wide variety of games.
There's gotta be a better way!
For starters, checking if the sprite reached its destination could be done by q5play behind the scenes as part of the main frame cycle loop. No need for separate loops.
For `sprite.moveTowards`, a `GrabberJoint` works in a similar but more robust, physics-based manner. So how about an interface like `sprite.moveTowards(position, tracking, maxForce)` that uses a `GrabberJoint` behind the scenes? The tracking percentage would affect the joint’s target position. I think `moveTo` could be implemented in a similar way. Yet, I fear the performance of such functions would be awful if applied to a lot of sprites at once.
What if instead of failing by default, q5play constantly keeps sprites on target to the position and at the speed specified in `sprite.moveTo`. I think going more imperative with these functions is the best approach. The sprite’s movement towards the destination could still be interrupted and cancelled by directing the sprite elsewhere with another `sprite.moveTo` call or setting `sprite.direction`.
But what if it’s physically impossible to reach the destination? Maybe the sprite just keeps trying until the movement is interrupted or perhaps there could be a time out where it’ll just stop.
Faster on-screen filtering and culling
My code in p5play for checking if a sprite is on-screen, and thus should be displayed, is really basic and inefficient. Also the code for culling sprites that go too far offscreen, is just as bad.
I plan to use Box2D’s shape casting functionality to do this.
Support for animated easing
I’d also like to add support for easing/tweening with anime.js to the move and rotate functions and animation playback. Probably this will be something I’ll work on after releasing q5play v4.0.
Project Timeline?
Seems I've got a lot of work to do. 😅 Check out the project planning page:
https://github.com/orgs/q5play/projects/1/views/1
I’m usually overconfident about how quickly I can make progress on a big project like this. For example, I thought I’d finish q5.js WebGPU over the summer and now it's January lol.
“You don't know what you don't know!”
Well, I do know re-implementing the contact system will be a challenge. 😬
In a month, I’d like to have a very basic alpha version that can be used to make physics demos! 🤞🏻
I’ll keep y’all updated. 🤝