On This Page

Docs

These are the complete docs for building games with Kube.

If you're a beginner, you probably don't need to read this. The chat in the editor will handle things for you.

The first few sections define all the objects that can exist in any game. The next sections discuss Scripting & UI. For scripting information, refer to the sections Script and Functions & Services. The last section discusses Assets, Hats, and uploading files and meshes.

Core Objects

Ball

A ball in the world.

FieldType
material

Visual Material texture settings.

opacity

number

0-1.

color
glow

number

0-1. How much it shines in its own color whatever the light, and glows onto its surroundings, like neon. 0 is a normal object.

isAlwaysOnTop

boolean

Whether to render in front of everything even if visually blocked. This should almost never be used.

radius

number

location
movement
isWalkthrough

boolean

Whether this object can pass through other objects.

isPushable

boolean

Whether physics can move this object: gravity makes it fall and other objects push it. When false, it is totally unmovable.

density

number

Brick

A brick in the world.

FieldType
material

Visual Material texture settings.

opacity

number

0-1.

color
glow

number

0-1. How much it shines in its own color whatever the light, and glows onto its surroundings, like neon. 0 is a normal object.

isAlwaysOnTop

boolean

Whether to render in front of everything even if visually blocked. This should almost never be used.

size
location
movement
isWalkthrough

boolean

Whether this object can pass through other objects.

isPushable

boolean

Whether physics can move this object: gravity makes it fall and other objects push it. When false, it is totally unmovable.

density

number

Cylinder

A cylinder in the world. Its symmetry axis is local Z.

FieldType
material

Visual Material texture settings.

opacity

number

0-1.

color
glow

number

0-1. How much it shines in its own color whatever the light, and glows onto its surroundings, like neon. 0 is a normal object.

isAlwaysOnTop

boolean

Whether to render in front of everything even if visually blocked. This should almost never be used.

radius

number

length

number

location
movement
isWalkthrough

boolean

Whether this object can pass through other objects.

isPushable

boolean

Whether physics can move this object: gravity makes it fall and other objects push it. When false, it is totally unmovable.

density

number

RigidGroup

A RigidGroup is a bunch of objects that move together. All Ball, Brick, and Cylinder child descendants of this are glued together and will move as one rigid body.

If you want to add animations, you must set followSkeletonId, use a SkAnimator to animate it, and add SkAttachment objects to glue objects to the SkJoints. There are some AI tools to automate this.

FieldType
followSkeletonId

ObjectId | null

The objectId of the Skeleton that this rigid group will follow.

location
movement

Group

Use this as a Folder. There is no special behavior, it is simply for organization.

Mesh

A mesh in the world.

To animate this mesh visually, you must set followSkeletonId and use a SkAnimator to animate it.

FieldType
scale
meshDataId
followSkeletonId

ObjectId | null

The objectId of the Skeleton that this mesh will visually follow.

physicalObjectId

ObjectId | null

The objectId of the Ball, Brick, Cylinder, or RigidGroup that the mesh will use as a collider and visually follow.

material

Visual Material texture settings.

opacity

number

0-1.

color
glow

number

0-1. How much it shines in its own color whatever the light, and glows onto its surroundings, like neon. 0 is a normal object.

isAlwaysOnTop

boolean

Whether to render in front of everything even if visually blocked. This should almost never be used.

Label

This is how you render text. All text must be attached to an object.

All measurements are in terms of world units, ie 1 block tall.

FieldType
text

string

Emojis are not allowed in label text.

physicalObjectId

ObjectId | null

Required. The objectId of the Ball, Brick, Cylinder, or RigidGroup the label follows.

lineHeight

number

maxWidth

number | null

Null means it only breaks on newlines.

horizontalAlign

'left' | 'center' | 'right'

Only applies when there are multiple rows.

backgroundColor

Color3 | null

Null means transparent bg.

backgroundOpacity

number

offsetFromObject

Additional offset from the object.

rotationFromObject

Additional rotation from the object.

billboardMode

'none' | 'full'

Whether to swivel to face the camera.

isConstantSize

boolean

If true, the label does not shrink with distance, and remains the size as if viewed from 10 units away.

opacity

number

0-1.

color
glow

number

0-1. How much it shines in its own color whatever the light, and glows onto its surroundings, like neon. 0 is a normal object.

isAlwaysOnTop

boolean

Whether to render in front of everything even if visually blocked. This should almost never be used.

LabelImage

A picture in the world. It follows an object and orients itself exactly like a Label.

All measurements are in terms of world units, ie 1 block tall.

FieldType
textureDataId
width

number

The picture stretches to fill width by height.

height

number

physicalObjectId

ObjectId | null

Required. The objectId of the Ball, Brick, Cylinder, or RigidGroup the image follows.

offsetFromObject

Additional offset from the object.

rotationFromObject

