Log In  

Tested version: 0.2.5g

When trying to save a new cart by running a cart that contains a save instruction in headless mode, and the target cart already exists,


Create a demo cart demo.p8 with the following lua content:


Then run it headlessly:

pico8 -x run demo.p8

The first time, all carts will be generated. On further runs, carts already exist, but only every other cart will be properly overwritten, starting from the second one, namely: demo_inconsistent_save2.p8 and demo_inconsistent_save4.p8

Workaround: make sure to remove all existing target carts in your custom script before saving them again.

P#141650 2024-02-19 15:05


Hey, I tried to add my YouTube channel in my user settings on this website, but each time I click on a field I get an error:

Error // Bad Data Format: <!DOCTYPE html>

then the field is replaced with [error]

The same occurs when checking Cart Listings: Make Favourites Public / Make Likes Public

P#141156 2024-02-05 18:00


I had a hard time debugging "Cannot load" error message when running a cartridge with pico8_0.2.5g -run, where the cart contained an import missing_spritesheet.png statement. I thought that "Cannot load" meant "Cannot load cart" but in fact the cart was loaded, only the spritesheet was not. This was due to the spritesheet missing from the carts folder, but I only understood this thanks to an older version of PICO-8, pico8_0.2.2c which would print an extra error message in the console:

> failed to import spritesheet; file: /path/to/missing_spritesheet.png

Is there a particular reason why the terminal message was removed? To make the terminal cleaner? In this case, could we at least have the full error message in PICO-8 instead?

Tested versions:

  • 0.2.2c: "failed to import spritesheet; file: /path/to/missing_spritesheet.png"
  • 0.2.5e: nothing is printed in terminal
  • 0.2.5g: nothing is printed in terminal

