用包管理器安装后需要 Vite 或 ESBuild 等打包器才能使用;TypeScript 全局类型需额外引入 kaplay/global 或在 tsconfig 中配置 types;create-kaplay 启动后默认访问 http://localhost:5173

kaplay
kaplayjsMIT活跃维护用 JavaScript/TypeScript 写 2D 游戏的轻量库,API 设计得像在玩游戏一样顺手,适合快速做小游戏原型和独立作品。
🦖 A JavaScript/TypeScript Game Library that feels like a game.
npx create-kaplay my-game
npm install kaplayREADME 未说明;作为浏览器端 2D 库,运行在支持 JavaScript 的浏览器环境,未提及显存或 GPU 要求
用组件拼装游戏对象的 JavaScript/TypeScript 2D 游戏库,写起来像在玩游戏
用 JavaScript/TypeScript 写 2D 游戏的轻量库,API 设计得像在玩游戏一样顺手,适合快速做小游戏原型和独立作品。
核心能力
- 用 rect、pos、area、body 等组件组合出游戏对象
- 通过 onCollide、onUpdate、onKeyDown 声明式绑定行为
- 内置 loadSprite 加载图片与 sprite 渲染
- 提供 KAPLAYGROUND 在线编辑器与 90+ 示例
- 支持 CDN 直接引入或 npm 等包管理器安装
容易踩的坑
- 4000.0.0-alpha.27.1最新正式版2026-05-12 · 2026-05-12 21:17 UTC
[4000.0.0-alpha.27.1] - 2026-05-12
Added
- Added a
repack: falseoption toloadSpite()and a repack parameter toloadSpriteAtlas(), 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 eitherloadSprite()orloadSpriteAtlas()) 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
- fix: only sync texture when needed by @dragoncoder047 in https://github.com/kaplayjs/kaplay/pull/1058
- feat: give sprite frame explicitly in pixels by @dragoncoder047 in https://github.com/kaplayjs/kaplay/pull/1061
- chore(deps-dev): bump vite from 7.1.12 to 7.3.2 by @dependabot[bot] in https://github.com/kaplayjs/kaplay/pull/1065
- examples: update flappy and pause menu by @imaginarny in https://github.com/kaplayjs/kaplay/pull/1073
- fix: broadphase event duplication on scene switch by @imaginarny in https://github.com/kaplayjs/kaplay/pull/1074
- fix: broadphase objects with stay cleared on scene switch by @imaginarny in https://github.com/kaplayjs/kaplay/pull/1077
- feat: add back no repack option, also minor speedup of repacker by @dragoncoder047 in https://github.com/kaplayjs/kaplay/pull/1062
- fix: add padding around edges of spritesheet to prevent stretch if uv ends up out of bounds by @dragoncoder047 in https://github.com/kaplayjs/kaplay/pull/1076
- fix: changed font scaling to fix loadSpriteAsFont by @dragoncoder047 in https://github.com/kaplayjs/kaplay/pull/1063
- fix: refresh packer for direct referenced sprites resolving by @dragoncoder047 in https://github.com/kaplayjs/kaplay/pull/1089
Full Changelog: https://github.com/kaplayjs/kaplay/compare/4000.0.0-alpha.27...4000.0.0-alpha.27.1
- Added a
- 4000.0.0-alpha.272026-03-19 · 2026-03-19 03:59 UTC
查看更新说明
[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
filteroptions, which override the globaltexFilterandfontFilteroptions (#1050) - @dragoncoder047 - Added global
fontFilteroption to control default font rasterization filter (#1050, #1052) - @dragoncoder047 - Added
tileModeoption 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
localeoption inDrawTextOpt(#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()andpiecewiseCatmullRom()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.texdoesn't exist, andSpriteData.framesis a list ofFrames instead of a list ofQuads. AFramecontainstexandq(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.onXXXXandscene.onXXXX()(#977) - @lajbel - Fixed input events attached to paused ancestors not being paused (#1009) - @amyspark-ng, @dragoncoder047
- Fixed type
UniformValueunion not includingTexture, 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
UniformValueunion not includingTexture, a valid option (#1018) - @dragoncoder047 - Fixed event crash when using
onLoador other events that doesn't return an EventController, and then usinggo()(#1024) - @lajbel, credits to @dragoncoder047 - Fixed the unexpected behavior of not preserving the aspect ratio in `drawSpri
- Added
- 4000.0.0-alpha.262026-01-13 · 2026-01-13 01:47 UTC
查看更新说明
[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.isVisuallyCollidingto 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 takingdebug.timeScaleinto 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
- feat: tween clone rgb/vec2 parameters by @lajbel in https://github.com/kaplayjs/kaplay/pull/982
- feat: Introduces sensors by @mflerackers in https://github.com/kaplayjs/kaplay/pull/979
- fix: return true for hasAnim() even if anim is just frame 0 by @dragoncoder047 in https://github.com/kaplayjs/kaplay/pull/985
- refactor: Convert hover handler to a system by @mflerackers in https://github.com/kaplayjs/kaplay/pull/986
- chore: build in typescript, improve contributing.md by @lajbel in https://github.com/kaplayjs/kaplay/pull/990
- fix: Should fix click by @mflerackers in https://github.com/kaplayjs/kaplay/pull/993
- feat: Added isVisuallyColliding to area by @mflerackers in https://github.com/kaplayjs/kaplay/pull/997
- feat: Added floodfill (used in various puzzle games and puzzle bobble like games) by @mflerackers in https://github.com/kaplayjs/kaplay/pull/998
- fix: input events now retain their paused state by @dragoncoder047 in https://github.com/kaplayjs/kaplay/pull/988
- fix: fix for loop syntax to use 'of' instead of 'in' by @benhuangbmj in https://github.com/kaplayjs/kaplay/pull/991
- fix(timer): use timeScale in loop/wait/tween by @Stanko in https://github.com/kaplayjs/kaplay/pull/1007
New Contributors
- @benhuangbmj made their first contribution in https://github.com/kaplayjs/kaplay/pull/991
- @Stanko made their first contribution in https://github.com/kaplayjs/kaplay/pull/1007
Full Changelog: https://github.com/kaplayjs/kaplay/compare/4000.0.0-alpha.25...4000.0.0-alpha.26
- Added
- 4000.0.0-alpha.252025-12-24 · 2025-12-24 01:32 UTC
查看更新说明
[4000.0.0-alpha.25] - 2025-12-23
Added
- Added the
fakeMouseMoveevent inFakeMouseComp, 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: https://github.com/kaplayjs/kaplay/compare/4000.0.0-alpha.24...4000.0.0-alpha.25
- Added the
- 4000.0.0-alpha.242025-12-12 · 2025-12-12 12:34 UTC
查看更新说明
[4000.0.0-alpha.24] - 2025-12-12
Added
- Added the
maxTimeStepandfixedUpdateModeoptions, as well assetFixedSpeed()for more granular control over fixed update and timing - @dragoncoder047 - Added parameterized formatting tags like
"[color=red]Red text![/color]"inCharTransformFuncfor more powerful text formatting options - @dragoncoder047 - Added
createRegularPolygon()andcreateStarPolygon()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
- Added the
用户点评
登录用户的真实使用记录,可按版本和硬件筛选还没有人点评。第一个说出真实体验的人最有价值。
🎮 KAPLAY.js — A JavaScript & TypeScript Game Library
KAPLAY is the fun-first, 2D game library for JavaScript and TypeScript. It’s made to feel like a game while you're making games. Simple. Fast. Powerful.
✨ Whether you’re a beginner or an experienced dev, KAPLAY comes with its own web-based editor — the KAPLAYGROUND — so you can try code instantly, and learn with more than 90 examples!
🎲 Quick Overview
// Start a game
kaplay({
background: "#6d80fa",
});
// Load an image
loadSprite("bean", "https://play.kaplayjs.com/bean.png");
// Add a sprite to the scene
add([
sprite("bean"), // it renders as a sprite
]);
Game objects are composed from simple, powerful components:
// Add a Game Obj to the scene from a list of components
const player = add([
rect(40, 40), // it renders as a rectangle
pos(100, 200), // it has a position (coordinates)
area(), // it has a collider
body(), // it is a physical body which will respond to physics
health(8), // it has 8 health points
// Give it tags for easier group behaviors
"friendly",
// Give plain objects fields for associated data
{
dir: vec2(-1, 0),
dead: false,
speed: 240,
},
]);
Blocky imperative syntax for describing behaviors
// .onCollide() comes from "area" component
player.onCollide("enemy", () => {
// .hp comes from "health" component
player.hp--;
});
// check fall death
player.onUpdate(() => {
if (player.pos.y >= height()) {
destroy(player);
}
});
// All objects with tag "enemy" will move to the left
onUpdate("enemy", (enemy) => {
enemy.move(-400, 0);
});
// move up 100 pixels per second every frame when "w" key is held down
onKeyDown("w", () => {
player.move(0, 100);
});
🖥️ Installation
🚀 Using create-kaplay
The fastest way to get started:
npx create-kaplay my-game
Then open http://localhost:5173 and edit src/game.js.
📦 Install with package manager
npm install kaplay
yarn add kaplay
pnpm add kaplay
bun add kaplay
You will need a bundler like Vite or ESBuild to use KAPLAY in your project. Learn how to setup ESbuild here.
🌐 Use in Browser
Include via CDN:
<script src="https://unpkg.com/[email protected]/dist/kaplay.js"></script>
📜 TypeScript Global Types
If you're using TypeScript, you used create-kaplay or installed with a
package manager and you want global types, you can load them using the
following directive:
import "kaplay/global";
vec2(10, 10); // typed!
But it's recommended to use tsconfig.json to include the types:
{
"compilerOptions": {
"types": ["./node_modules/kaplay/dist/declaration/global.d.ts"]
}
}
[!WARNING]
If you are publishing a game (and not testing/learning) maybe you don't want to use globals, see why.
You can also use all KAPLAY source types importing them:
import type { TextCompOpt } from "kaplay"
import type * as KA from "kaplay" // if you prefer a namespace-like import
interface MyTextCompOpt extends KA.TextCompOpt {
fallback: string;
}
📚 Resources
📖 Docs
📺 Tutorials
- 🎥 KAPLAY Library Crash Course by JSLegend ⚔️
- 📖 Learn JavaScript basics and KAPLAY to make games quickly
💬 Community
🎮 Games
Collections of games made with KAPLAY, selected by KAPLAY:
🙌 Credits
KAPLAY is an open-source project, maintained by the KAPLAY Team and core contributors and with the support of many other amazing contributors.
🏆 Recognitions
- Thanks to mulfok for the awesome mulfok32 color palette, used in KAPLAY sprites and art
- Thanks to Pixabay for the great
burp sound, used in
burp()function - Thanks to Kenney for all used assets for examples
- Thanks to abrudz for the amazing APL386 font
- Thanks to Polyducks for the amazing kitchen sink font font
- Thanks to 0x72 for the amazing Dungeon Tileset
- Thanks to @Minamotion for the
tinycharacter sprite used in some of the tests