Additional rotation from the object. With none, it faces the object's +Z side.

billboardMode

'none' | 'full'

Whether to swivel to face the camera.

isConstantSize

boolean

If true, the image does not shrink with distance, and remains the size as if viewed from 10 units away.

opacity

number

0-1.

color
glow

number

0-1. How much it shines in its own color whatever the light, and glows onto its surroundings, like neon. 0 is a normal object.

isAlwaysOnTop

boolean

Whether to render in front of everything even if visually blocked. This should almost never be used.

Pivot

This lets you specify a center point that this object will rotate/scale relative to. The pivot moves, turns and scales with the object. Max one per object.

This only applies when using a PropertyAnimator on rotation, or one of the functions on objectService. It is ignored when manually setting rotation/size directly.

FieldType
physicalObjectId

ObjectId | null

offsetFromObject

Tween

This smoothly changes a value on an object. Typically you should create this with an animationService.tweenTo().

This is equivalent to a PropertyAnimator with 2 keyframes. It is an easier, cheaper way to dynamically move a property if you don't know the values you want beforehand.

  • All tween objects are automatically applied to their objects.
  • tweenTo() creates a tween for you and deletes it once it finishes.
  • They write the animated value into the real field every step.
FieldType
targetObjectId

ObjectId | null

propertyPath

string[]

Path to the number on the target object, for example ["location", "rotation", "y"].

fromValue

number

toValue

number

duration

number

In seconds.

interpolationStyle
clockState

Playing from when the tween started.

Animation Objects

SkAnimator

This animates a Skeleton.

You can have many animators on the same skeleton. They will be blended in order of decreasing priority until each item's weight reaches a total of 100. See also: AnimationService.

Start and stop an animation with .play() and .pause() on this animator's clock, and fade it in and out with .smoothlyChangeWeight().

FieldType
skeletonId

ObjectId | null

The Skeleton to animate.

skAnimationId

ObjectId | null

Using this SkAnimation.

clockId

ObjectId | null

The Clock that lets you pause/play it.

priority

number

0-10.

weight

number

0-100.

weightTransition

Skeleton

This is the root of a skeleton. Skeletons only apply to objects with the followSkeletonId field.

This should contain a child SkJoint for each joint in the skeleton. Hierarchies are allowed.

You can (1) animate it and (2) glue objects to it, using SkAnimation, SkAnimator, and SkAttachment.

SkAnimation

This is how you define an animation.

It should contain a keyframe SkKeyframe child for each timestep in your animation.

FieldType
duration

number

interpolationStyle

The interpolation style of the animation. Use Step for a stop-motion effect.

SkAttachment

Use this to glue an object in a RigidGroup to an SkJoint.

FieldType
objectId

ObjectId | null

The objectId that should follow the SkJoint.

jointName

string

offsetFromJoint

The offset the object will have relative to the joint even as the joint moves.

SkJoint

A child of Skeleton or SkJoint.

FieldType
restPose

This joint's resting location relative to its parent joint. You can change this to move joints around. SkKeyframePose is combined with this pose, so you can change the orientation of a joint during an animation.

SkKeyframe

A child of SkAnimation.

For each desired joint in this keyframe, it should contain a SkKeyframePose child.

FieldType
time

number

Time in seconds in the animation.

SkKeyframePose

A child of SkKeyframe.

The pose of an individual joint.

FieldType
jointName

string

localPose

This joint's location relative to its restPose. A zero position and rotation leave the joint at its rest pose.

PropertyAnimator

Use this to smoothly animate one property on any object.

You can have multiple of these at once. They blend independently given a targetObjectId and property, so you can animate multiple fields at once.

  • You can't use this to physically change subitems in a RigidGroup. Use a SkAnimator for that instead.
  • If you're using this to create a moving platform, be sure it isn't pushable, or else it purposefully won't work - too confusing.
  • It writes the animated value into the real field every step.
FieldType
targetObjectId

ObjectId | null

propertyPath

string[]

Path to the continuous field to animate, for example ["color", "r"], ["opacity"], or ["location", "position", "x"].

propertyAnimationId

ObjectId | null

The PropertyAnimation to play.

clockId

ObjectId | null

The Clock that lets you pause/play it.

priority

number

0-10.

weight

number

0-100.

weightTransition

PropertyAnimation

This defines the animation for a PropertyAnimator to play.

It should contain a keyframe PropertyKeyframe child for each timestep in your animation.

FieldType
duration

number

interpolationStyle

PropertyKeyframe

A child of PropertyAnimation.

FieldType
time

number

value

number

Other Objects

Clock

A clock is a helper object that says how far along the animation is in a SkAnimator, PropertyAnimator, or SoundPlayer.

FieldType
clockState

Whether it is paused or playing, and how far along it is.

shouldLoop

boolean

Whether it should loop when done.

speed

number

A multiplier onto the normal speed.

Material

