Developer Manual

Step-by-step guide to creating your first PLUTO-8 game.

1. Getting Started

PLUTO-8 is a fantasy console that runs entirely inside Roblox. You do not need to install anything — just join the Roblox game and you are ready to create.

What is a Fantasy Console?

A fantasy console is a simulated retro gaming machine with deliberately limited specifications. Those constraints make it easy to learn game development and to finish small games quickly. PLUTO-8 is inspired by PICO-8 but lives natively inside Roblox, so your games are instantly playable by millions of people.

Specifications at a Glance

  • Display: 256×256 pixels, 256-color palette
  • Frame rate: 30 FPS
  • Sprites: 256 sprites, each 8×8 pixels
  • Tilemap: 128×64 tiles
  • Scripting: Lua (sandbox, PICO-8-style API)
  • RAM: 32 KB addressable memory
  • SFX: 8 sound effect slots

Opening the Code Editor

  1. Join the PLUTO-8 Roblox game.
  2. Press Tab (or tap the pencil icon on mobile) to enter Developer Mode.
  3. The code editor opens on the right side of the screen.
  4. Type your Lua code and press Ctrl+Enter (or the Run button) to load it.
Note: Developer mode is available to everyone. There is no separate account or permission needed.

2. Hello World

The smallest possible PLUTO-8 game just needs a _draw function:

lua
function _draw()
  cls(0)
  print("HELLO, WORLD!", 60, 120, 7)
end

Copy this into the code editor and press Run. You should see white text on a black background.

Let's break it down:

  • cls(0) — clear the screen to color 0 (black). Always call this first in _draw or you will see the previous frame.
  • print("HELLO, WORLD!", 60, 120, 7) — draw text at position (60, 120) in color 7 (white). The screen is 256×256, so (60, 120) is near the left-center.
Tip: Color 7 is always white in the default PLUTO-8 palette. Color 0 is black. See the color reference in the API docs.

3. The Game Loop

PLUTO-8 runs three functions in a fixed order, 30 times per second:

lua
function _init()
  -- Runs ONCE when the cartridge starts.
  -- Initialise your variables here.
  score = 0
  player = { x = 128, y = 200 }
end

function _update()
  -- Runs 30 times per second.
  -- Move objects, check collisions, update state.
  if btn(0) then player.x -= 2 end
  if btn(1) then player.x += 2 end
end

function _draw()
  -- Runs after _update.
  -- Issue all draw commands here.
  cls(1)
  spr(1, player.x, player.y)
  print("SCORE: "..score, 2, 2, 7)
end

Execution Order

  1. On first run: _init() is called once.
  2. Every frame (30 fps): _update() is called.
  3. Immediately after: _draw() is called.
  4. The resulting frame is sent to all connected clients.
Note: All three functions are optional. If _init is missing, the game still works. If _draw is missing, nothing is displayed.

Delta Time

Since _update is called exactly 30 times per second, you can use a fixed timestep: moving an object by 2 pixels per frame is moving it at 60 pixels per second. No delta-time multiplication is needed.

4. Drawing

Colors

PLUTO-8 uses a 256-color palette. Colors 0–15 follow the classic PICO-8 palette. Colors 16–255 are extended. The most common colors:

  • 0 — Black
  • 1 — Dark Blue
  • 2 — Dark Purple
  • 3 — Dark Green
  • 5 — Dark Grey
  • 6 — Light Grey
  • 7 — White
  • 8 — Red
  • 9 — Orange
  • 10 — Yellow
  • 11 — Green
  • 12 — Blue
  • 14 — Pink
  • 15 — Peach

Shapes

lua
-- Pixel
pset(50, 50, 8)

-- Line
line(0, 0, 100, 100, 7)

-- Rectangle (outline / filled)
rect(10, 10, 60, 40, 6)
rectfill(10, 10, 60, 40, 1)

-- Circle (outline / filled)
circ(128, 128, 30, 11)
circfill(128, 128, 30, 3)

Text

Use print(text, x, y, color) for simple text. The built-in font is 4×6 pixels. Each character is 4 pixels wide with 1 pixel spacing, so a line of text is about 5 pixels per character.

lua
print("PLAYER 1", 80, 10, 7)
print("SCORE: "..tostr(score), 2, 2, 10)

Camera

Use camera(x, y) to scroll the view. All draw calls after this are offset so that world position (x, y) appears at screen position (0, 0).

lua
function _draw()
  cls(1)
  camera(cam_x, cam_y)   -- follow the player
  map(0, 0, 0, 0, 32, 32)
  spr(1, player.x, player.y)
  camera(0, 0)           -- reset for HUD
  print("HP:"..hp, 2, 2, 8)
end

5. Input

PLUTO-8 supports up to 7 buttons. On desktop they map to keyboard keys; on mobile a D-pad and A/B buttons are shown on screen.

IDActionKeyboardMobile
0Left← or AD-pad left
1Right→ or DD-pad right
2Up↑ or WD-pad up
3Down↓ or SD-pad down
4A (confirm)ZA button
5B (cancel)XB button
6StartEnterStart button

btn vs btnp

