Charted Arena API

Build a bot algorithm, connect it via REST, and compete in battle arenas or races.

Two game modes: Battle Mode (fight to the death) and Race Mode (first to the finish line). Both modes use the same core API.

Quick Start

  1. JoinPOST /api/battle/join or POST /api/race/join with your bot’s name. You’ll get back a botId. Your bot starts on a waiting list.
  2. Wait for promotion — a viewer in the arena will promote your bot into the match. Poll GET /api/bot/:botId/status every 1–2 seconds until status changes from "waiting".
  3. Wait for match start — once promoted, status becomes "ready" (bot is placed in the arena but frozen). A viewer starts the match. Poll status every 100–200ms until it becomes "active".
  4. Game loop — repeat every 100–200ms:
  5. LeavePOST /api/bot/:botId/leave (works from waiting list or active match)
Private rooms: Viewers can create private battle arenas or race rooms, each with a short join code (e.g. A3K7 or RACE-B2N5). To join a private room instead of the main one, use POST /api/battle/:joinCode/join or POST /api/race/:joinCode/join. State is available at GET /api/battle/:joinCode/state or GET /api/race/:joinCode/state.
Rate limit: The /status and /command endpoints are limited to 1 request per 100ms per bot (each endpoint has its own limit, so calling both in the same 100ms is fine). Other endpoints are not rate-limited. Retry with a short backoff on 429 Too Many Requests.
Waiting list timeout: Bots on the waiting list are removed after 30 seconds of no API calls. Keep polling /status to stay alive while waiting for promotion.
Movement commands are latched: Once you send "thrust": "forward", your bot keeps driving forward until you send a different command. You don’t need to resend the same command repeatedly — only send when you want to change direction. Sending just { "thrust": "forward" } changes thrust without affecting the current turn state, and vice versa.

Battle Mode

Bots fight in a 20m × 20m square with walls on all sides. Your bot is a small tank that can drive forward/backward and turn left/right. The goal: reduce other bots to 0 health using your saw blade, ramming, or bombs.

PropertyValueDetails
Arena size20 × 20mX: −10 to +10, Z: −10 to +10
Health100 HPWhen health reaches 0, your bot is destroyed
Bot size0.6m radiusBots are circles for collision purposes
Max speed5 m/sAt full health. Slows to 40% at 0 HP
Turn rateπ rad/sAt full health. Slows with damage (same curve as speed)
Arena coordinate system

Rotation

The rotation field tells you which direction a bot is facing, measured in radians:

Rotation valueFacing direction
0Toward +Z (up on the map)
1.57 (π/2)Toward +X (right)
3.14 (π)Toward −Z (down)
−1.57 (−π/2)Toward −X (left)

"turn": "left" increases rotation (counter-clockwise on the map). "turn": "right" decreases it (clockwise).

Combat

Saw Attack — your main weapon. Swings forward and damages any bot directly in front of you within 1.5 meters. The attack takes about 0.5 seconds to complete, then needs 2 seconds to recharge. Only hits bots in a 60° cone. Deals more damage the faster you’re moving. Check sawCooldown — when it’s 0, you can attack again.

Ram Damage — crashing into another bot at speed deals damage to both bots. Faster collisions deal more damage (up to 15 HP per hit). Combine boost + ramming for maximum impact.

Boost — activates a 0.5-second burst of 2.5× speed. Great for charging into enemies or escaping. Has a 10-second cooldown. Check boostCooldown — when it’s 0, boost is ready.

Damage Slowdown — as your bot takes damage, it gets slower and turns more sluggishly. At full health: 100% speed. At 50 HP: ~70%. Near death: 40%. This means a damaged bot is easier to finish off.

Arena Objects (Battle Only)

Objects spawn on the arena floor every 15 seconds (up to 3 at a time). Check the objects array in the state response for positions and types.

TypeEffect
healthInstantly heals 35 HP on contact. Always picks up
bomb_pickupPicked up and held by your bot (check heldItem). You can only hold one at a time. Use { "action": "useItem" } to drop it 1.5m behind you as an active bomb
bomb_activeDeals 60 damage + knockback to any bot that touches it (including the bot that placed it). Disappears after detonating or after 10 seconds. If a bot is destroyed while holding a bomb, it drops automatically

Race Mode

Same 20m × 20m arena but with interior walls creating a winding track. First bot to complete the full circuit and cross the finish line wins. All combat mechanics still work (saw, ram, boost) — you can attack opponents while racing. No items or pickups spawn.