An image or "texture" that is shown on an object. Albedo is the main texture. The other fields are optional PBR properties.

FieldType
albedoTextureDataId

DataId | null

This is the main texture for color. Four values (RGBA) per pixel.

metallicTextureDataId

DataId | null

Optional metallic information. One value per pixel.

roughnessTextureDataId

DataId | null

Optional roughness information. One value per pixel.

normalTextureDataId

DataId | null

Optional normal information. Three values (XYZ) per pixel.

alphaMode

Says how to handle the underlying object's transparency. Only relevant when albedoTextureDataId has transparency in it.

isDoubleSided

boolean

Render both sides of the surface, with back-face normals reversed for lighting.

Player

You can never create or delete a Player object yourself. It is automatically created and deleted when a player joins and leaves. It is important for you to set the head and character, or the player won't be able to see or move.

FieldType
username

string

userid

UserId | null

This can be null in rare cases (e.g., guest players when playtesting). Use the player object's id for a perfect identifer, not this.

headObjectId

ObjectId | null

The objectId of the Ball, Brick, Cylinder, or RigidGroup the player's camera will follow.

characterObjectId

ObjectId | null

The objectId of the Ball, Brick, Cylinder, or RigidGroup this player controls.

preferredColor

Color3 | null

The color this player picked on their profile, or null. Read when they join, and refreshed by game.playerService.refreshPlayerProfile().

equippedItemId

number | null

The catalog item this player is wearing on their profile, like a hat, or null. Read when they join, and refreshed by game.playerService.refreshPlayerProfile().

Script

Every Script is either a Server or Client script.

  • Client scripts run on every client.
  • Server scripts run on the server.
  • Some APIs are only exposed to one or the other; read the docs.
  • Deleting a Script stops it. Its listeners, timers, tick() loops and GUI stop, and its exports can no longer be called.

Import/Export:

  • It is valid to write import { functionName } from './scriptName'. Hiererarchies of object names are valid.
  • Script names, paths, & contents are only read once when the script is created.

Server/Client communication:

  • Write 'use server' at the top of a Server script to make all exports importable by Client scripts.
  • Write 'use client' at the top of a Client script to make all exports importable by Server scripts.
  • The first parameter to all client functions must be the player's object id (player.id).
  • Exports must be async functions.
  • Exports must be validly callable by the other side.
  • You should validate server function inputs and client function returns, as these are untrusted.

UI:

  • We expose React-like syntax to draw UI on the screen. Every Tailwind CSS v4 class works, including arbitrary values like w-[42%], and text defaults to black and 16px as on any web page.
  • You may only use <div>, <button>, text, arrays, null, className, onClick, onRightClick, onMouseDown, onMouseUp, and onHover primitive props.
  • Use <div> for layout and text. Pointer input on a div reaches both the div (its hover styles and onHover) and the game, unless the div has onClick, onRightClick, onMouseDown or onMouseUp.
  • Use <button> with onClick for clickable UI. A button, or a div with any of those four handlers, keeps presses to itself, so pressing it does not press the game. The innermost element with an onClick handles the click.
  • onClick is only for the left button. onRightClick fires when the right button goes down and comes back up on the element. onMouseDown and onMouseUp get "left" or "right". onMouseUp fires on the element the button is let go over, even if it went down somewhere else. Each one goes to the innermost element that has it.
  • Style with className only. Do not use style props or unsupported HTML tags.
  • Each shown GUI covers the whole screen, so w-full and h-full mean the whole screen. Place your content with Tailwind, like absolute top-4 right-4 for a corner, absolute bottom-6 left-1/2 -translate-x-1/2 for the bottom middle, or absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 for the center. Without that it sits in the top left corner. If you use screen-filling wrappers, you can use pointer-events-none to keep them from blocking other GUIs that mount, and pointer-events-auto on the content inside.
  • Build a meter bar as a fixed-size track with a percent-width fill inside it, never as a row of recolored squares.
  • Round dynamic tailwind classes to whole numbers, because new class strings rebuild the stylesheet.
tsx
const HealthMeter = ({ fraction }: { fraction: number }) => {  const percent = Math.round(Math.min(1, Math.max(0, fraction)) * 100)  return <div className="w-48 h-4 rounded-sm bg-black/50 p-1 overflow-hidden">    <div className={`h-full rounded-sm bg-emerald-400 w-[${percent}%]`} />  </div>}
  • useState() works like usual.
  • createState() works for state that comes from outside react, eg for 'use client' functions. This is similar to useSyncExternalStore.
  • Write createState(initialValue). The .get() function automatically subscribes the relevant React components to re-render when .set() is called. .set() is intended to be defined inside a 'use client' function and called from the server.
  • Update UI that moves (meters, timers, charge bars) frequently, and use transition-[width] duration-[Nms] ease-linear with N about the time between updates (below we use 17ms or 1 tick), so the bar slides between values.
