release-v4000-alpha-26 featured image github v4000 Alpha 26

KAPLAY v4000 Alpha 26

lajbel, 01/20/2026, in bean’s house

Hey! lajbel here. We’re in the new year! 2026, and I’m happy to showcase you the KAPLAY v4000 Alpha 26.

As usual, let’s see how to install this version:

npm i kaplay@next
# If you want to use specifically this alpha
# npm i kaplay@4000.0.0-alpha.26

(!) Breaking Changes in Area

If you are an area() user, you must know that now if an area doesn’t have AreaComp.isSensor: true it will not process collisions. Now, let’s explain why.

Imagine you had an UI object, like a button and it is above the enemies. If enemies have area and the button too, the button will collide with the enemy. A previous workaround was using AreaComp.collisionIgnore, but collisions kept being processed. Now, by default, areas don’t collide. This is way more performant.

// BEFORE
const collidableObject = add([
	area(),
]);

// NOW
const collidableObject = add([
	area({ isSensor: true }),
]);

Note that body() objects doesn’t need AreaComp.isSensor.

Also, we decided to not add a way to make all areas AreaComp.isSensor: true by default, as a way to encourage performance-improving practices.

AreaComp.isVisuallyColliding

When you need to test if an object collides with another object in the screen space (and not in the world space, where the collision system works), you can use AreaComp.isVisuallyColliding, this is useful with fixed() objects, for example. Note that this requires extra processing that is outside the collision system.

Connectivity maps and flood fill

Written by MF

A connectivity map marks all islands in a graph with a unique ID, this way, for example, when pathfinding, you can first figure out if there even exists a path before starting to look for it, and if the start and destination are found to be in two separate islands (meaning there is no chance of there being any path), skip the path search step.

Flood fill is used to mark all similar nodes from a (set of) starting node(s). You can use it to pop all balls of similar color, like in puzzle bobble, or tsum tsum. But there’s lots of other usages too. Like puzzle bubble uses it to know which ball are attached to the ceiling, by flooding from all ceiling balls regardless of color. All balls not “filled” are loose.

function testFill() {
    const grid = new NavGrid(2, 3, (a, b) => true);

    debug.log(floodFill(grid, 0, node => (node & 1) == 0));
    debug.log(floodFill(grid, 1, node => node & 1));
}

testFill();

function testConnectivity() {
    const grid = new NavGrid(2, 3, (a, b) => (a & 1) === (b & 1));
    console.log(buildConnectivityMap(grid));
}

testConnectivity();

Acknowledgments

Thank you reader for your time, and thanks to all the contributors, especially those who made this release possible: @Stanko, @mflerackers, @dragoncoder047 and @benhuangbmj.

Also, I hope you’re enjoying these release blogs.

Changelog for 4000.0.0-alpha.26

Added

Changed

Fixed

  • Fixed tween() not cloning the passed vectors/colors - @lajbel
  • Fixed timer() related events (tween/loop/wait) not taking debug.timeScale into account - @Stanko
  • Fixed the vibration effect on bodies introduced in alpha.25 thanks to @lajbel’s debugging skills - @mflerackers
  • Fixed SpriteComp.hasAnim() returning false erroneously when the animation named was just constant frame 0 - @dragoncoder047
  • Fixed levelComp.serialize() use for…of in the place of the for…in when looping through the tile object keys - @benhuangbmj
  • Fixed input events attached to a game object having the event’s paused value reset when the object is paused or unpaused - @dragoncoder047
  • Hidden objects are processed again in transform - @mflerackers
  • When the parent is changed, the transform is invalidated - @mflerackers
  • Fixed click and hover for fixed() objects - @mflerackers
  • Object toWorld/fromWorld/toScreen/fromScreen work more logical now - @mflerackers
  • Sticky platforms work again - @mflerackers

Removed

  • (!) onClick(() => {}) was removed, use onMousePress() instead. onClick("tag", () => {}); stays the same,
kaplay logo