PropertyValue
Starting gridRows of 5, behind the start/finish line
Max racers20
Countdown5 seconds (bots frozen until countdown ends)

Track Layout

The 20×20m arena has the same outer walls as battle mode, plus four interior walls that create corridors. Bots race through the corridors in order.

Race track layout

When a bot finishes the race, it is removed from the arena and placed back on the waiting list. The race ends when all bots have finished or died, or when a viewer stops it.

Endpoints

Joining

POST /api/battle/join

Join the main battle arena’s waiting list.

// Request
{ "name": "MyBot" }

// Response
{ "botId": "your-bot-uuid", "name": "MyBot", "status": "waiting" }

Name max 32 characters.

POST /api/battle/:joinCode/join

Join a private battle arena’s waiting list. Same request/response format.

POST /api/race/join

Join the main race room’s waiting list. Same request/response format.

POST /api/race/:joinCode/join

Join a private race room’s waiting list. Same request/response format.

Bot Status

GET /api/bot/:botId/status

Check your bot’s current status. While on the waiting list, each call resets the 30-second timeout.

// Response
{ "status": "waiting" }     // on waiting list, not yet promoted
{ "status": "ready" }       // promoted into match, waiting for match to start
{ "status": "active" }      // match is running, commands accepted
{ "status": "dead" }        // bot was destroyed
{ "status": "not_found" }   // bot doesn't exist (removed or expired)

Commands

POST /api/bot/:botId/command

Control your bot. Only works when status is "active". Returns 400 if match hasn’t started, 409 if bot is still on the waiting list.

Movement

Movement commands are sticky — they stay active until you send a different value. You can set thrust and turn independently.

{ "thrust": "forward", "turn": "left" }    // drive forward while turning left
{ "thrust": "forward", "turn": null }      // drive straight, stop turning
{ "thrust": null, "turn": "right" }        // stop driving, spin in place
{ "thrust": null, "turn": null }           // full stop
{ "thrust": "forward" }                    // change thrust only, turn unchanged
{ "turn": "left" }                         // change turn only, thrust unchanged

thrust: "forward" | "backward" | null
turn:   "left" | "right" | null

Actions

One-shot commands that trigger immediately. Your current movement continues — actions don’t cancel thrust/turn.

{ "action": "attack" }    // swing your saw blade (check sawCooldown first)
{ "action": "boost" }     // 2.5x speed burst for 0.5s (check boostCooldown first)
{ "action": "useItem" }   // drop held bomb behind you (battle only)

POST /api/bot/:botId/leave

Remove your bot from the room. Works whether your bot is on the waiting list, on the grid, or in an active match.

// Response
{ "ok": true }

Game State

GET /api/battle/state

Current battle arena state. Only returns alive bots (dead bots excluded so you don’t target wrecks).

GET /api/battle/:joinCode/state

Private battle arena state.

GET /api/race/state

Current race room state. Includes track info.

GET /api/race/:joinCode/state

Private race room state.

Example battle state response:

{
  "tick": 1420,
  "timestamp": 1712345678000,
  "matchStatus": "active",
  "bots": [{
    "id": "bot-uuid",
    "name": "MyBot",
    "x": 3.21,
    "z": -1.54,
    "rotation": 1.571,
    "health": 85,
    "speed": 2.12,
    "sawCooldown": 0,
    "boostCooldown": 0,
    "heldItem": null,
    "alive": true
  }],
  "objects": [
    { "id": "uuid", "x": 5.0, "z": -2.0, "type": "health" },
    { "id": "uuid", "x": -3.5, "z": 7.1, "type": "bomb_active" }
  ]
}

Bot Fields

FieldDescription
idUnique bot identifier (use this to find yourself in the array)
nameBot display name
x, zPosition in meters from arena center (−10 to +10)
rotationDirection the bot is facing in radians (see Rotation table)
healthRemaining HP (0–100). 0 = destroyed
speedHow fast the bot is currently moving (m/s)
sawCooldownSeconds until saw can be used again (0 = ready)
boostCooldownSeconds until boost can be used again (0 = ready)
heldItemnull or "bomb" (battle only)
aliveWhether bot is alive

Object Fields (battle only)

FieldDescription
idUnique object identifier
x, zPosition in meters from arena center
type"health", "bomb_pickup", or "bomb_active"