tsx
const now = createState(getTime())const runClock = async () => {  while (true) {    await tick()    now.set(getTime())  }}runClock()const ChargeMeter = ({ chargeStartTime }: { chargeStartTime: number }) => {  const percent = Math.round(Math.min(1, (now.get() - chargeStartTime) / 2000) * 100)  return <div className="w-48 h-4 bg-black/50 overflow-hidden">    <div className={`h-full bg-emerald-400 transition-[width] duration-[17ms] ease-linear w-[${percent}%]`} />  </div>}
  • Avoid updating UI from setInterval; it makes bars jump.
  • Do not put UI in the upper left portion of the screen; that is reserved for chat.

Libraries:

  • You can use setTimeout() and setInterval(). Note that timers wait for the next tick to run, so they essentially only run on multiples of 16.6ms. Eg setTimeout(fn, 30) runs after ~33ms.
  • You can use console, URL, TextEncoder/TextDecoder, structuredClone, atob/btoa, Event/EventTarget, and AbortController.
  • There is no fetch, no WebSocket, no storage, and no crypto (use Math.random()).

Managing Objects:

  • Only the server can create and modify objects.
  • All objects in the world are replicated to all users. You cannot create an object on just one client.
FieldType
objectLocation

SoundPlayer

FieldType
soundDataId

The sound to play.

clockId

ObjectId | null

The Clock that lets you pause/play it.

volume

number

0-1

WorldLighting

Controls the sun location and sky settings.

FieldType
sunOrientation
sunColor
sunIntensity

number

sunShadowSoftness

number

How soft sun shadows are: how far every shadow edge blurs, in world units. 0 gives hard shadows.

ambientTopColor
ambientBottomColor
ambientIntensity

number

ambientOcclusionIntensity

number

0-1. How dark ambient occlusion can make creases and the ground right next to objects. 0 turns it off, 1 lets the deepest creases go black.

ambientOcclusionRadius

number

How far ambient occlusion reaches onto nearby surfaces, in world units.

ambientOcclusionSunlightStrength

number

0-1. How much ambient occlusion also darkens sunlight. 0 darkens only the sky light.

skyTopColor

The gradient sky color straight overhead.

skyHorizonColor

The gradient sky color at the horizon, which both halves of the gradient start from.

skyBottomColor

The gradient sky color straight down, below the horizon.

skyboxTextureDataId

DataId | null

An equirectangular skybox TextureData id. Transparent pixels reveal the gradient sky.

skyboxTextureOpacity

number

0 means the skybox texture is hidden. 1 means the skybox texture is fully visible.

skyReflectionStrength

number

0-1. How strongly shiny and metal surfaces reflect the sky. 0 turns sky reflections off.

vibrance

number

0-1. Pushes dull colors further from gray, and already vivid ones barely at all. 0 turns it off.

Functions & Services

stopScript

Use await stopScript() to stop all further execution of a script.

()

→

Promise<never>

tick

Use await tick() to await the next tick of the game loop. The game ticks 60 times a second on both the server and clients. Always use this in places like infinite while loops, otherwise the script will stall and be killed.

()

→

Promise<void>

vec3

PlayerService

To deal with players joining or leaving, use the object service.

  • Join: game.objectService.addObjectCreatedListener((player) => { ... }, "player")
  • Leave: game.objectService.addObjectDeletedListener((playerObjectId) => { ... }, "player").
FieldType
getAllPlayers

()

→

(LiveObject & { type: "player" })[]

getLocalPlayerObjectId

Only callable by a Client Script.

setLocalPlayerMetadata

metadata: JsonValue

→

void

Share state about your player with the server and other clients. For example, which animation is currently playing on your character.

Note: It is sent urgently, so you can use it for e.g. animations. Numbers are rounded to 3 decimal places to save bandwidth.

Throws if the metadata is over 256 bytes as JSON.

Only callable by a Client Script.

getPlayerMetadata

playerObjectId: ObjectId

→

JsonValue | null

The last JSON value received from this player, or null.

getPlayerCameraDirection

playerObjectId: ObjectId

→

Vec3 | null

getPlayerPing

playerObjectId: ObjectId

→

number | null

The ping (round trip) measured from the server. On clients it updates once a second.

refreshPlayerProfile

playerObjectId: ObjectId

→

Promise<void>

Fetches this player's kube.new profile again and updates their preferredColor and equippedItemId. Only callable by a Server Script.

getServerFnCaller

()

→

ObjectId | null

Use this in a 'use server' function to see which player called it. Null if called by the server.

Only callable by a Server Script.

getCharacterLocationAtTime

objectId: ObjectId,

timeMs: number

→

Location3D | null

Returns the location of the character object at the given time. timeMs is in units of the getTime() clock. It must be recent (within the last second). Returns null for old times and non-characters. Only callable by a Server Script.

