Kaboom is a Javascript game programming library that helps you make games fast and fun.
// start the game

// load a default sprite

// add character to screen, from a list of components
const player = add([
    sprite("bean"),  // renders as a sprite
    pos(120, 80),    // position in world
    area(),          // has a collider
    body(),          // responds to physics and gravity

// jump when player presses "space" key
keyPress("space", () => {
    // .jump() is provided by the body() component

Play with it yourself or check out the examples in the Playground!

kaboom(conf?: KaboomConf)
Initialize kaboom context. The starting point of all kaboom games.
// this will create a blank canvas and import all kaboom functions to global

// init with some configs (check out #KaboomConf for full config list)
// create a game with custom dimension, but stretch to fit container, keeping aspect ratio, with a clear color
    width: 320,
    height: 240,
    stretch: true,
    letterbox: true,
    font: "sinko",
    background: [ 0, 0, 255, ],

// all kaboom functions are imported to global automatically

// can also prevent kaboom from importing all functions to global and use a context handle
const k = kaboom({ global: false });

Game Obj
add(comps: CompList<T>) => Character<T>
Create and add a game obj to the scene, from a list of components or tags. The added and returned game obj will contain all methods from each component.
// let's add our player character to the screen
// we use a list of components to define who they are and how they actually work
const player = add([
    // it renders as a sprite
    // it has a position
    pos(100, 200),
    // it has a collider
    // it is a physical body which will respond to physics
    // you can easily make custom components to encapsulate reusable logics
    // give it tags for controlling group behaviors
    // plain objects fields are directly assigned to the game obj
        dir: vec2(-1, 0),
        dead: false,
        speed: 240,

// .jump is provided by body()

// .moveTo is provided by pos()
player.moveTo(100, 200);

// run something every frame
// player will constantly move towards player.dir, at player.speed per second
player.action(() => {

// .collides is provided by area()
player.collides("tree", () => {

// run this for all game objs with tag "friendly"
action("friendly", (friend) => {
    // .hurt is provided by health()

// check out #Character for stuff that exists for all game objects, independent of its components.
get(tag?: Tag) => Character[]
Get a list of all game objs with certain tag.
// get a list of all game objs with tag "bomb"
const allBombs = get("bomb");

// without args returns all current objs in the game
const allObjs = get();
every(t: Tag, cb: (obj: Character) => T)
Run callback on every game obj with certain tag.
// how destroyAll() works
every("fruit", destroy);

// without tag it runs through every game obj
every((obj) => {});
every(cb: (obj: Character) => T)
revery(t: Tag, cb: (obj: Character) => T)
Run callback on every game obj with certain tag in reverse order.
revery(cb: (obj: Character) => T)
readd(obj: Character) => Character
Remove and re-add the game obj.
// mainly useful when you want to make something to draw on top
destroy(obj: Character)
Remove the game obj.
// every time froggy collides with anything with tag "fruit", remove it
froggy.collides("fruit", (fruit) => {
destroyAll(tag: Tag)
Remove all game objs with certain tag.
// destroy all objects with tag "bomb" when you click one
clicks("bomb", () => {
pos(x: number, y: number) => PosComp
// this game obj will draw the "froggy" sprite at (100, 200)
    pos(100, 200),
pos(xy: number) => PosComp
pos(p: Vec2) => PosComp
pos() => PosComp
scale(x: number, y: number) => ScaleComp
scale(xy: number) => ScaleComp
scale(s: Vec2) => ScaleComp
scale() => ScaleComp
rotate(a: number) => RotateComp
Rotation (in degrees). (This doesn't work with the area() collider yet)
color(r: number, g: number, b: number) => ColorComp
Sets color (rgb 0-255).
// blue frog
    color(0, 0, 255)
color(c: Color) => ColorComp
color() => ColorComp
opacity(o?: number) => OpacityComp
Sets opacity (0.0 - 1.0).
sprite(spr: string | SpriteData, conf?: SpriteCompConf) => SpriteComp
Render as a sprite.
// minimal setup

// with config
const froggy = add([
    sprite("froggy", {
        // start with animation "idle"
        anim: "idle",

// play / stop an anim"jump");

// manually setting a frame
froggy.frame = 3;
text(txt: string, conf?: TextCompConf) => TextComp
Render as text.
// a simple score counter
const score = add([
    text("Score: 0"),
    pos(24, 24),
    { value: 0 },

player.collides("coin", () => {
    score.value += 1;
    score.text = "Score:" + score.value;

// set to another default font on start up ("sink" is a pixel font provided by default)
kaboom({ font: "sink" });
rect(w: number, h: number) => RectComp
Render as a rectangle.
// i don't know, could be an obstacle or something
    pos(80, 120),
    rect(20, 40),
circle(radius: number) => CircleComp
Render as a circle.
    pos(80, 120),
uvquad(w: number, h: number) => UVQuadComp
Render as a UV quad.
    uvquad(width(), height()),
area(conf?: AreaCompConf) => AreaComp
Collider. Will calculate from rendered comps (e.g. from sprite, text, rect) if no params given.
    // without args it'll auto calculate from the data sprite comp provides

    // scale to 0.6 of the sprite size
    area({ scale: 0.6 }),
    // we want the scale to be calculated from the center

// define custom area with topleft and botright point
const player = add([
    area({ width: 20, height: 40. }),

// die if player collides with another game obj with tag "tree"
player.collides("tree", () => {

// check for collision manually every frame instead of registering an event
player.action(() => {
    if (player.isColliding(bomb)) {
        score += 1;
origin(o: Origin | Vec2) => OriginComp
Origin point for render (default "topleft").
// set origin to "center" so it'll rotate from center
    rect(40, 10),
layer(l: string) => LayerComp
Which layer this object belongs to.
z(z: number) => ZComp
Determines the draw order for objects on the same layer. Object will be drawn on top if z value is bigger.
outline(width?: number, color?: Color) => OutlineComp
Give obj an outline.
body(conf?: BodyCompConf) => BodyComp
Physical body that responds to gravity. Requires "area" and "pos" comp. This also makes the object "solid".
// froggy jumpy
const froggy = add([
    // body() requires "pos" and "area" component

// when froggy is grounded, press space to jump
// check out BodyComp for more methods
keyPress("space", () => {
    if (froggy.grounded()) {

// a custom event provided by "body"
froggy.on("ground", () => {
    debug.log("oh no!");
solid() => SolidComp
Make other objects cannot move pass. Requires "area" comp.
move(direction: number | Vec2, speed: number) => MoveComp
Move towards a direction infinitely, and destroys when it leaves game view. Requires "pos" comp.
// enemy throwing feces at player
const projectile = add([
    move(player.pos.angle(enemy.pos), 1200),
cleanup(time?: number) => CleanupComp
destroy() the character if it's out of screen. Optionally specify the amount of time it has to be off-screen before removal.
// remove this 3 seconds after it leaves screen
    pos(80, 80),
    move(LEFT, 120),
follow(obj: Character | null, offset?: Vec2) => FollowComp
Follow another game obj's position.
shader(id: string) => ShaderComp
Custom shader.
timer(n?: number, action?: () => void) => TimerComp
Run certain action after some time.
fixed() => FixedComp
Unaffected by camera.
// this score counter better be fixed on top left and not affected by camera
const score = add([
    pos(12, 12),
stay() => StayComp
Don't get destroyed on scene switch.
player.collides("bomb", () => {
    // spawn an explosion and switch scene, but don't destroy the explosion game obj on scene switch
        sprite("explosion", { anim: "burst", }),
    go("lose", score);
health(hp: number) => HealthComp
Handles health related logic and events.
const player = add([

player.collides("bad", (bad) => {
player.collides("apple", () => {

player.on("hurt", () => {

// triggers when hp reaches 0
player.on("death", () => {
lifespan(time: number, conf?: LifespanCompConf) => LifespanComp
Destroy the game obj after certain amount of time
// spawn an explosion, destroy after 2 seconds and the switch scene
    sprite("explosion", { anim: "burst", }),
    lifespan(2, () => go("lose")),
on(event: string, tag: Tag, cb: (obj: Character, args: ...) => void) => EventCanceller
Register an event on all game objs with certain tag.
// a custom event defined by body() comp
// every time an obj with tag "bomb" hits the floor, destroy it and addKaboom()
on("ground", "bomb", (bomb) => {
action(tag: Tag, cb: (obj: Character) => void) => EventCanceller
Register "update" event (runs every frame) on all game objs with certain tag.
// move every "tree" 120 pixels per second to the left, destroy it when it leaves screen
// there'll be nothing to run if there's no "tree" obj in the scene
action("tree", (tree) => {
    tree.move(-120, 0);
    if (tree.pos.x < 0) {

// without tags it just runs it every frame
action(() => {
action(cb: () => void) => EventCanceller
render(tag: Tag, cb: (obj: Character) => void) => EventCanceller
Register "draw" event (runs every frame) on all game objs with certain tag. (This is the same as `action()`, but all draw events are run after updates)
render(cb: () => void) => EventCanceller
collides(t1: Tag, t2: Tag, cb: (a: Character, b: Character, side?: RectSide) => void) => EventCanceller
Register event when 2 game objs with certain tags collides. This function spins off an action() when called, please put it at root level and never inside another action().
collides("sun", "earth", () => {
clicks(tag: Tag, cb: (a: Character) => void) => EventCanceller
Register event when game objs with certain tags are clicked. This function spins off an action() when called, please put it at root level and never inside another action().
hovers(tag: Tag, cb: (a: Character) => void) => EventCanceller
Register event when game objs with certain tags are hovered. This function spins off an action() when called, please put it at root level and never inside another action().
mousePos() => Vec2
Get current mouse position (without camera transform).
mouseWorldPos() => Vec2
Get current mouse position (after camera transform)
mouseDeltaPos() => Vec2
How much mouse moved last frame.
keyDown(k: Key | Key[], cb: () => void) => EventCanceller
Registers an event that runs every frame when a key is down.
// move left by SPEED pixels per frame every frame when "left" is being held down
keyDown("left", () => {
    froggy.move(-SPEED, 0);
keyPress(k: Key | Key[], cb: () => void) => EventCanceller
Registers an event that runs when user presses certain key.
// .jump() once when "space" is just being pressed
keyPress("space", () => {
keyPress(cb: () => void) => EventCanceller
keyPressRep(k: Key | Key[], cb: () => void) => EventCanceller
Registers an event that runs when user presses certain key (also fires repeatedly when they key is held).
// delete last character when "backspace" is being pressed and held
keyPressRep("backspace", () => {
    input.text = input.text.substring(0, input.text.length - 1);
keyPressRep(cb: () => void) => EventCanceller
keyRelease(k: Key | Key[], cb: () => void) => EventCanceller
Registers an event that runs when user releases certain key.
keyRelease(cb: () => void) => EventCanceller
charInput(cb: (ch: string) => void) => EventCanceller
Registers an event that runs when user inputs text.
// export type into input
charInput((ch) => {
    input.text += ch;
mouseDown(cb: (pos: Vec2) => void) => EventCanceller
Registers an event that runs every frame when mouse button is down.
mouseClick(cb: (pos: Vec2) => void) => EventCanceller
Registers an event that runs when user clicks mouse.
mouseRelease(cb: (pos: Vec2) => void) => EventCanceller
Registers an event that runs when user releases mouse.
mouseMove(cb: (pos: Vec2) => void) => EventCanceller
Registers an event that runs whenever user move the mouse.
touchStart(cb: (id: TouchID, pos: Vec2) => void) => EventCanceller
Registers an event that runs when a touch starts.
touchMove(cb: (id: TouchID, pos: Vec2) => void) => EventCanceller
Registers an event that runs whenever touch moves.
touchEnd(cb: (id: TouchID, pos: Vec2) => void) => EventCanceller
Registers an event that runs when a touch ends.
keyIsDown(k: Key) => boolean
If certain key is currently down.
// almost equivalent to the keyPress() example above
action(() => {
    if (keyIsDown("left")) {
        froggy.move(-SPEED, 0);
keyIsPressed(k?: Key) => boolean
If certain key is just pressed last frame.
keyIsPressedRep(k?: Key) => boolean
If certain key is just pressed last frame (accepts help down repeatedly).
keyIsReleased(k?: Key) => boolean
If certain key is just released last frame.
mouseIsDown() => boolean
If certain mouse is currently down.
mouseIsClicked() => boolean
If mouse is just clicked last frame.
mouseIsReleased() => boolean
If mouse is just released last frame.
mouseIsMoved() => boolean
If mouse moved last frame.
loadRoot(path?: string) => string
Sets the root for all subsequent resource urls.
loadSprite("froggy", "sprites/froggy.png"); // will resolve to ""
loadSprite(id: string | null, src: SpriteLoadSrc, conf?: SpriteLoadConf) => Promise<SpriteData>
Load a sprite into asset manager, with name and resource url and optional config.
// due to browser policies you'll need a static file server to load local files
loadSprite("froggy", "froggy.png");
loadSprite("apple", "");

// slice a spritesheet and add anims manually
loadSprite("froggy", "froggy.png", {
    sliceX: 4,
    sliceY: 1,
    anims: {
        run: {
            from: 0,
            to: 3,
        jump: {
            from: 3,
            to: 3,
loadSpriteAtlas(src: SpriteLoadSrc, data: SpriteAtlasData) => Promise<Record<string, SpriteData>>
Load sprites from a sprite atlas.
loadSpriteAtlas("sprites/dungeon.png", {
    "hero": {
        x: 128,
        y: 68,
        width: 144,
        height: 28,
        sliceX: 9,
        anims: {
            idle: { from: 0, to: 3 },
            run: { from: 4, to: 7 },
            hit: { from: 8, to: 8 },

const player = add([

// or load from json file, see SpriteAtlasData export type for format spec
loadSpriteAtlas("sprites/dungeon.png", "sprites/dungeon.json");
loadSpriteAtlas(src: SpriteLoadSrc, url: string) => Promise<Record<string, SpriteData>>
loadAseprite(name: string | null, imgSrc: SpriteLoadSrc, jsonSrc: string) => Promise<SpriteData>
Load a sprite with aseprite spritesheet json.
loadAseprite("car", "sprites/car.png", "sprites/car.json");
loadPedit(name: string, src: string) => Promise<SpriteData>
loadBean(name?: string) => Promise<SpriteData>
Load default sprite "bean".

// use it right away
loadSound(id: string, src: string) => Promise<SoundData>
Load a sound into asset manager, with name and resource url.
loadSound("shoot", "horse.ogg");
loadSound("shoot", "");
loadFont(id: string, src: string, gridWidth: number, gridHeight: number, conf?: FontLoadConf) => Promise<FontData>
Load a bitmap font into asset manager, with name and resource url and infomation on the layout of the bitmap.
// load a bitmap font called "04b03", with bitmap "fonts/04b03.png"
// each character on bitmap has a size of (6, 8), and contains default ASCII_CHARS
loadFont("04b03", "fonts/04b03.png", 6, 8);

// load a font with custom characters
loadFont("cp437", "cp437.png", 6, 8, "☺☻♥♦♣♠");
loadShader(name: string, vert?: string, frag?: string, isUrl?: boolean) => Promise<ShaderData>
Load a shader into asset manager with vertex and fragment code / file url.
// load only a fragment shader from URL
loadShader("outline", null, "/shaders/outline.glsl", true);

// default shaders and custom shader format
    `vec4 vert(vec3 pos, vec2 uv, vec4 color) {
    // predefined functions to get the default value by kaboom
    return def_vert();
`vec4 frag(vec3 pos, vec2 uv, vec4 color, sampler2D tex) {
    // turn everything blue-ish
    return def_frag() * vec4(0, 0, 1, 1);
}`, true);
load(l: Promise<T>)
Add a new loader to wait for before starting the game.
load(new Promise((resolve, reject) => {
    // anything you want to do that stalls the game in loading state
width() => number
Get the width of game.
height() => number
Get the height of game.
center() => Vec2
Get the center point of view.
// add froggy to the center of the screen
    // ...
dt() => number
Get the delta time since last frame.
// rotate froggy 100 deg per second
froggy.action(() => {
    froggy.angle += 100 * dt();
time() => number
Get the total time since beginning.
screenshot() => string
Take a screenshot and get the dataurl of the image.
focused() => boolean
If the game canvas is currently focused.
Focus on the game canvas.
ready(cb: () => void)
Run something when assets finished loading.
const froggy = add([
    // ...

// certain assets related data are only available when the game finishes loading
ready(() => {
isTouch() => boolean
Is currently on a touch screen device.
shake(intensity: number)
Camera shake.
// shake intensively when froggy collides with a "bomb"
froggy.collides("bomb", () => {
camPos(pos: Vec2) => Vec2
Get / set camera position.
// camera follows player
player.action(() => {
camScale(scale: Vec2) => Vec2
Get / set camera scale.
camRot(angle: number) => number
Get / set camera rotation.
toScreen(p: Vec2) => Vec2
Transform a point from world position to screen position.
toWorld(p: Vec2) => Vec2
Transform a point from screen position to world position.
gravity(g: number) => number
Get / set gravity.
layers(list: string[], def?: string)
Define layers (the last one will be on top).
// defining 3 layers, "ui" will be drawn on top most, with default layer being "game"
], "game");

// use layer() comp to define which layer an obj belongs to

// without layer() comp it'll fall back to default layer, which is "game"
loop(t: number, cb: () => void) => EventCanceller
Run the callback every n seconds.
// spawn a bomb at random position every frame
loop(1, () => {
        pos(rand(0, width()), rand(0, height())),
wait(n: number, cb?: () => void) => Promise<void>
Run the callback after n seconds.
cursor(c?: Cursor) => Cursor
Get / set the cursor (css). Cursor will be reset to "default" every frame so use this in an per-frame action.
hovers("clickable", (c) => {
regCursor(c: string, draw: string | ((mpos: Vec2) => void))
Load a cursor from a sprite, or custom drawing function.
loadSprite("froggy", "sprites/froggy.png");

// use sprite as cursor
regCursor("default", "froggy");
regCursor("pointer", "apple");
fullscreen(f?: boolean) => boolean
Enter / exit fullscreen mode.
// toggle fullscreen mode on "f"
keyPress("f", (c) => {
play(id: string, conf?: AudioPlayConf) => AudioPlay
Play a piece of audio, returns a handle to control.
// play a one off sound

// play a looping soundtrack (check out AudioPlayConf for more configs)
const music = play("OverworldlyFoe", {
    volume: 0.8,
    loop: true

// using the handle to control (check out AudioPlay for more controls / info)
burp(conf?: AudioPlayConf) => AudioPlay
volume(v?: number) => number
Sets global volume.
// makes everything quieter
audioCtx: AudioContext
Get the underlying browser AudioContext.
rand() => number
Get a random number (with optional bounds).
// a random number between 0 - 1

// a random number between 0 - 8

// a random number between 50 - 100
rand(50, 100);

// a random vec2 between vec2(0) and vec2(100)
rand(vec2(0), vec2(100));

// spawn something on the right side of the screen but with random y value within screen height
    pos(width(), rand(0, height())),
rand(n: T) => T
rand(a: T, b: T) => T
randi() => number
rand() but integer only.
randi(n: T) => T
randi(a: T, b: T) => T
randSeed(seed?: number) => number
Get / set the random number generator seed.
vec2(x: number, y: number) => Vec2
Make a 2d vector.
// { x: 0, y: 0 }

// { x: 10, y: 10 }

// { x: 100, y: 80 }
const pos = vec2(100, 80);

// move to 150 degrees direction with by length 10
pos = pos.add(dir(150).scale(10));
vec2(p: Vec2) => Vec2
vec2(xy: number) => Vec2
vec2() => Vec2
rgb(r: number, g: number, b: number) => Color
RGB color (0 - 255).
quad(x: number, y: number, w: number, h: number) => Quad
Rectangle area (0.0 - 1.0).
choose(lst: T[]) => T
Choose a random item from a list.
// decide the best fruit randomly
const bestFruit = choose(["apple", "banana", "pear", "watermelon"]);
chance(p: number) => boolean
rand(1) <= p
// every frame all objs with tag "unlucky" have 50% chance die
action("unlucky", (o) => {
    if (chance(0.5)) {
lerp(from: number, to: number, t: number) => number
Linear interpolation.
map(v: number, l1: number, h1: number, l2: number, h2: number) => number
Map a value from one range to another range.
mapc(v: number, l1: number, h1: number, l2: number, h2: number) => number
Map a value from one range to another range, and clamp to the dest range.
dir(deg: number) => Vec2
Get directional vector from an angle
// move towards 80 deg direction at SPEED
player.action(() => {
wave(lo: number, hi: number, t: number, func?: (x: number) => number) => number
Interpolate between 2 values (default using Math.sin motion).
// bounce color between 2 values as time goes on
action("colorful", (c) => {
    c.color.r = wave(0, 255, time());
    c.color.g = wave(0, 255, time() + 1);
    c.color.b = wave(0, 255, time() + 2);
deg2rad(deg: number) => number
Convert degrees to radians.
rad2deg(rad: number) => number
Convert radians to degrees.
rng(seed: number) => RNG
Make a new random number generator.
testLineLine(l1: Line, l2: Line) => Vec2 | null
Check if 2 lines intersects, if yes returns the intersection point.
testRectRect(r1: Rect, r2: Rect) => boolean
Check if 2 rectangle overlaps.
testRectLine(r: Rect, l: Line) => boolean
Check if a line and a rectangle overlaps.
testRectPt(r: Rect, pt: Vec2) => boolean
Check if a point is inside a rectangle.
scene(id: SceneID, def: SceneDef)
Define a scene.
go(id: SceneID, args: ...)
Go to a scene, passing all rest args to scene callback.
addLevel(map: string[], conf: LevelConf) => Level
Construct a level based on symbols.
// example from demo/platformer.js
    "                          $",
    "                          $",
    "                          $",
    "           $$         =   $",
    "  %      ====         =   $",
    "                      =   $",
    "                      =    ",
    "       ^^      = >    =    ",
], {
    // define the size of each block
    width: 32,
    height: 32,
    // define what each symbol means, by a function returning a comp list (what you'll pass to add())
    "=": () => [
    "$": () => [
        pos(0, -9),
    "^": () => [
getData(key: string, def?: T) => T
Get data from local storage, if not present can set to a default value.
setData(key: string, data: any)
Set data from local storage.
drawSprite(conf: DrawSpriteConf)
Draw a sprite.
drawText(conf: DrawTextConf)
Draw a piece of text.
drawRect(conf: DrawRectConf)
Draw a rectangle.
drawLine(conf: DrawLineConf)
Draw a line.
drawLines(conf: DrawLinesConf)
Draw lines.
drawTri(conf: DrawTriConf)
Draw a triangle.
drawCircle(conf: DrawCircleConf)
Draw a circle.
drawEllipse(conf: DrawEllipseConf)
Draw an ellipse.
drawPoly(conf: DrawPolyConf)
Draw a convex polygon from a list of vertices.
drawUVQuad(conf: DrawUVQuadConf)
Draw a rectangle with UV data.
Push current transform matrix to the transform stack.

// these transforms will affect every render until popTransform()
pushTranslate(120, 200);
pushRotate(time() * 120);

drawCircle(vec2(0), 120);

// restore the transformation stack to when last pushed
Pop the topmost transform matrix from the transform stack.
pushTranslate(x: number, y: number)
pushTranslate(p: Vec2)
pushScale(x: number, y: number)
pushScale(s: number)
pushScale(s: Vec2)
pushRotate(angle: number)
plug(plugin: KaboomPlugin<T>)
Import a plugin.
debug: Debug
Debug stuff.
// pause the whole game
debug.paused = true;

// enter inspect mode
debug.inspect = true;

// in debug mode (on by default, unless disabled by `debug: false` in KaboomConf), some keys are binded to toggle certain debug features:
// F1: toggle debug.inspect
// F2: call debug.clearLog()
// F8: toggle debug.pause
// F7: decrease debug.timeScale
// F9: increase debug.timeScale
// F10: call debug.stepFrame()
All chars in ASCII.
CP437_CHARS: string
All chars in CP437.
LEFT: Vec2
Left directional vector vec2(-1, 0).
UP: Vec2
Up directional vector vec2(0, -1).
DOWN: Vec2
Down directional vector vec2(0, 1).
canvas: HTMLCanvasElement
The canvas DOM kaboom is currently using.