The console library
Every cart on this site runs against the same library, and it travels with your upload — there is nothing to install and nothing to require. This is console 0.2.0, the version a new upload is pinned to.
A cart is a directory with a contract: an entry file naming a class after the directory, and whatever art and data it needs beside it. Zip that directory, upload it, publish it. Everything below is what goes in the file.
Get the console
A cart runs on the console: a directory of Ruby, this library, and a small tool that boots it. Unpack the download into a checkout and the library is in place.
Download the library — sign in to download it with a key that lets you publish carts from the console.
Your first cart
The smallest cart that moves
A cart is one class in one file, named after the directory it lives in: `carts/mine/app/mine.rb` defines `Mine`. Paths are relative to that directory, so `sprites/ship.png` means this cart's own file.
TITLE = 'mine' class Mine def self.assets { sprites: { ship: 'sprites/ship.png' } } end def setup @x = 400.0 end def update @x += 4 if input.held?(:right) @x -= 4 if input.held?(:left) end def render draw.sprite path: :ship, x: @x, y: 300 end end
Splitting it into scenes
A cart with scenes gets `enter`, `update` and `render` per scene instead of the cart's own `update`. `scene_data` is per-scene state that survives scene changes.
class Mine def setup scene :title, TitleScene scene :play, PlayScene goto :title end end class TitleScene def enter scene_data[:t] = 0 end def update goto :play if input.pressed?(:accept) end def render draw.text 'PRESS SPACE', x: screen[:w] / 2.0, y: screen[:h] / 2.0, anchor_x: 0.5, anchor_y: 0.5, size_px: 32, color: :accent end end
Playable before any art exists
`auto_sprite` synthesises a texture for any name that has no file, and never overwrites one that does. Generated textures are deterministic, so the same cart looks the same everywhere and can stand in for art later.
def setup auto_sprite :ship, 16, 16, :accent, :frame auto_sprite :star, 8, 8, :warn, :circle end def render draw.sprite path: :star, x: 120, y: 200 draw.sprite path: :ship, x: 260, y: 200 end
Entities, animation and a camera
An entity is a hash you own. `animate` plays a numbered directory of frames from the cart's sprites, and the camera clamps itself to the world rectangle it is given.
WORLD = { x: 0, y: 0, w: 1600, h: 900 } def setup @ship = spawn :ship, kind: :hero, x: 800, y: 120, w: 28, h: 28, sprite: :ship animate @ship, :run, fps: 12 camera.follow @ship camera.bounds = WORLD end def update @ship[:x] += 3 if input.held?(:right) @ship[:x] -= 3 if input.held?(:left) camera.update end def render draw.sprite path: @ship[:sprite], x: @ship[:x], y: @ship[:y] end
Sound, and work that reschedules itself
Music is one track; effects are fire-and-forget. `every` runs a block every N frames, which covers spawners, timers and cooldowns without coroutines.
def setup music :theme every 90, immediate: true do next_wave end end def update sfx :laser if input.pressed?(:accept) end def next_wave spawn :enemy, kind: :foe, x: 800, y: 900, w: 24, h: 24, sprite: :enemy, dx: -1 end
The hooks
Every hook is optional. A cart that defines none of them still boots — it just does nothing.
self.assets |
before boot | Names the art the cart owns. Declaring it is what lets the site check the cart at upload time and tell you a sprite is missing before anyone plays it. |
setup |
once, at boot | Build your state here: spawn things, load a level, start music. |
update |
every frame | Move, read input, advance timers. Only used when the cart defines no scenes. |
render |
every frame, last | Draws after whichever scene drew, so it is where a HUD that persists across scenes belongs. |
Sending a cart here
No archive to build and no upload form. With the bundle unpacked and your key in it:
./publish-site arcade
- Download the console. Signed in, the download arrives with a key in dragonstation.json beside the library.
- Build a cart. ./run --cart arcade boots it locally.
- Send it: ./publish-site arcade. It arrives here as a draft under your account.
- Play it, then publish it from the cart's page.
The same upload, without the browser. Everything the form refuses it refuses — traversal, symlinks, expansion bombs, art a cart names but does not own — and it says so in the message.
The endpoint, if you are writing your own client
POST /api/carts Authorization: Bearer <the api_key from dragonstation.json> archive=<the cart ZIP, as multipart/form-data> 201 {"slug","title","status":"draft","url",...} 401 {"error":"unauthorised"} a key that is not yours, or one a newer download replaced 422 {"error":...,"problems":[...]} the refusals below, one per line 400 {"error":"no archive"} nothing was sent 503 {"error":"no console library ..."} nothing to pin the cart to
The API
20 modules, described by the library's own comments. Nothing here is written by hand, so it cannot drift from the code it describes.
- Console::Version Console::Version -- kept separate so the version string is in exactly one 0 methods
- Console::Str Console::Str -- string helpers that avoid Regexp. 13 methods
- Console::Geom Console::Geom -- rect/point helpers. 26 methods
- Console::Palette Console::Palette -- named colors and a tiny style table. 3 methods
- Console::Assets Console::Assets -- where a cart's files actually live. 8 methods
- Console::Draw Console::Draw -- the rendering helper layer. 24 methods
- Console::Input Console::Input -- one action vocabulary across keyboard, controller and 29 methods
- Console::Sprites Console::Sprites -- name -> texture resolution, plus procedural generation. 22 methods
- Console::Frame Console::Animation -- sprite animation and an animation state machine. 22 methods
- Console::Audio Console::Audio -- sound effects and music. 19 methods
- Console::Camera Console::Camera -- a 2D scroll/zoom camera. 28 methods
- Console::Tween Console::Tween -- frame-driven interpolation and a tick scheduler. 23 methods
- Console::SceneDef Console::Scene -- the scene controller. 30 methods
- Console::EntityStore Console::Entity -- a flat, fast entity store. 21 methods
- Console::UI Console::UI -- immediate-mode widgets. 16 methods
- Console::Map Console::Map -- load a level out of an LDTK export. 13 methods
- Console::Body Console::Body -- a kinematic platformer body. 19 methods
- Console::Test Console::Test -- a tiny in-engine test harness. 12 methods
- Console::CartLoader Console::CartLoader -- finds a cart, requires it, and starts the console. 25 methods
- Console::API Console -- the cartridge runtime. 83 methods