Skip to content

Releases: kaplayjs/kaplay

4000.0.0-alpha.27.1

Choose a tag to compare

@lajbel lajbel released this 12 May 21:17

[4000.0.0-alpha.27.1] - 2026-05-12

Added

  • Added a repack: false option to loadSpite() and a repack parameter to
    loadSpriteAtlas(), for faster loading if you're packing stuff at build-time
    (#1063) - @dragoncoder047

Changed

  • (!) Updated "explicit LoadSpriteOpt.frames" method of loading a sprite
    with animations (via either loadSprite() or loadSpriteAtlas()) to use
    pixel values for the coordinate space, which is more intuitive (#1061) -
    @dragoncoder047
  • Added padding around edges of spritesheet to prevent stretch if uv ends up out
    of bounds (#1076) - @dragoncoder047

Fixed

  • Updated texture packer refreshing to only refresh when a sprite is actually
    requested to be drawn, to reduce the number of times the texture is synced to
    the GPU, which is slow (#1058) - @dragoncoder047
  • Fixed loadBitmapFontFromSprite() so that the font drawn using it is drawn
    with the correct scale, as one would expect (#1063) - @dragoncoder047
  • Added exists() which also checks whether the object is not undefined. This
    should temporarily fix the problem with undefined objects during collision due
    to iterators being invalidated by destroy. - @mflerackers
  • Fixed broadphase event duplication on scene switch, causing repeated
    broadphase object registrations, which resulted in a performance drop
    (#1074) - @imaginarny, @mflerackers
  • Fixed broadphase objects cleared on scene switch including those with stay()
    (#1077) - @imaginarny, @mflerackers

What's Changed

Full Changelog: 4000.0.0-alpha.27...4000.0.0-alpha.27.1

4000.0.0-alpha.27

Choose a tag to compare

@lajbel lajbel released this 19 Mar 03:59
4a5b94b

[4000.0.0-alpha.27] - 2026-03-19

Added

  • Added debug.warn() to log warning messages (#1028) - @lajbel
  • Added debug.logMessage() to log a messages array with a wrapping style
    (warn, error, info) (#1028) - @lajbel
  • Added per-sprite and per-font filter options, which override the global
    texFilter and fontFilter options (#1050) - @dragoncoder047
  • Added global fontFilter option to control default font rasterization filter
    (#1050, #1052) - @dragoncoder047
  • Added tileMode option to 9-slice sprites with four tiling strategies:
    'none' (stretch all), 'edges' (tile edges only), 'center' (tile center
    only), and 'all' (tile both edges and center) (#996) - @JustKira
  • Added a calculate() method to the internal FPS counters, so advanced users
    can access them to create their own FPS monitor (#1010) - @dragoncoder047
  • Added Intl.Segmenter-based grapheme splitting for proper Indic language
    support, via the locale option in DrawTextOpt (#1013) - @shajidhasan
  • Added topMostOnlyActivate kaplay option. When true, only the topmost object
    will receive clicks. This avoids problems in a UI where elements overlap -
    @mflerackers
  • Added a fill() component - @mflerackers
  • Added lerpAngle() helper function to interpolate between clamped angles,
    preventing 360掳 spins during transitions from 180 to -180 (#1054) -
    @imaginarny
  • Added piecewiseBezier() and piecewiseCatmullRom() to evaluate curves with
    multiple points - @mflerackers

Removed

  • (!) The texture uv coordinates for primitives (solid- or gradient-filled
    circles, rectangles, lines, and polygons) have now been changed to (1, 1)
    instead of (0, 0), so shaders written for primitives will need to be updated.
    (#1021) - @dragoncoder047

Changed

  • (!) The global onDraw() handler's no-tag form now always draws before
    all game objects are drawn, regardless of whether it was attached after game
    objects were added
    (#977) - @lajbel
  • (!) The sprite data format has been changed to allow individual frames to
    be on different GPU textures. Now SpriteData.tex doesn't exist, and
    SpriteData.frames is a list of Frames instead of a list of Quads. A
    Frame contains tex and q (quad) properties that contain that data.
    (#1021) - @dragoncoder047
  • Updated the texture packer to use a new packing algorithm which may get more
    sprites onto the same texture, improving graphics batching performance
    (#1011) - @dragoncoder047
  • Updated all sprite and font loading to pack everything in the same texture to
    allow it to all batch together, for speed and efficiency (#1021) -
    @dragoncoder047
  • Added spritesheet repacking, so spritesheet images that contain lots of blank
    space don't waste texture memory (#1021) - @dragoncoder047

Fixed

  • Fixed vertical alignment of text within a now-consistent bounding box across
    browsers (#1053) - @imaginarny
  • Fixed tiled mode drawing of sprites ignoring opacity when it was 0 (#1020) -
    @dragoncoder047
  • Now, all global events handlers are available in scopes, app.onXXXX and
    scene.onXXXX() (#977) - @lajbel
  • Fixed input events attached to paused ancestors not being paused (#1009) -
    @amyspark-ng, @dragoncoder047
  • Fixed type UniformValue union not including Texture, a valid option
    (#1018) - @dragoncoder047
  • Text component no longer hangs if the requested width is too narrow for a
    single character - @dragoncoder047
  • Fixed input events attached to paused ancestors not being paused (#1009) -
    @amyspark-ng, @dragoncoder047
  • Fixed type UniformValue union not including Texture, a valid option
    (#1018) - @dragoncoder047
  • Fixed event crash when using onLoad or other events that doesn't return an
    EventController, and then using go() (#1024) - @lajbel, credits to
    @dragoncoder047
  • Fixed the unexpected behavior of not preserving the aspect ratio in
    drawSprite (#1026) - @benhuangbmj
  • Fixed onClick() and onCollide() tag variants no longer working -
    @mflerackers
  • Fixed destroy() messing up enumeration during update() and
    fixedUpdate(), thanks to @imaginarny for helping to find the cause -
    @mflerackers
  • Fixed canvas not resizing to fullscreen on setFullscreen() (#1055) -
    @imaginarny

New Contributors

Full Changelog: 4000.0.0-alpha.26...4000.0.0-alpha.27

4000.0.0-alpha.26

Choose a tag to compare

@lajbel lajbel released this 13 Jan 01:47

[4000.0.0-alpha.26] - 2026-01-12

Added

  • Added AreaCompOpt.isSensor. Areas without body or is sensor will no longer
    be eligible for collisions - @mflerackers
  • Added floodFill() for puzzle games - @mflerackers
  • Added AreaComp.isVisuallyColliding to test collisions in screen space. This
    can be used for fixed objects which do not necessarily collide in world space.
    Note that this involves additional processing as it tests outside the
    collision system, which works in world space - @mflerackers
  • Added buildConnectivityMap() - @mflerackers
  • Added buildConvexHull() - @mflerackers

Changed

  • Both worldPos and screenPos are properties now - @mflerackers

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

What's Changed

New Contributors

Full Changelog: 4000.0.0-alpha.25...4000.0.0-alpha.26

4000.0.0-alpha.25

Choose a tag to compare

@lajbel lajbel released this 24 Dec 01:32

[4000.0.0-alpha.25] - 2025-12-23

Added

  • Added the fakeMouseMove event in FakeMouseComp, it will triggers when you move the object - @lajbel
  • Global retrieve() method to get the objects with area within a certain rectangle - @mflerackers

Changed

  • Transforms are now only recalculated when needed. Thus static objects no longer increase computation in the transform phase - @mflerackers
  • Areas are now only recalculated when the area settings or (optional) renderArea has changed - @mflerackers
  • World (transformed) areas are now only recalculated when the area or transform has changed - @mflerackers
  • World bounding boxes are now only recalculated when the world area has changed - @mflerackers
  • Broad stage collision detection spatial structures are now only updated when an object's world bounding box has changed - @mflerackers
  • You can no longer change the position of an object by doing obj.pos.x += 1. You need to assign a new Vec2 or use moveBy instead - @mflerackers
  • The grid broadphase has been rewritten for performance - @mflerackers

Full Changelog: 4000.0.0-alpha.24...4000.0.0-alpha.25

4000.0.0-alpha.24

Choose a tag to compare

@lajbel lajbel released this 12 Dec 12:34

[4000.0.0-alpha.24] - 2025-12-12

Added

  • Added the maxTimeStep and fixedUpdateMode options, as well as
    setFixedSpeed() for more granular control over fixed update and timing -
    @dragoncoder047
  • Added parameterized formatting tags like "[color=red]Red text![/color]" in
    CharTransformFunc for more powerful text formatting options -
    @dragoncoder047
  • Added createRegularPolygon() and createStarPolygon() to create 2D regular
    polytopes - @mflerackers
  • Added createCogPolygon() to create 2D regular cogs - @mflerackers
  • Added getSpriteOutline() that takes a sprite asset and returns a polygon
    showing the outline - @milosilo-dev
  • Added Quadtree for collision detection (only for fixed size screen for now,
    needs expansion) - @mflerackers
  • Added vertical sweep and prune - @mflerackers
  • Added configuration to choose broad phase algorithm - @mflerackers

Fixed

  • Fixed the fakeMouse() component not giving the right position when the
    camera transform was not the identity matrix - @dragoncoder047
  • Fixed tall fonts being cropped - @anthonygood
  • Fixed the sprite animation onEnd() callback being called before the
    animation actually stopped, so if the onEnd callback started a new animation,
    the new animation was instantly stopped - @dragoncoder047
  • Now playMusic() actually uses the requested volume and playback rate given
    in the options - @dragoncoder047

4000.0.0-alpha.23

Choose a tag to compare

@lajbel lajbel released this 05 Nov 23:19

[4000.0.0-alpha.23] - 2025-11-05

Added

  • Added getGamepadAnalogButton() to read the analog value of buttons like the
    triggers - @dragoncoder047

    isGamepadButtonDown("rtrigger"); // -> true/false, 0/1
    getGamepadAnalogButton("rtrigger"); // -> analog value between 0 (not pressed) and 1 (fully pressed)
  • Added chorded button bindings using the Buttons API, so you can bind different
    actions to tab and shift+tab, and handle them like normal. Also works with
    gamepads and mouse! - @dragoncoder047

    kaplay({
        buttons: {
            forward: {
                keyboard: "tab",
                gamepad: "south",
            },
            backward: {
                keyboard: "shift+tab",
                gamepad: "rshoulder+south",
            },
        },
    });
  • Added skew to text formatting, so now italics is possible - @dragoncoder047

  • Added lifetime scopes, a way to define the lifetime of an event handler
    using a specific scope, scene, app or a game object - @lajbel,
    @dragoncoder047

    app.onUpdate(() => {
        // runs until it is cancelled
    });
    
    scene("game", () => {
        const obj = add([]);
    
        obj.onUpdate(() => {
            // runs until obj is destroyed
        });
    
        scene.onUpdate(() => { // or just onUpdate(() => {
            // runs until scene is changed
        });
    });

    All the available handlers in the scopes are GameEventHandlers ones:

    • onKeyDown()
    • onKeyPress()
    • onKeyPressRepeat()
    • onKeyRelease()
    • onCharInput()
    • onMouseDown()
    • onMousePress()
    • onMouseRelease()
    • onMouseMove()
    • onScroll()
    • onTouchStart()
    • onTouchMove()
    • onTouchEnd()
    • onGamepadConnect()
    • onGamepadDisconnect()
    • onGamepadButtonDown()
    • onGamepadButtonPress()
    • onGamepadButtonRelease()
    • onGamepadStick()
    • onButtonDown()
    • onButtonPress()
    • onButtonRelease()
    • onTabHide()
    • onTabShow()

    And this game object handlers may differ when using it with obj and
    scene/app:

    • onFixedUpdate()
    • onUpdate()
    • onDraw()
  • Added app scope for app event handlers - @lajbel

    app.onUpdate(() => {
        // runs until it is cancelled
    });
  • Added KAPLAYOpt.defaultLifetimeScope for setting the default lifetime scope
    used for event handlers - @lajbel

    kaplay({
        defaultLifetimeScope: "app", // default is "scene"
    });
    
    onKeyPress("space", () => {
        // runs until is cancelled
    });
  • Added skew to text formatting, so now italics is possible - @dragoncoder047

Changed

  • (!) Renamed onShow() to onTabShow() and onHide() to onTabHide() -
    @lajbel

  • In addition to being the scene() function, now scene is also a scope for
    scene event handlers - @lajbel

    scene("game", () => {
        scene.onUpdate(() => { // or just onUpdate(() => {
            // runs until scene is changed
        });
    });

Fixed

  • Now pushScene() and popScene() give the arguments to the scene in the same
    way that go() does rather than passing them all to the first argument as an
    array - @dragoncoder047
  • Fixed a flicker due to the fadeIn not setting opacity until the next frame -
    @mflerackers
  • Fixed FPS cap not working correctly - @mflerackers, @dragoncoder047

Full Changelog: 4000.0.0-alpha.22...4000.0.0-alpha.23

4000.0.0-alpha.22

4000.0.0-alpha.22 Pre-release
Pre-release

Choose a tag to compare

@lajbel lajbel released this 09 Oct 17:35

[4000.0.0-alpha.22] - 2025-10-9

Added

  • Added KAPLAYOpt.types, kaplayTypes() and Opt to config specific
    TypeScript Advanced Features (TAF) - @lajbel

    kaplay({
        types: kaplayTypes<
            // Opt<> is optional but recommended to get autocomplete
            Opt<{
                scenes: {}; // define scenes and arguments
                strictScenes: true; // you can only use defined scenes
            }>
        >(),
    });
  • Added TypesOpt.scenes to type scenes and parameters - @lajbel

    const k = kaplay({
        types: kaplayTypes<
            Opt<{
                scenes: {
                    game: [gamemode: "normal" | "hard"];
                    gameOver: [score: number, highScore: number];
                };
            }>
        >(),
    });
    
    // If you trigger autocomplete it shows "game" or "gameOver"
    k.scene("game", (gamemode) => {
        // gamemode is now type "normal" | "hard"
    
        // @ts-expect-error Argument of type 'string' is not assignable
        // to parameter of type 'number'.
        k.go("gameOver", "10", 10); //
    });

    The methods that support this are:

    • scene
    • go
    • onSceneLeave
    • getSceneName
  • Added TypesOpt.strictScenes to make usable scenes just the ones defined -
    @lajbel

    const k = kaplay({
        types: kaplayTypes<
            Opt<{
                scenes: {
                    game: [gamemode: "normal" | "hard"];
                    gameOver: [score: number, highScore: number];
                };
                strictScenes: true;
            }>
        >(),
    });
    
    // @ts-expect-error Argument of type '"hi"' is not assignable to
    // parameter of type '"game" | "gameOver"'.
    k.scene("hi", () => {});
  • Added named animations - @mflerackers

    By giving a name to an animation, you can define more than one animation

    const anim = obj.animation.get("idle");
    anim.animate("pos", [0, 5, 0], { relative: true });
  • Added screenshotToBlob() to get a screenshot as a Blob - @dragoncoder047

  • Added getButtons() to get the input binding buttons definition - @lajbel

  • Added RuleSystem, DecisionTree and StateMachine for enemy AI -
    @mflerackers

  • Added constraint components for distance, translation, rotation, scale and
    transform constraints - @mflerackers

  • Added inverse kinematics constraint components using FABRIK and CCD, the
    latter one can use bone constraints to constrain the angle - @mflerackers

  • Added skew to Mat23, transformation stack, RenderProps, GameObjRaw as well as
    a component - @mflerackers

  • Added texture uniforms, in order to access more than one texture at a time in
    shaders - @mflerackers

Fixed

  • Now error screen should be instantly shown - @lajbel

Changed

  • Now, you can use color(c) with a hexadecimal literal number (ex: 0x00ff00) -
    @lajbel
    // blue frog
    add([
        sprite("bean"),
        color(0x0000ff),
    ]);
  • (!) KAPLAYCtx doesn't use generics anymore. Now, KAPLAYCtxT uses
    them - @lajbel
  • Now, kaplay will return KAPLAYCtx or KAPLAYCtxT depending if it's using
    Advanced TypeScript Features or not - @lajbel
  • loadShader() now also checks for link errors as well as compile errors and
    reports them rather than just silently trying to use a borked shader -
    @dragoncoder047
  • The debug record() function now records with sound enabled like it should -
    @dragoncoder047
  • Now KAPLAYOpt.spriteAtlasPadding is set to 2 by default - @lajbel
  • Transformation and drawing is split now, so the transform can be modified
    before drawing - @mflerackers

Full Changelog: 4000.0.0-alpha.21...4000.0.0-alpha.22

4000.0.0-alpha.21

4000.0.0-alpha.21 Pre-release
Pre-release

Choose a tag to compare

@lajbel lajbel released this 07 Aug 17:20

[4000.0.0-alpha.21] - 2025-08-07

Added

  • Added Prefabs - @mflerackers, @lajbel, @amyspark-ng and other contributors.
  • Added new scene methods pushScene() and popScene(), for stack behaviour in
    scenes - @itzKiwiSky
  • Added throwError() for throwing custom errors to the blue screen, even
    errors KAPLAY can't handle. - @lajbel
  • Added insertionSort() - @dragoncoder047
  • Added a mapping for PS5 (DualSense) gamepads, so now you can bind actions to
    the touchpad press (only works in Chrome for some reason) - @dragoncoder047

Changed

  • Renamed KAPLAYOpt.tagsAsComponents to KAPLAYOpt.tagComponentIds - @lajbel
  • Now moving mouse changes the value of getLastInputDevice() - @amyspark-ng
  • Now GameObjRaw.exists() work for nested objects

Fixed

New Contributors

Full Changelog: 4000.0.0-alpha.20...4000.0.0-alpha.21

4000.0.0-alpha.20

4000.0.0-alpha.20 Pre-release
Pre-release

Choose a tag to compare

@lajbel lajbel released this 15 Jun 16:49

[4000.0.0-alpha.20] - 2025-06-15

Added

  • Now you can use the frames of a sprite in an atlas also as a font -
    @dragoncoder047
  • Improved various doc entries. - All Contributors.

Fixed

  • Fixed AreaComp#onClick() attaching events to app, instead of object, so
    event wasn't being paused with obj.paused - @lajbel
  • Fixed all touch events having a bad transform - @lajbel
  • Fixed sprite scaling not working properly when letterbox - @mflerackers
  • Fixed "add" event running twice in addLevel() tiles - @lajbel
  • Fixed blend component having a wrong ID - @lajbel

Removed

Full Changelog: 4000.0.0-alpha.19...4000.0.0-alpha.20

3001.0.19

Choose a tag to compare

@lajbel lajbel released this 15 Jun 16:50

[3001.0.19] - 2025-06-15

  • Fixed AreaComp#onClick() attaching events to app, instead of object, so
    event wasn't being paused with obj.paused - @lajbel
  • Fixed all touch events having a bad transform - @lajbel

Full Changelog: 3001.0.18...3001.0.19