btn(id) returns true every frame the button is held. Use it for continuous movement.

btnp(id) returns true only on the first frame the button is pressed. Use it for actions that should fire once per press — like jumping, firing, or selecting a menu item.

lua
function _update()
  -- continuous movement
  if btn(0) then x -= 2 end
  if btn(1) then x += 2 end

  -- single press: jump only once
  if btnp(4) and on_ground then
    vy = -6
  end
end

6. Sprites

The sprite sheet holds 256 sprites, each 8×8 pixels. Sprite 0 is the top-left; sprite 1 is to its right, and so on across 16 columns.

Creating Sprites

  1. Open the sprite editor tab (palette icon) in Developer Mode.
  2. Click a sprite slot to select it.
  3. Draw with the pencil tool — left-click to place pixels, right-click to erase.
  4. Use the palette to pick colors.
  5. Zoom in with the zoom slider for precision.

Drawing Sprites in Code

lua
-- Draw sprite 1 at position (x, y)
spr(1, x, y)

-- Draw a 2x2 block of sprites starting at sprite 4
-- This draws sprites 4, 5, 20, 21 as a 16x16 image
spr(4, x, y, 2, 2)

-- Flip horizontally (for left-facing walk)
spr(1, x, y, 1, 1, facing_left)

Sprite Animation

lua
function _init()
  frame = 0
  anim_timer = 0
end

function _update()
  anim_timer += 1
  if anim_timer >= 8 then  -- change frame every 8 ticks (~4 fps)
    anim_timer = 0
    frame = (frame + 1) % 4   -- cycle frames 0-3
  end
end

function _draw()
  cls(0)
  spr(1 + frame, player.x, player.y)
end
Tip: Color 0 is transparent by default when drawing sprites. Use palt() to change which colors are transparent.

7. Tilemap

The tilemap is a 128×64 grid of tile indices. Each tile refers to a sprite. Draw a section of the map with map().

Designing Maps

  1. Open the Map Editor tab (grid icon) in Developer Mode.
  2. Select a tile from the sprite sheet at the bottom.
  3. Click and drag on the map canvas to paint tiles.
  4. Use the fill tool (bucket) for large areas.

Drawing the Map

lua
function _draw()
  cls(1)
  -- draw tiles 0,0 to 31,31 of the map at screen (0,0)
  map(0, 0,   -- map top-left tile
      0, 0,   -- screen destination
      32, 32) -- tile count (256x256 pixels)
end

Tile-based Collision

lua
function is_solid(wx, wy)
  local tx = flr(wx / 8)
  local ty = flr(wy / 8)
  local tile = mget(tx, ty)
  -- sprite flag 0 = solid tile
  return (tile ~= 0) and (tile & 1) ~= 0
end

8. Sound

PLUTO-8 supports 8 SFX slots (0–7). You define sounds in the SFX editor inside Developer Mode, then play them in code.

Playing SFX

lua
sfx(0)          -- play sfx 0 at full volume
sfx(1, 0.5)     -- play sfx 1 at 50% volume

function _update()
  if btnp(4) then
    sfx(0)     -- play "jump" sound
    vy = -5
  end
end
Note: The SFX editor and music system are coming in a future update. Currently you can use pre-defined SFX from the default cartridge.

9. Tips & Tricks

Performance

  • Keep _draw lean. Minimize draw calls per frame — avoid drawing thousands of individual pixels with pset.
  • Use sprite sheets and spr() instead of per-pixel drawing.
  • Avoid allocating new tables every frame — reuse objects instead.
  • Clearing only dirty rectangles with rectfill instead of cls can help in static scenes.

Common Patterns

Entity list:

lua
bullets = {}

function spawn_bullet(x, y, vx, vy)
  add(bullets, {x=x, y=y, vx=vx, vy=vy, alive=true})
end

function _update()
  for b in all(bullets) do
    b.x += b.vx; b.y += b.vy
    if b.x < 0 or b.x > 256 then del(bullets, b) end
  end
end

State machine:

lua
state = "title"

function _update()
  if state == "title" then
    if btnp(4) then state = "game" end
  elseif state == "game" then
    update_game()
  elseif state == "gameover" then
    if btnp(4) then state = "title" end
  end
end

AABB collision check:

lua
function overlaps(a, b)
  return a.x < b.x+b.w and a.x+a.w > b.x
     and a.y < b.y+b.h and a.y+a.h > b.y
end

10. Publishing

Once your game is complete, you can publish it to the PLUTO-8 community browser.

How to Publish

  1. Open the code editor and make sure your game runs without errors.
  2. Click the Publish button (upload icon) in the Developer Mode toolbar.
  3. Enter a name and optional description for your game.
  4. Click Confirm.
  5. Your game is now listed in the Community Games browser and playable by everyone.

After Publishing

  • Players can like your game and leave it in their favourites.
  • You can update your game at any time — the cartridge is replaced for all future plays.
  • Play count and like count are shown on your game card.
Note: Published games are moderated. Games that violate Roblox Community Guidelines will be removed.
Tip: Give your game a clear title and test it on mobile before publishing — many players use touch controls.