Skip to content

Client Script API

Liam Robinson edited this page Nov 8, 2026 · 1 revision

Client Script API

Every function a client script has (API level 1). Client scripts run in the player's game: a mod's in single player, a server resource's on that server (when the server allows client scripts and the player's Servers' Scripts option is on). They see the game a frame late and can never change the ride itself.

The resource, timer, command, settings, JSON, storage, element and event functions are the same as on the server (Server Script API), with these differences:

  • No setElementDataClientWritable, no element data subscribers, no setElementOwner.
  • addCommandHandler names may also contain -; a handler gets (command, ...args). A client takes commands from its own player only.
  • Data written on an element the server made is a request: the server applies it only if it opened that key for this player, and otherwise sends its own value back.
  • client does not exist; localPlayer is the player's own element on a server and in a gamemode played alone, nil for a mod's script in single player.
  • Events go to the server with triggerServerEvent(name, source, ...) and triggerLatentServerEvent(name, bandwidth, source, ...). The source must be the player's own element or one it owns.

The game

Function What
getLocalRider(): Element? the player's rider, or nil with no level up
getRiders(): {Element} the local rider, then the race's computer riders, then the named ghosts (Explore's rival ghosts, a server's riders)
getRiderState(rider): RiderState? name, position and isLocal for every rider; for the local rider also velocity and speed (m/s), air, grind, trick (the running trick's name), score, health (0 to 1) and finished
getInstance(): Instance? {mode, drop, state, clock}: the event type, ssx:drop/<name>, countdown / running / results, the event clock in ms
getStandings(): {Standing} the HUD leaderboard's rows: {rider?, name, place, value, finished}
getContent(id): ContentInfo? {id, kind, source, layer}: which package (or the disc) provides an id
listContent(kind): {string} every id of a kind
preloadContent(id): boolean starts decoding a texture, sound or marker ahead of use (64 a frame)
getGroundHeight(x, z): number? the height of the highest surface below that point on the level's collision

Drawing

Drawing works only inside an onClientRender handler, on a 1280 × 720 stage scaled to the window like the HUD. Draws are kept until the next frame's.

Function What
draw.text(text, x, y, color?, options?): boolean plain text, at most 256 bytes; options: size (6 to 128), align ("left", "center", "right"), shadow. A character the font lacks is drawn as ?
draw.rect(x, y, w, h, color): boolean a filled rectangle
draw.image(x, y, w, h, texture, color?): boolean a texture id, tinted by color; drawn once decoded
tocolor(r, g, b, a?): number a colour from 0–255 parts (a 255 by default)

Per frame at most 1 024 draw calls for all scripts and 512 per package, 2 048 glyphs, and rectangles and images covering 16 stages' area. Images: at most 2 048 pixels a side, 32 held.

Sound, music, camera, messages

Function What
playSound(id, position?): boolean a sound id, at a point in the world if given. 32 a frame for all scripts, 8 per package; at most 8 positioned sounds and 32 package sounds at once
playMusic(id): boolean plays a song in place of the level's licensed music (once a second per package); refused on a Deadly Descent
setCameraTarget(rider?): boolean the camera follows another rider of getRiders; nil gives the player's own camera back
fadeCamera(fadeIn, seconds?, color?): boolean fades the screen to a colour (fadeIn = false) or back (0 to 60 s)
showHudMessage(title, text, seconds?): boolean the HUD's message panel for 0.5 to 30 s (title 256 bytes, text 1 024); once a second per package; false while the game's own message is up

Keys

Function What
bindKey(key, state, handler): boolean calls handler(key, state) when the key goes "down", "up" or "both"
unbindKey(key, state?, handler?): boolean

Only keys the game never reads can be bound: F2–F8, F10–F12, the digits, the number pad (not its Enter), b m o p u, , . / ; ' [ ] - = ` \, Insert, Pause and Scroll Lock. In the API they are named "f2", "0", "num_0", "num_add", "num_sub", "num_mul", "num_div", "num_dec", "b", ",", "insert", "pause", "scroll" and so on. No key reaches a script while the chat or the on-screen keyboard is open. At most 64 binds per package.

Markers

Function What
createMarker(id, position, options?): Element a marker of a marker id in the level that is up: options heading (degrees), size (0.1 to 100 m), color, visible
setMarkerPosition, getMarkerPosition, setMarkerColor, setMarkerVisible change your own markers

The local rider hitting a marker raises onClientMarkerHit(rider, forward) on it: a sphere or cylinder when entered, a ring or gate at each crossing (forward along its heading). At most 256 markers per package and 1 024 per level; a new level removes the last level's markers.

Types

type RiderState = { name: string, position: Vec3, velocity: Vec3?, speed: number?, air: boolean?,
    grind: boolean?, trick: string?, score: number?, health: number?, finished: boolean?, isLocal: boolean }
type Instance = { mode: string, drop: string, state: "lobby" | "countdown" | "running" | "results", clock: number }
type Standing = { rider: Element?, name: string, place: number, value: number, finished: boolean }
type ContentInfo = { id: string, kind: string, source: string, layer: string }
type MarkerOptions = { heading: number?, size: number?, color: number?, visible: boolean? }
type TextOptions = { size: number?, align: ("left" | "center" | "right")?, shadow: boolean? }

Clone this wiki locally