getPlayerMetadataAtTime

playerObjectId: ObjectId,

timeMs: number

→

JsonValue | null

Returns the metadata of the player at the given time. timeMs is on the getTime() clock and must be recent (within the last second). Returns null for old times, and times when metadata was null. Only callable by a Server Script.

JsonValue

string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue }

ObjectService

This service exposes methods for reading and modifying objects.

All of the functions here are recursive: scale, rotate, move, setObjectWalkthrough, etc., meaning they apply to the object and all of its children.

If you only want to operate on one object and not its children, simply write object.propery = value, and don't use the functions here.

If you call these in a Client script the changes are only applied locally (not broadcast).

FieldType
getObjectById

objectId: ObjectId

→

LiveObject | null

Returns the object with this ObjectId.

getObjectByName

name: string

→

LiveObject | null

Returns any object with this name.

getObjectsByName

name: string

→

Returns all the objects with this name.

getObjectsByType

objectType: ObjectInfo["type"]

→

Returns all the objects with this type.

createObject

name: string,

objectInfo: ObjectInfo,

parentId: ObjectId

→

LiveObject | null

deleteObject

objectId: ObjectId

→

void

cloneObject

objectId: ObjectId,

cloneName: string

→

Remember to move the clone afterwards so it doesn't overlap.

createSkAttachmentsInRigidGroup

skeletonId: ObjectId,

rigidGroupId: ObjectId

→
addCatalogItemToWorld

catalogItemId: number,

parentId: ObjectId

→

Promise<LiveObject | null>

Adds a catalog item (hat) to the world by its id. Only callable by a Server Script.

scaleObjectBy

objectId: ObjectId,

scaleMultiplier: number

→

void

Scales the object by the multiplier. References Pivot if there is one. Note that repeatedly calling this will grow/shrink the object multiple times.

rotateObjectBy

objectId: ObjectId,

axis: Vec3,

degrees: number

→

void

Rotate the object around the provided axis in world coordinates. References Pivot if there is one.

rotateObjectTo

objectId: ObjectId,

rotation: Vec3

→

void

Rotate the object to the provided rotation, in degrees like location.rotation. References Pivot if there is one.

moveObjectBy

objectId: ObjectId,

offset: Vec3

→

void

moveObjectTo

objectId: ObjectId,

position: Vec3

→

void

teleportObjectTo

objectId: ObjectId,

position: Vec3

→

void

This is the only way you should teleport things. This is moveObjectTo, but without interpolating/physically sweeping the object.

setObjectWalkthrough

objectId: ObjectId,

isWalkthrough: boolean

→

void

setObjectPushable

objectId: ObjectId,

isPushable: boolean

→

void

setObjectDensity

objectId: ObjectId,

density: number

→

void

castRay

rayOrigin: Vec3,

rayDirection: Vec3,

options?: RaycastOptions

→

RaycastHit | null

Casts a ray through the physics world and returns the closest hit, or null. The direction does not need to be normalized.

castObjectShape

objectId: ObjectId,

direction: Vec3,

→

RaycastHit | null

Casts the object and returns the closest hit, or null. The direction does not need to be normalized.

getGroundSupport

objectId: ObjectId,

→

RaycastHit | null

What the object is standing on, or null. Use this to check if a character is grounded.

getVelocityRelativeToGround

objectId: ObjectId,

→

Shortcut that uses getGroundSupport. If there is no ground, this is just the regular velocity.

getGravity

()

→

The world's gravity, in distance per second squared.

getObjectAxes

objectId: ObjectId

→

{ x: Vec3, y: Vec3, z: Vec3 }

Get the x, y, z basis vectors (axes) of the object, based on its current rotation.

addObjectCreatedListener

callback: ObjectCreatedCallback<ObjectType>,

objectType?: ObjectType,

→

void

Fires when an object is created. Does not fire for objects that already existed when the world loaded.

removeObjectCreatedListener

callback: ObjectCreatedCallback<ObjectType>

→

void

addObjectDeletedListener

callback: ObjectDeletedCallback<ObjectType>,

objectType?: ObjectType,

→

void

Fires when an object is deleted. Deleting an object fires this for it and every descendant.

removeObjectDeletedListener

callback: ObjectDeletedCallback<ObjectType>

→

void

ObjectCreatedCallback

object: LiveObject & { type: ObjectType }

→

void

ObjectDeletedCallback

objectId: ObjectId,

objectType: ObjectType

→

void

RaycastHit

What castRay, castObjectShape, and getGroundSupport hit.

FieldType
object

The Ball, Brick, or Cylinder that was hit. For a RigidGroup, this is the subitem that was hit.

distance

number

How far the ray or shape traveled before it hit.

position

World-space point where it hit.

normal

Unit vector pointing out of the surface that was hit.

RaycastOptions

FieldType
maxDistance

