Skip to content

PICO 8 Program Structure

THE_ORONCO edited this page May 21, 2022 · 5 revisions

PICO 8 Program Structure

When a PICO-8 programs runs, all of the code from tabs is concatenated (from left to right) and executed. It is possible to provide your own main loop manually, but typically PICO-8 programs use 3 special functions that, if defined by the author, are called during program execution:

function description
_UPDATE() Called once per update at 30fps.
_DRAW() Called once per visible frame.
_INIT() Called once on program startup.

A simple program that uses all three might look this:

FUNCTION _INIT()
    -- ALWAYS START ON WHITE
    COL = 7
END
    
FUNCTION _UPDATE()
    -- PRESS X FOR A RANDOM COLOUR 
    IF (BTNP(5)) COL = 8 + RND(8) 
END
    
FUNCTION _DRAW()
    CLS(1)
    CIRCFILL(64,64,32,COL)
END

_DRAW() is normally called at 30fps, but if it can not complete in time, PICO-8 will attempt to run at 15fps and call_UPDATE() twice per visible frame to compensate.

Running PICO-8 at 60fps

_UPDATE60()

When _UPDATE60() Is defined instead of _UPDATE(), PICO-8 will run in 60fps mode:

Both _UPDATE60() and _DRAW() are called at 60fps
half the PICO-8 CPU is available per frame before dropping down to 30fps.

Note that not all host machines are capable of running at 60fps. Older machines, and / or web versions might also request PICO-8 to run at 30 fps (or 15 fps), even when the PICO-8 CPU is not over capacity. In this case, multiple _UPDATE60 calls are made for every_DRAW call in the same way.

#INCLUDE

Source code can be injected into a program at cartridge boot (but not during runtime), using "#INCLUDE FILENAME", where FILENAME is either a plaintext file (containing Lua code), a tab from another cartridge, or all tabs from another cartridge:

#INCLUDE SOMECODE.LUA
#INCLUDE ONETAB.P8:1
#INCLUDE ALLTABS.P8

When the cartridge is run, the contents of each included file is treated as if it had been pasted into the editor in place of that line.

Filenames are relative to the current cartridge (so, need to save first)

  • Includes are not performed recursively.
  • Normal character count and token limits apply.

When a cartridge is saved as .P8.PNG, or exported to a binary, any included files are flattened and saved with the cartridge so that there are no external dependencies.

#INCLUDE can be used for things like

  • Sharing code between cartridge (libraries or common multi-cart code)
  • Using an external code editor without needing to edit the .p8 file directly.
  • Treating a cartridge as a data file that loads a PICO-8 editing tool to modify it.
  • Loading and storing data generated by an external (non-PICO-8) tool.

Quirks of PICO-8

Common gotchas to watch out for:

  • The bottom half of the sprite sheet and bottom half of the map occupy the same memory.
    Best use only one or the other if you're unsure how this works.
  • PICO-8 numbers have limited accuracy and range; the minimum step between numbers is approximately 0.00002 (0x0.0001), with a range of -32768 (-0x8000) to approximately 32767.99999 (0x7fff.ffff)
    If you add 1 to a counter each frame, it will overflow after around 18 minutes!
  • Lua arrays are 1-based by default, not 0-based. FOREACH starts at TBL[1], not TBL[0].
  • COS() and SIN() take 0โ€ฆ1 instead of 0โ€ฆPI*2, and SIN() is inverted.
  • SGN(0) returns 1.

CPU

Although PICO-8 does not have a clearly defined CPU, there is a virtual CPU speed of 8MHz, where each Lua vm instruction costs around 2 cycles. Built-in operations like drawing sprites also have a CPU cost. This means that a PICO-8 cartridge made on a host machine with a powerful CPU can still be guaranteed to run (reasonably) well on much slower machines, and to not drain too much battery on phones / when running on the web.

To view the CPU load while a cartridge is running, press CTRL-P to toggle a CPU meter, or print out STAT(1) at the end of each frame.

Clone this wiki locally