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
skewto 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,appor a game object - @lajbel, @dragoncoder047app.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
GameEventHandlersones:onKeyDown()onKeyPress()onKeyPressRepeat()onKeyRelease()onCharInput()onMouseDown()onMousePress()onMouseRelease()onMouseMove()onScroll()onTouchStart()onTouchMove()onTouchEnd()onGamepadConnect()onGamepadDisconnect()onGamepadButtonDown()onGamepadButtonPress()onGamepadButtonRelease()onGamepadStick()onButtonDown()onButtonPress()onButtonRelease()onHide()onShow()
And this game object handlers may differ when using it with
objandscene/app: -
Added
appscope for app event handlers - @lajbelapp.onUpdate(() => { // runs until it is cancelled }); -
Added
KAPLAYOpt.defaultLifetimeScopefor setting the default lifetime scope used for event handlers - @lajbelkaplay({ defaultLifetimeScope: "app", // default is "scene" }); onKeyPress("space", () => { // runs until is cancelled }); -
Added
skewto text formatting, so now italics is possible - @dragoncoder047
Changed
-
(!) Renamed
onShow()toonTabShow()andonHide()toonTabHide()- @lajbel -
In addition to being the
scene()function, nowsceneis also a scope for scene event handlers - @lajbelscene("game", () => { scene.onUpdate(() => { // or just onUpdate(() => { // runs until scene is changed }); });
[4000.0.0-alpha.22] - 2025-10-9
Added
-
Added
KAPLAYOpt.types,kaplayTypes()andOptto config specific TypeScript Advanced Features (TAF) - @lajbelkaplay({ 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.scenesto type scenes and parameters - @lajbelconst 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.strictScenesto make usable scenes just the ones defined - @lajbelconst 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 aBlob- @dragoncoder047 -
Added
getButtons()to get the input binding buttons definition - @lajbel -
Added
RuleSystem,DecisionTreeandStateMachinefor 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), ]); - (!)
KAPLAYCtxdoesn’t use generics anymore. Now,KAPLAYCtxTuses them - @lajbel - Now,
kaplaywill returnKAPLAYCtxorKAPLAYCtxTdepending 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.spriteAtlasPaddingis set to2by default - @lajbel - Transformation and drawing is split now, so the transform can be modified before drawing - @mflerackers
[4000.0.0-alpha.21] - 2025-08-07
Added
-
Added
GameObjRaw.serialize()for serializing the game object and its components. - @mflerackers, @lajbelconst 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, @lajbelconst 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, @lajbelloadPrefab("bean", "/bean.kaprefab"); addPrefab("bean"); -
Added new scene methods
pushScene()andpopScene(), 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
- Now
GameObjRaw.exists()work for nested objects - Now moving mouse changes the value of
getLastInputDevice()- @amyspark-ng - (!) Renamed
KAPLAYOpt.tagsAsComponentstoKAPLAYOpt.tagComponentIds- @lajbel
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
- Fixed
AreaComp#onClick()attaching events to app, instead of object, so event wasn’t being paused withobj.paused- @lajbel - Fixed all touch events having a bad transformation - @lajbel
- Fixed sprite scaling not working properly with
KAPLAYOpt.letterbox- @mflerackers - Fixed “add” event running twice in
addLevel()tiles - @lajbel - Fixed blend component having a wrong ID - @lajbel
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
-
Added
fakeMouse()to create a fake mouse cursor - @lajbelconst myCursor = add([fakeMouse(), sprite("kat"), pos(100, 100)]); myCursor.press(); // trigger onClick events if the mouse is over myCursor.release(); myCursor.moveBy(vec2(100, 200)); // move as your wish -
Added
system()to replace internal events or create new - @mflerackerssystem("collision", () => { // system code }, [SystemPhase.AfterFixedUpdate, SystemPhase.AfterUpdate]), -
Added
ellipse()component - @mflerackers -
Added circle and (rotated) ellipse collision shapes - @mflerackers
-
Added
clipLineToRect()- @mflerackers -
Added
obj.setParent()to change the parent of a game object - @mflerackers -
Added restitution and friction to physics - @mflerackers
-
All game objects have methods
onTag()andonUntag()for watching tag changes - @mflerackers -
Added
SystemPhaseenum to identify different lifecycle events in the game loop that systems can hook into - @mflerackers -
Added Blend mode is selectable to change how sprites are composited on top of each other - @mflerackers
-
Added Picture API to cache drawing of selected objects - @mflerackers
-
Added
drawCanvas()- @mflerackers -
Added
video()component to embed a video file into the game - @mflerackers -
Added
level()component and parent argument toaddLevel()- @KeSuave -
Allow the
text()component to change the font and apply shaders per-character - @dragoncoder047 -
Allow characters in text to be scaled and have the text flow around it with
stretchInPlace: false- @dragoncoder047 -
Expose the formatted text parsing functions to allow manipulation of formatted text - @dragoncoder047
-
Now you can use the frames of a sprite in an atlas also as a font - @dragoncoder047
-
More errors raised during object creation are caught and cause the blue crash screen - @lajbel
-
The blue crash screen will no longer fail to draw if the error message contains brackets - @dragoncoder047
-
Now you can use the global option
inspectOnlyActive: falseto prevent paused objects from showing in the debug inspect view, this is useful if you are swapping out objects for different views - @dragoncoder047 -
The
OffScreenCompnow has an optionoffscreenDistanceto change the distance at which an object is considered off-screen - @dragoncoder047 -
Now you can cherry-pick specific frames of a sprite sheet by using the
frameslist, instead of being limited to consecutive framesstartandend- @dragoncoder047 -
wave()can now go back and forth between any value that is able to be used withlerp()- @dragoncoder047, @mflerackers -
The
TextInputComphas more events:focus,blur,input, andchange, to better interact with the text input state - @dragoncoder047 -
Areas no longer struggle with parents whose transform inst’t up-to-date - @mflerackers
-
Exported step and smoothstep - @mflerackers
-
Small circles and arcs use now less points than larger ones - @mflerackers
-
Added pushMatrix, storeMatrix and loadIdentity to the stack functions - @mflerackers
-
Typed
StateComp- @amyspark-ng -
Added bias to line drawing, which controls the offset from the center of the line - @mflerackers
-
Added
SpriteAnimPlayOpt.preventRestartto allowSpriteComp.play()to be called from anonUpdate()and not reset the animation to frame 0 - @dragoncoder047
Changed
- (!) - Now
z()is global instead of relative - @mflerackers - (!) Layers now work globally, no longer only between siblings - @mflerackers
- (!): Changed default behavior to
kaplay({ tagsAsComponents: false }) - The physics engine creates less garbage - @mflerackers
- Tag-based events are slightly faster - @dragoncoder047
- Moved camera to the shader - @mflerackers
- Replaced the Separating Axis Theorem (SAT) collision detection module with the
Gilbert–Johnson–Keerthi
(
GJK) algorithm, which is faster - @mflerackers - Now if you pass a nullish value to
.use()it throws an error - Improved TypeScript in game objects - @amyspark-ng, @lajbel, @KeSuave
- Added/updated JSDoc comments to some members - @ErikGXDev, @dragoncoder047
- The
textInputcomponent’sisFocusedproperty is now a one-hot lockout, setting it to true (focused) will clear focus from all the other text inputs - @dragoncoder047 - Changed the API of
HealthComp- @amyspark-ng - CapsLock now affects
TextInputComp- @amyspark-ng
Fixed
GameObjRaw.exists()now correctly returns false if the parent was destroyed but obj wasn’t - @dragoncoder047Vec2.dot()now actually does the Correct Calculation™ - @andrenanninga- Fixed
debug.timeScalenot affectingdt()scale - @lajbel - Fixed
wait()’sTimerComp.onEnd()being waiting for twice the duration - @dragoncoder047 - Fixed non-focused
TextInputCompbackspace - @KeSuave - Fixed 9slice sprites behaving wrong when using
Anchor- @mflerackers - Fixed rendering glitches with outlines on circles - @mflerackers
- Fixed
setCursorLocked(true)throwing error if the browser is using the old non-Promise-based API return value - @imaginarny - Fixed
PatrolCompnot going to last waypoint - @nojaf - Fixed various TypeScript types - @amyspark-ng, @lajbel, @KeSuave
Removed
- (!)
make()was sent to doom - @lajbel