number

Stop looking past this distance. Defaults to unlimited.

includeWalkthrough

boolean

Whether isWalkthrough objects can be hit. Defaults to false.

excludeObjectIds

Objects the ray should pass through. Excluding a RigidGroup excludes all of its subitems, so pass a character's RigidGroup id here to not hit yourself.

ShapeCastOptions

FieldType
maxDistance

number

Stop looking past this distance. Defaults to unlimited.

includeWalkthrough

boolean

Whether isWalkthrough objects can be hit. Defaults to false.

excludeObjectIds

Objects the ray should pass through. Excluding a RigidGroup excludes all of its subitems, so pass a character's RigidGroup id here to not hit yourself.

startOffset

Defaults to no offset.

GroundSupportOptions

FieldType
maxDistance

number

Defaults to 0.01.

maxGroundSlopeDegrees

number

Defaults to 55.

AnimationService

This service exposes methods for smoothly transitioning animation values.

FieldType
smoothlyChangeWeight

animatorId: ObjectId,

targetWeight: number,

durationMs: number,

interpolationStyle?: InterpolationStyle

→

void

Use this to smoothly change the weight of a SkAnimator or PropertyAnimator.

tweenTo

objectId: ObjectId,

propertyPath: string[],

targetValue: number,

durationMs: number,

interpolationStyle?: InterpolationStyle

→

Use this any time you want to smoothly move one of an object's numbers to a new value, like opening a door when a button is pressed. For example, tweenTo(doorId, ["location", "rotation", "y"], 90, 500).

The number moves from its current value to targetValue over durationMs, and then stays there. Tweening a number that is already tweening starts from wherever it has got to. While a tween plays, it wins over any PropertyAnimator on the same number, and it overwrites anything a script writes to that number.

This returns the Tween object. It sits at the world root while it plays and deletes itself when it finishes. Delete it yourself to stop the tween where it is.

ClockService

This service exposes methods for controlling Clock objects on SkAnimator, PropertyAnimator, and SoundPlayer.

FieldType
pause

clockId: ObjectId

→

void

play

clockId: ObjectId,

animationTimeSecs: number

→

void

setSpeed

clockId: ObjectId,

speed: number

→

void

CollisionService

  • collisionService fires even when isWalkthrough=true.
  • It never fires between two objects that both have isWalkthrough=true.
  • For RigidGroups, collisionService fires using the subitem(s) involved in the collision.

If you want to detect collisions accurately, use this service on the client, and call a 'use server' function that confirms the hit. On the server, collision detection of characters can be inaccurate.

FieldType
addCollisionListener
→

void

Only fires once, when the collision starts. It does not keep firing while the objects stay in contact. Objects that are already touching when the world loads, which on a client is when the player joins, don't fire it, but they still fire the stop listener when they separate.

removeCollisionListener
→

void

addCollisionStopListener
→

void

removeCollisionStopListener
→

void

CollisionCallback

object1: LiveObject,

object2: LiveObject

→

void

ClientCharacterService

Only callable by a Client Script.

FieldType
addBeforeStepListener
→

void

This is the preferred way to program a character controller, vehicle controller, etc. It runs on the client, right before each physics step.

In order to detect quick key/mouse inputs, you should usually save them in a variable and have this function make relevant changes. You're allowed to use functions like game.clientInputService.isKeyDown() here too, but just know they shouldn't be used alone as they won't detect presses that happen faster than a tick.

Example usage: game.clientCharacterService.addBeforeStepListener((characterObjectId, dtSecs) => ({ velocityChange: { x: 0, y: 0, z: 20 * dtSecs } })).

removeBeforeStepListener
→

void

addAfterStepListener
→

void

Similar to addBeforeStepListener, but runs right after each physics step, so you can see where the character ended up and correct it, like snapping players onto the ground.

removeAfterStepListener
→

void

CharacterStepCallback

A function that returns the changes you want to make to the character. This is called per physics step. You can't await here.

To time things across steps, add up dtSecs. getTime() is also accurate here.

characterObjectId: ObjectId,

dtSecs: number

→

CharacterChanges

FieldType
velocityChange

Delta in velocity.

angularVelocityChange

Delta in angular velocity (degrees).

velocity

New velocity. If velocityChange is provided, it's added on.

position

New position.

GuiService

Only callable by a Client Script.

The guiKey can be anything; it is a string you pick to identify the GUI root.

FieldType
show

guiKey: string,

rootRenderable: GuiRootRenderable

→

void

Example: game.guiService.show("my-key", <MyComponent />)

hide

guiKey: string

→

void

Example: game.guiService.hide("my-key")

useState

Only callable by a Client Script.

initialState: T | (() => T)

→

[T, (nextState: T | ((prevState: T) => T)) => void]

createState

Only callable by a Client Script.

initialState: T

→

