Changelog

All notable changes to this project will be documented in this file.

The format is (mostly) based on Keep a Changelog, and this project adheres to Semantic Versioning.

[unreleased]

Added

  • 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

Changed

  • 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

Fixed

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

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,

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

Added

Changed

  • (!) 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
  • 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
  • The grid broadphase has been rewritten for performance - @mflerackers
  • Global retrieve() method to get the objects with area within a certain rectangle - @mflerackers

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

Added

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] - 2025-11-05

Added

Changed

Fixed

[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:

  • 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

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

Added

  • Added GameObjRaw.serialize() for serializing the game object and its components. - @mflerackers, @lajbel

    const bean = add([sprite("prefab")]);
    const beanSerialized = bean.serialize();
  • Added createPrefab() for serializing an object and register it (or not) as a prefab from a Game Object. - @mflerackers, @lajbel

    const beanObj = add([sprite("bean")]);
    
    // Serialize game object and register it as a prefab asset
    createPrefab("bean", beanObj);
    
    addPrefab("bean");
    
    // Just get serialized data
    const serializedBean = createPrefab(beanObj);
    
    addPrefab(beanObj);
  • Added addPrefab() for creating an object previously serialized - @mflerackers, @lajbel

    loadPrefab("bean", "/bean.kaprefab");
    
    addPrefab("bean");
  • 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

Fixed

  • Fixed shader error messages - @dragoncoder047
  • Fixed compatibility issues when calculating font height with missing TextMetrics props - @imaginarny

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

Added

  • Added loadSpriteFromFont() for loading a bitmap font from a loaded sprite. - @dragoncoder047

Changed

  • Improved various doc entries. - Many contributors

Fixed

Removed

  • (!) loadPedit() was removed - @lajbel

[4000.0.0-alpha.19] - 2025-05-16

This version changelog covers versions 4000.0.0-alpha.0 through 4000.0.0-alpha.19, as we didn’t have a concise changelog strategy before.

Added

Changed

Fixed

Removed

  • (!) make() was sent to doom - @lajbel

kaplay logo