To repro: create a cart with "import dummy.png" (where dummy.png is a file doesn't exist in carts/) and run it with pico8 -run from terminal.

P#138219 2023-12-04 11:43


Cart #komehara_reordered-0 | 2022-09-20 | Code ▽ | Embed ▽ | No License

This is a game I made for the Global Game Jam 2018 on theme "Transmission", but I never posted it here. In fact, it's the first cartridge I'm posting on the BBS.

I wanted to improve it before uploading it, esp. add keyboard-only controls, so that users can play it on a micro-console without mouse... But I never got the time, so I prefer posting the current version here, so at least you can try it on PC or mobile device (using touch for mouse + gamepad overlay).


REORDERED is a puzzle game inspired by the concept of out-of-order delivery in computer networking. The player must reorder the letters of a message sent from an antenna to another to form another word.

To reorder letters, the player must mess up the travel time of some letters by interacting with the environment.


!! Mouse or touch is required to play !!

  • Mouse click on Togglable element (green, small eye icon in the center) to toggle it
  • Mouse click on Restart icon to restart level
  • Keyboard press (X) (keyboard X/V/M) to start emitting letters
  • (Debug) Press Arrow Left/Right to go to previous/next level


To reorder letters, the player must mess up the travel time of some letters by interacting with the environment.

A given word is decomposed in letters that are sent at regular intervals from an emitting antenna. A letter travels in straight line until it reaches an element that affects its trajectory. The player can enable and disable those elements in order to guide the letters into a certain direction.

Letters eventually arrive on a receiving antenna to compound a word again. Some trajectories being longer than others, letters are effectively reordered by the timely toggling of interactive elements, causing a new word to be created.

Special tiles

  • Arrow: changes the direction of a traveling letter. Has a togglable variant.
  • Mirror: reflects direction of a traveling letter. Has a togglable variant.
  • Emitter: shaped as an antenna, it sends the original word, letter by letter
  • Receiver: shaped as a circle (parabola seen from above), it receives the final word, letter by letter.

itch.io link: https://komehara.itch.io/reordered
Code repository link: https://github.com/hsandt/ggj2018-Transmission

P#111198 2022-05-02 16:00 ( Edited 2022-09-20 12:47)


Hey, is there a way to draw a rectangle of arbitrary dimension filled with tiled sprites, aka textured rectangle or tiled rectangle? I can of course manually iterate and draw the same sprites many times, but this had very bad performance so I quickly hit over 1 CPU (meaning I'm dropping from 60 to 30 FPS).

In other engines, you'd set sprite rendering mode to Tiled and stretch the sprite, resulting in a repeating pattern preserving the sprite original size, instead of stretching the original sprite like sspr.

Recently, we got textured lines (tline) and fill patterns (fillp) but there is no direct way to use them to draw tiled sprites.

I see some ways to do that, but they are all workarounds, with no one-line solution:
a. iterate on 2D coordinates and draw same sprite with spr() -> bad performance
b. prepare a tilemap of the same sprites and draw them, offset by some camera() with map() -> need to either setup tilemap in data or generate them at runtime, and need to think with reverse offset to set camera properly. Kind of a waste of data if your rectangle is always using the same sprite. Only works if you have spare space in your tilemap (or you must overwrite some tilemap data and restore it later; note that with Big Maps in PICO-8 0.2.4, it's less of an issue)
c. use fillp to set up a binary pattern and use it for rectfill -> only works for binary (2-color) sprites

I'm using a. for now, and only drawing sprites visible by at least 1px on screen to spare CPU, but even that costs too much (around 0.7 out of my budget of 0.1! and I need TWO layers of those because the foreground layer has some transparency, so I quickly get over CPU budget).

I think I'll be switching to b. for now.


sspr() could have an additional argument "render_mode_option". 0 is default of stretching, 1 is for tiled rendering.

P#110822 2022-04-25 11:41



I'm heavily using .p8 files as headless scripts to automate my export pipeline. It's great, as all commands are available and when running headlessly, you can even save/load which fail in normal GUI mode.

However, I noticed that errors are completely silent (unlike GUI mode, which shows error messages). How can I catch PICO-8 errors from within the .p8 script or, as last resort, from the bash script I'm using to pico8 -x the .p8 script?

The most commonly failing commands are save("game.p8.png") when the PNG is too big, and export("game.bin/html") where we're missing an icon or label. When that happens, I must re-run the commands in GUI mode and inspect every error one-by-one. Besides, the commands I manually type are generally slightly different from the script ones (which are very complete with all options) so I'm not exactly sure if I'm reproducing it right.

My suggestions:
a. access error code of last native PICO-8 command (like, save, load and export) with a simple function (e.g. err() which returns an int). We could go further by getting the error message too (e.g. errmsg())
b. make all commands return a success bool. Currently, only load does this (undocumented). save and export return nil so I cannot verify success of the operation.
c. Less convenient, but let a bash/shell script running pico8 -x check the final result of the operations (1 for error, 0 for success) and read error messages in some way. This is less convenient as it doesn't pinpoint where the error occurred in the script. Currently, pico-8 cartridge..p8 -export "... game.bin" does print error messages; but it doesn't return 1 on failure (e.g. missing label) so we cannot check that in a bash script.
d. An option for pico8 -x that prints all native command errors as we would see them in the GUI (load -> "could not load" or better "could not load [filename]", export -> "no icon found, please capture a label or use -i")

P#110648 2022-04-21 14:22


As noted in one comment on https://pico-8.fandom.com/wiki/Printh and as I myself experienced during my project, printh seems not to support hyphen/minus character - in file names.

Since the only known special filename is "@clip", I don't see why.

It bothers me in my project as my game title is hyphenated and I use it for the logs. Fortunately, the logs are placed in a local folder containing the .p8, so I can name it whatever I want and it won't be mixed up with logs from my other projects.

Workaround: underscore _ and even space work, so use them instead.

Is there a rationale for preventing usage of this character? If not, could it be allowed in a future version of PICO-8?

I understand that some characters like colon : are forbidden due to system limitations, but hyphen - is supported on all systems I know of.

On the other side, slash / is a risky one but surprisingly, printh does not error directly when you use it. Instead, it will try to write the file, then fail with "printh: could not write file" as the system prevents the operation. Even Windows doesn't accept forward slashes, so I'm not sure why printh would wait for a low-level error on this one.

P#110286 2022-04-15 19:32


I noticed that load() supports filenames without extensions and will pick filebasename.p8 if it exists, else filebasename.p8.png (I didn't exactly try the priority order, as there is only either a .p8 or .p8.png in my distributed game folder).

However, reload() won't work with a file basename. It needs the .p8 or .p8.png extension. When distributing a game both for p8 and png though, you don't know in advance what you'll need, and would need to create two versions of the code, one reloading files in .p8, the other reloading files in .p8.png, for both versions to work. I actually wrote a string replacement script to support them both!

It would be nice if reload() supported file basename like load() for this reason. I don't know if this is a bug report or a suggestion, but I would expect reload() to be consistent with load() so I opened this thread in the bug section nevertheless.

P#96060 2021-08-15 18:29


Hey, I've just triggered with profiling overlay you see in the top-right of the attached screenshot, and I cannot find how I enabled it. It would be nice to use since I was using my own custom profiler (printing stats) so far but this one has a nice graph and shows clearly when I drop to 30 FPS.

It really is an overlay, not part of the game. As a matter of fact, taking a screen capture with F1 with ignore the overlay!

As a hint, I was reload my game with a script that sends keystrokes Ctrl+R to the PICO-8 window. Maybe I pressed something else, or the keystrokes were not correctly registered at that time, and it triggered another shortcut; which I cannot find at all in the documentation!

Note: this happened in the editor, not the runtime binary version of PICO-8.

P#95654 2021-08-04 18:26 ( Edited 2021-08-04 18:27)


Calling this in editor:


will crash the editor instantly.

Opening pico8 in terminal, or running a script in headless mode containing this line (pico8 -x script.p8), allows us to see:

Segmentation fault (core dumped)

Interestingly, mkdir() (no argument at all) just shows the help, like mkdir without brackets at all.

P#86297 2021-01-09 17:58 ( Edited 2021-01-21 18:23)


I noticed that the screenshot captures done with F1 from the editor contain a black color #020408, while a system screenshot will show that PICO-8 uses #000000 while running.

This caused some issues as I was editing screen captures, Aseprite noticed that the color didn't match the black color from the PICO-8 palette, and operations like color replacement failed.

This may have been the case from the start, and I have no idea how many times I used screenshots in my editing process, possibly mixing "good" and "bad" black together. As they are not distinguishable with bare eyes this may be a problem for later (e.g. I try to fill an area with color bucket but it only colors a small area).

I could only find one occurrence of this color, on the PICO-8 Wikipedia talk: https://en.wikipedia.org/wiki/Talk:Pico-8

Apparently the old page was mentioning #020408 for black. Either it was the old color, or the author checked the colors from an F1 screenshot (and I would have done the same!).

I don't know if other colors are different during screenshot.
I don't know if it's intentional for web export, human eye convenience or anything, or if it's just an old value that was not updated.

P#86291 2021-01-09 15:20


Sometimes your cartridge fits in the token count, but not the characters count / compressed size, so you can't export it until you reduce the number of characters in the cartridge. You'd also like to keep comments, meaningful variable names and even debug code where you can, in case you're gonna continue working on the code.

One way to do this is to use a build pipeline:

  • copy your source file(s) to an intermediate directory
  • process file(s) to reduce code size
  • output final cartridge

If you use picotool or work with compiled languages, you should be familiar with that process. It may sound a bit overkill for PICO-8, but is very useful if you're stuck in the case mentioned above.

This is what I do when working with my custom framework pico-boots, but while I don't think many devs would be interested in using a complete framework for PICO-8 written by somebody else, they may be interested in the individual processing steps described below. You can always refer to pico-boots' repository for implementation details.

Note that I use picotool to build my cartridge from multiple sources, but the techniques I use apply to single files too. For those also using picotool, I'll explain for which Step the processing should be applied: on individual sources (pre-processing) or on the output cartridge (post-processing).


  • Comment stripping
  • Minification
  • Debug code stripping
    • Multi-line
    • Single-line
  • Going further

Comment stripping

Probably the most obvious, you'll want to remove single line ("--") and block ("--[[ ]]") comments.

I used to do it manually; now the minification step does it for me, so I won't dwell on it. However, if you already use very short variable names but have lots of comments, it's worth trying comment stripping alone.

Single line comment stripping is easy to implement in a file processing script, block comment may be a bit trickier.

Step: pre-processing or post-processing


Code minification mainly consists in variable/key name shortening, space trimming and comment stripping. You can whatever does the job. However, note that minifiers are written for pure Lua and may not like PICO-8 shortcuts such as single-line

if(cond) effect


a += 5

So you may want to expand those into pure Lua statements, manually or inside your pipeline (don't worry though, minification will often more than make up for the loss of space).

If you use picotool, note that it generates a single-line "if(cond) effect" during the build. So you'll need to expand that in your pipeline post-processing, just before minification.

Personally, I use a custom branch in my fork of luamin. It's basically Luamin plus a few features/fixes:

  • option "--minify-level" for aggressive member minification: useful if you want to minify even attribute and method names (requires extra care!)
  • option "--newline-separator" for partial newline preservation for easier debugging: otherwise the code is a veeery long line and error messages just tell you "assert on line 1" (note that there is only a newline where luamin would put a ';' without the option, so clauses ending with brackets, like function definitions, will still chain into longer lines)
  • works outside the terminal (e.g. in Sublime Text)
    (more info on minification and npm in pico-boots README)

I know that picotool has a --lua-minify option, but it was too aggressive for me (it minifies even "__call" which breaks metatable logic).

Step: pre-processing or post-processing (but if using aggressive minification, post-processing only so member names are minified the same way across the cartridge)


function demo_app.instantiate_gamestates() -- override
  return {main_menu(), debug_demo(), input_demo(), render_demo()}

function demo_app.on_pre_start() -- override

function demo_app.on_post_start() -- override
  -- enable mouse devkit

function demo_app.on_reset() -- override


function n.o()return{i(),j(),k(),l()}end
function n.p()end
function n.q()g:r(true)h:s(m.t.u)end
function n.v()h:s(nil)end

Implementation example

There is not much to do since it's already in the Luamin package, which you can get using "npm install/update" (see package.json).

However, if you're working on an actual .p8 cartridge rather than pure Lua code, you may want to extract the lua section, minify it and reinject it back. minify.py does precisely that (and also expands single-line "if(cond) effect").

Possible improvement on luamin for PICO-8

Only use lower characters to generate minified identifiers (see IDENTIFIER_PARTS in luamin.js). Otherwise, since PICO-8 auto-lowers characters when opening code in the integrated editor, it may cause variable conflicts ("Ab" becomes "ab" which may be another variable in scope) or even prevent cartridge from running ("Do" becomes "do" which is a keyword).

Debug code stripping


You can use whatever you want to flag debug code and remove it for the release build. However, you need some way to tell your pipeline that you are making a debug vs a release build.

On my projects, I use generic symbol-based preprocessing similar to the one in C# (or C++), but very much simplified. You define a set of symbols for your current build config (e.g. debug config defines ["assert", "log"], release config defines nothing). Then you surround parts you don't want in some build configs with special markers:

-- will be stripped in release
#if log
printh("Player hit!")
printh("Remaining HP: "..player.hp)

During build, any code surrounded by undefined symbols is stripped.

Of course, if you don't need one symbol per debug feature like me, you can just define "debug" for the debug config and surround all your debug code with "#if debug" and "#endif".

Step: pre-processing or post-processing, but recommend pre-processing to make core build faster, as there would be less code to assemble


#if assert
assert(damage > 0)

player.hp = player.hp - damage

#if log
printh("Player hit!")
printh("Remaining HP: "..player.hp)

becomes in release config:

player.hp = player.hp - damage


For single-line, you can write a simple parser that strips line calling certain functions.

For instance, I strip all single-line "assert(...)" calls if the "assert" symbol is not defined. It means that in the example above, I don't even need the "#if assert" anymore. It's very convenient when you have many logs and assertions. But multi-line stripping is still useful for more complex behaviors.

Step: same as multi-line


assert(damage > 0)

player.hp = player.hp - damage

log("Player hit!")
log("Remaining HP: "..player.hp)

becomes in release config:

player.hp = player.hp - damage

Implementation example

See both multi-line and single-line stripping in preprocess.py

It also contains a reversed stripping tag "--[[#pico8" and "--#pico8]]" that comments out the code until it is built into a cartridge; but that's only useful if you run unit tests in pure Lua.

Going further

If your game has a lot of text, string compression is your next step. You can make your own, or check out a tool like p8advent (also see post which mentions alternative).

Also, if you're interested in the full build pipeline I use, check out the generic build script and the demo project script using the former.

P#72970 2020-02-10 20:48 ( Edited 2020-02-10 21:01)


When printing with no coordinate arguments, text is printed on the next line after the last prompt position. In addition, when the bottom of the screen is reached, all previous graphics are moved upward.

It is also possible to print multiple lines at once, and when printed at the bottom, it will also move all previous graphics from multiple lines.

However, the lines after the 1st one won't be printed correctly. The 2nd line's bottom half will be hidden, and the lines after that will be invisible.


  1. Start PICO-8
  2. Enter print("1\n2\n3")
  3. Repeat until prompt reaches the bottom of the screen
  4. Repeat. From then on, "2" will be cut and "3" will be invisible.


This also applies to Lua multiline strings using [[ ]], [==[ ]==], etc. instead of "\n".

P#60412 2018-12-28 14:47

Follow Lexaloffle:          
Generated 2024-02-21 15:20:26 | 0.081s | Q:29