{ get: () => T; set: (nextState: T | ((prevState: T) => T)) => void; }

GuiRootRenderable

GuiRenderable

This is the equivalent to a React Node.

The typical usage is <MyComponent />.

You can also use strings, numbers, booleans, null, and undefined.

Vec3Math

Common vector math functions for Vec3s. Every function returns a new vector.

Example usage: const velocity = vec3.scale(vec3.normalize(direction), speed).

FieldType
add

a: Vec3,

b: Vec3

→
sub

a: Vec3,

b: Vec3

→

a minus b.

scale

v: Vec3,

multiplier: number

→
dot

a: Vec3,

b: Vec3

→

number

cross

a: Vec3,

b: Vec3

→
length

v: Vec3

→

number

normalize

v: Vec3

→

Length 1, or the zero vector if v has no length.

clampLength

v: Vec3,

maxLength: number

→
projectOntoPlane

v: Vec3,

planeNormal: Vec3

→

Uploads and Data

Assets

The 'Assets' page contains a bunch of public objects anyone can use in their game. If you make an object, mesh, script, skybox, etc that other people might find useful to use in their game, we encourage you to upload it there. Assets can be deleted and changed at any time. If you update an asset, it won't update in games that have already downloaded it.

To upload an asset, ask the chat, or manually drag and drop a file into the editor.

Catalog

The 'Catalog' page is full of objects that people can buy and wear as hats. As a developer, you can think of it as the 'Assets' page, but with meshes only. A catalog item's creator can delete it on the website, and everyone who bought it loses it. It can also be unlisted: it leaves the Catalog page and can't be bought, but anyone with its id can still use it.

All catalog items (hats) have constraints and will be rejected if they do not follow them:

  • Hats should be placed in coordinates as if the origin were the top center of the character's head.
  • Assume a 1x1x1 head.
  • Dimensions: The widest side must be MIN_HAT_WIDEST_SIDE=0.5 to MAX_HAT_WIDEST_SIDE=2.0 units. The bounding box's center must be within MAX_HAT_CENTER_OFFSET=0.25 units within X Y Z of the origin. The bounding box must fit within MIN_HAT_BOTTOM=-0.6 to MAX_HAT_BOTTOM=0.1 units up/down from the origin.

To make a new catalog item, the chat must first create an asset, and then convert that asset to a catalog item.

Data

The 'Data' page (hard to find, but you can get to it on some assets) is blob storage for sounds, textures, and meshes. Lots of places reference it, including most assets and catalog items. Data is immutable once created, but it can be deleted.

Upload Constraints

Every file can be at most 16 MiB.

  • Mesh: a .glb with embedded textures, at most 8 MiB. Assets can have at most 20,000 triangles and 60,000 vertices, and catalog items can have at most 10,000 triangles and 30,000 vertices.
  • Texture: a PNG, JPEG, or WebP image, at most 2048 pixels per side.
  • Sound: Ogg Vorbis, MP3, WAV, or FLAC. A sound under 10 seconds becomes an Sfx asset, and a longer one a Sound asset.
  • Material: a color image, plus optional normal, roughness, and metalness images, each following the Texture rules and at most 64 MiB together.
  • Skybox: a PNG, JPEG, or WebP panorama exactly twice as wide as it is tall, at most 4096 x 2048.
  • Object: An object published from the world. Can't come from a file; use the chat to do this, it's a special tool.

Reference

LiveObject

Scripts usually deal with this object type.

It contains all the fields of either a Ball, Brick, Clock, Cylinder, Label, LocalGroup, SkJoint, SkAnimation, SkAnimator, SkAttachment, SkKeyframe, SkKeyframePose, Material, Mesh, Pivot, Player, PropertyAnimation, PropertyAnimator, PropertyKeyframe, RigidGroup, Script, Skeleton, SoundPlayer, Tween, or WorldLighting.

This object is a proxy, meaning every time you read a value from it, you get the current value in the world. You can also set fields on it, and they will affect the real object in the world. If the object is deleted, the id remains readable, but other fields will throw an error. Use getObjectById(), or a try/catch, if you think the object might be deleted.

And it contains the following additional fields:

FieldType
id
type

'ball' | 'brick' | 'clock' | 'cylinder' | 'group' | 'label' | 'labelImage' | 'material' | 'mesh' | 'pivot' | 'player' | 'propertyAnimation' | 'propertyAnimator' | 'propertyKeyframe' | 'rigidGroup' | 'script' | 'skAnimation' | 'skAnimator' | 'skAttachment' | 'skeleton' | 'skJoint' | 'skKeyframe' | 'skKeyframePose' | 'soundPlayer' | 'tween' | 'worldLighting'

name

string

parent

LiveObject | null

getChild

name: string

→

LiveObject | null

getChildren

name?: string

→

ObjectLocation

"Server" | "Client"

ClockState

If paused: animationTimeSecs is the duration into the animation to show.

If playing: localStartMs is the timestamp at which the animation started to play. The other field timeLocation says whether this timestamp is relative to the server's clock or the client's clock.

| { Paused: { animationTimeSecs: number } } | { Playing: { localStartMs: number, timeLocation: ObjectLocation } }

InterpolationStyle

"Step" | "Linear" | "EaseInOut" | "EaseIn" | "EaseOut"

WeightTransition

FieldType
startedAtMs

number

The simulation time in ms at which the weight transition started in timeLocation.

timeLocation

Whether startedAtMs is client time or estimated server time.

initValue

number

finalValue

number

durationMs

number

easing

BallPrimitiveSettings

FieldType
radius

number

CylinderPrimitiveSettings

FieldType
radius

number

length

number

BrickPrimitiveSettings

FieldType
size

BallMaterialSettings

Tile size is in world units, and tile offset is fractions of a tile. The origin for the tiling is the -Z top pole.

FieldType
materialInfoId

The objectId of the Material to use.

tileSizeU

number

tileSizeV

number

tileOffsetU

number

tileOffsetV

number

CylinderMaterialSettings

Tile size is in world units, and tile offset is fractions of a tile. The origin for the tiling is the +Z end of the -X side, and the top left corner of the square around each cap.

FieldType
materialInfoId

The objectId of the Material to use.

tileSizeU

number

tileSizeV

number

tileOffsetU

number

tileOffsetV

number

BrickMaterialSettings

Tile size is in world units, and tile offset is fractions of a tile. The origin for the tiling is the top left corner (the top face's -X -Z corner and the bottom face's -X +Z corner).

FieldType
materialInfoId

The objectId of the Material to use.

tileSizeU

number

tileSizeV

number

tileOffsetU

number

tileOffsetV

number

MeshMaterialSettings

FieldType
materialInfoId

The objectId of the Material to use.

AlphaMode

This only applies to Material objects whose albedoTextureDataId have transparency. Default is Opaque.

  • Opaque means to render the underlying object as solid, meaning show the base color in the transparent areas.
  • Mask makes the underlying object either opaque or invisible in areas, based on the transparency cutoff.
  • Blend uses the texture alpha multiplied by the object opacity for smooth transparency.

"Opaque" | { Mask: { cutoff: number } } | "Blend"

ObjectInfo

This is a helper type. It contains all the fields of either a Ball, Brick, Clock, Cylinder, Group, SkJoint, SkAnimation, SkAnimator, SkAttachment, SkKeyframe, SkKeyframePose, Material, Mesh, Pivot, Player, PropertyAnimation, PropertyAnimator, PropertyKeyframe, RigidGroup, Script, Skeleton, SoundPlayer, Tween, or WorldLighting, plus a type field saying which one it is.

Vec3

FieldType
x

number

y

number

z

number

ObjectId

Every object in the world gets a unique id that you can use to reference it. It's just a number. These object ids may change between compile time and runtime. We automatically convert most object ids, but you should not hard-code object ids into scripts as we can't parse those until runtime.

number

DataId

"Data" generally means large assets that are uploaded and given an id, like mesh data and texture data. Objects reference them using their data id.

number

UserId

The user-id of the user's account. For example, kube.new/profile/[user-id].

number

Location3D

This is called "location", but really means both position and rotation. In other words, the full information needed to place the object in 3D at snapshot in time. Can be global or local depending on context.

FieldType
position

Distance along x, y, z.

rotation

The object's x, y, z rotation in degrees with respect to the world, applied in order of y, x, z.

Movement3D

Speed information of an object. The derivative of Location3D.

FieldType
velocity

Distance per second along x, y, z.

angularVelocity

Degrees per second around x, y, z.

PhysicsToggles3D

A helper type.

FieldType
isWalkthrough

boolean

Whether this object can pass through other objects.

isPushable

boolean

Whether physics can move this object: gravity makes it fall and other objects push it. When false, it is totally unmovable.

density

number

Color3

RGB color.

FieldType
r

number

Red on a scale of 0-1.

g

number

Green on a scale of 0-1.

b

number

Blue on a scale of 0-1.

VisualSettings

A helper type.

FieldType
opacity

number

0-1.

color
glow

number

0-1. How much it shines in its own color whatever the light, and glows onto its surroundings, like neon. 0 is a normal object.

isAlwaysOnTop

boolean

Whether to render in front of everything even if visually blocked. This should almost never be used.

PhysicsInfo3D

A helper type.

FieldType
location
movement

Orientation3D

A helper type.

FieldType
theta

number

Degrees around the vertical direction. Starts at Z and goes clockwise around Y. This is what changes when you look left or right.

phi

number

Degrees with respect to the horizon (XZ plane). Upwards is positive. This is what changes when you look up or down.

CameraLocation

A helper type.

FieldType
position
orientation