Log In  

Hi All! PICO-8 0.2.1b is now up on lexaloffle, Humble, itch.io, and for PocketCHIP. This update started as a continuation of 0.2.0 bug-fixing work, but I also relaxed my position on API minimalism just enough to add some new features :D

UPDATE: 0.2.1b is now live and fixes the print() bug, and a few other things. See the changelog below for details.


You can draw ovals (technically, ellipses) both when running a cartridge, and when using the shape tools in the graphics/map editors. Ovals are specified by their boundary rectangle, and follow the usual draw state rules.


function _draw()
 for i=0,31/32,1/32 do
  local x=64+cos(i+t()/8)*48
  local y=64+sin(i+t()/8)*44
  local w=8+cos(i*2+t()/2)*6
  local h=8+sin(i*3+t()/2)*6


 print("pico-8 0.2.1",40,62,13)

Serial I/O

To make it easier to set up workflows for getting data in and out of carts during development, some new serial() channels are available. You can turn a file on your host machine into a binary stream, or drag and drop it into the running cartridge to do the same. From the manual:

Additional channels are available for bytestreams to and from the host operating system.
These are intended to be most useful for UNIX-like environments while developing toolchains,
and are not available while running a BBS or exported cart. Maximum transfer rate in all 
cases is 64k/sec (blocks cpu).

        0x800    dropped file   //  stat(120) returns TRUE when data available
        0x802    dropped image  //  stat(121) returns TRUE when data available
        0x804    stdin
        0x805    stdout
        0x806    file specifed with: pico8 -i filename
        0x807    file specifed with: pico8 -o filename

Image files dropped into PICO-8 show up on channel 0x802 as a bytestream:
The first 4 bytes are the image's width and height (2 bytes each little-endian, like PEEK2),
followed by the image in reading order, one byte per pixel, colour-fitted to the display 
palette at the time the file was dropped.

Drag and Drop

On a related note, you can also now drop .p8.png cartridges into PICO-8 to open them. If there is a cartridge with unsaved changes, it will prompt before continuing. You can also drop .png files into the spritesheet, by first selecting the sprite that should be the top-left corner location.

API Changes

add() now comes with an optional 3rd parameter: an integer that specifies the location in the table that the new value should be inserted at. Similarly, a new variation of del() is available: deli(tbl, index) ("delete by index") allows deleting from a given location in the table rather than by value.

split() is also new. It complements the common strategy of storing data as strings. From the manual:

split str [separator] [convert_numbers] 

Split a string into a table of elements delimited by the given separator (defaults to ",").
When convert_numbers is true, numerical tokens are stored as numbers (defaults to true).
Empty elements are stored as empty strings.

split("1,2,3")               -- returns {1,2,3}
split("one:two:3",":",false) -- returns {"one","two","3"}
split("1,,2,")               -- returns {1,"",2,""}

Binary Storage

It is now also more efficient to store 8-bit binary data in the source code section, by encoding it as a binary string. The .p8.png format stores uncompressable sequences as a raw block of data, effectively allowing cart authors to choose how much of the code section to trade for raw binary storage.

Binary strings can be encoded by escaping characters that can't appear in the source code. For example:
0 should become "\000" (or "\0" when not followed by another number), etc. To make this easier, previously invisible characters C1..C15 have font entries, and also unicode replacements when copying and pasting. I'm working on a snippet for converting between data strings and raw binary data, to make this process easier. UPDATE: here's the snippet.

HTML Touch Support under iOS

Touch controls for HTML exports is now a little smoother, and works when running from inside an iFrame (including itch.io game pages). I removed the mobile buttons menu by default (the buttons along the top: fullscreen, sound, close) as they aren't very useful and are messy, but they can be turned back on in the options near the top of the exported html.

Changelog // added 0.2.1b

There are many other bug fixes in this update, but I haven't gotten around to replying to the BBS threads yet. For now, please check the complete changelog:


Added: split(str,"") splits by single characters
Updated: Tower of Archeos 1.1 via INSTALL GAMES
Fixed: print(num,x,y) always prints numbers num in hexidecimal
Fixed: .p8.png decoder can enter an infinite loop (caused exports to freeze on boot)
Fixed: Can't save screenshot/gif when running a BBS cart with illegal characters in title.
Fixed: INSTALL_GAMES is broken
Fixed: Mouse is broken in HTML exports


Added: oval() ovalfill() split()
Added: circle drawing tool is now an oval tool (hold shift for circle)
Added: hold shift with line tool to snap to 22.5 degree angles from origin (0:1, 1:1, 2:1 gradients)
Added: serial() channels for stdin,stdout
Added: raw binary and image files dropped in to PICO-8 also become byte streams readable w/ serial()
Added: add(tbl, val, index) -- insert val into table at index
Added: deli(tbl, index) -- delete element from table by index (index defaults to last element)
Added: show progress while exporting binaries (can be slow now that generating zip files)
Added: -e to add an extra file to exported binaries zip files // export -e manual.txt foo.bin
Added: RESET command to reset the runtime / draw state
Added: drag and drop cartridges into PICO-8 window to load them
Added: hash stored in .p8.png so that cartridges corrupted by image quantization can show a specific error
Added: raw data blocks in compressed code format (useful for storing long binary strings efficiently)
Added: clip(x,y,w,h,true): 5th parameter indicates that the clipping region should be clipped by the old one
Added: -export switch can be used to convert .p8 files to .p8.png from commandline. // pico8 foo.p8 -export foo.p8.png
Added: extcmd("screen",scale) and extcmd("video",scale) to override the default scale (e.g. scale 2 means 256x256)
Added: printh(str, filename, overwrite, save_to_desktop) -- 4th parameter to save output file to desktop
Changed: add(), del() no longer implemented with Lua snippet; lower cpu cost.
Changed: line(),rect() cost the same as rectfill() when drawing equivalent shapes
Changed: all drawing operations in sprite editor now observe fill pattern state
Changed: numbers can be immediately followed by identifiers (a=1b=2) // lexaloffle.com/bbs/?tid=38038
Changed: Sprite editor shows only active area after shift-selecting sprites
Changed: copy/paste in the code editor treats uppercase ascii characters as puny font only when puny mode (ctrl+p) enabled
Changed: C0 Controls characters (except for 0x0,0x9,0xa,0xd) encoded in .p8 / clipboard with unicode replacements
Changed: stat(4) converts characters to PICO-8 format (P -> puny p, hiragana unicode -> single character etc.)
Changed: serial() returns number of bytes processed (1/8ths included for partial bytes)
Changed: IMPORT SPRITESHEET.PNG now uses the current sprite as the destination coordinate instead of 0,0.
Changed: Standardized name of the display palette to "display palette" (was sometimes referred to as "screen palette").
Changed: tostr() returns nil (used to return "[nil]")
Changed: don't need to set bit 0x40 at address 0x5f2c to use secondary palette.
Improved: exported binary's data.pod file 90% smaller (~870k -> ~85k)
Fixed: pack(...).n is shifted right 16 bits
Fixed: ctrl-r doesn't reload external changes for carts which are over compressed code capacity
Fixed: false positives when detecting external changes for some older cart versions
Fixed: .p8.png carts saved with dense code (compressed size > raw size, including very small carts) stores junk
Fixed: error message duplication when loading future version of .p8.png carts
Fixed: Illegal colours can enter spritesheet via serach-replace after setting with color()
Fixed: Preprocessor: "foo():a().x+=1" "a=b[1]c+=1"
Fixed: hex numbers written with punyfont characters breaks syntax high-lighting
Fixed: shift+- in sprite editor jumps too vertically when zoomed in
Fixed: clicking a note in sfx editor creates a selection (-> backspace clears without moving rows)
Fixed: print()/printh()/stop() doesn't respect __tostring metatable method (regression)
Fixed: time() and btnp() speed changes after stopping program, typing a command and then resuming.
Fixed: phantom drag & drop events sent to unused music channels causing them to occasionally unmute themselves
Fixed: undo after moving sprites in map mode only undoes the changes to the map and not the spritesheet.
Fixed: inconsistent token counting for negative or bnot'ed numbers https://www.lexaloffle.com/bbs/?tid=38344
Fixed: Crash when INSTALL_GAMES / INSTALL_DEMOS without a writeable disk
Fixed: stat(4) (clipboard contents) does not convert unicode to corresponding glyphs
Fixed: (MacOS) Using discrete GPU ~ drains battery. Now using integrated GPU when available.
Fixed: screensaver is blocked while PICO-8 is running (needed to set SDL_HINT_VIDEO_ALLOW_SCREENSAVER: "1")
Fixed: screen glitches after running for 25 days
Fixed: (HTML Exports) touch controls not registering when running under iOS from an iframe (e.g. on an itch.io page)
Fixed: (HTML Exports) tap and hold brings up the select menu under iOS
Fixed: (HTML Exports) button blocked by canvas when overlapping on small screens

P#78861 2020-07-04 00:59 ( Edited 2020-07-05 21:37)

Nice! Love all of these and my urge to make a faux-textmode system for PICO-8 is rising.

It looks like 0x09, 0x0a and 0x0d are missing glyphs for me though? Running the amd64 version on Ubuntu 20.04.

P#78877 2020-07-04 07:54

@oakreef these are tab, carriage return, and line feed. They usually have no printable representation.

P#78879 2020-07-04 08:20

Ah yes of course. Zep's post made me think there was going to be non-whitespace characters for all the control characters but I didn't think about it very hard :P

testing it out print("test\10test") works just like print("test\ntest") now

now if only print("\7") also played a bell sound

P#78880 2020-07-04 09:06 ( Edited 2020-07-04 09:14)

I hadn't even finished flashing the unofficial API for 0.2.0h yet...

P#78883 2020-07-04 11:17


Thanks for fixing that 25-day video corruption bug. I was looking at some windows I had open earlier, after a long absence from programming mode, and I couldn't even read the source code to see if I should save it or not. That's a relief. :)

Also thanks for the indexed add/del stuff. I also hope that making them C-side helps perf a bit on embedded hardware.

P#78889 2020-07-04 11:57


Quick request:

Since a blank token string would otherwise be meaningless, would it be okay to request that split("abc","") result in {"a","b","c"}? Effectively say that the zero-width gap between characters is a delimiter. :)

P#78890 2020-07-04 12:02 ( Edited 2020-07-04 12:07)

Updated to v0.2.1b to fix some urgent bugs, and to add @Felice's suggestion (very handy!)

Added: split(str,"") splits by single characters
Updated: Tower of Archeos 1.1 via INSTALL_GAMES
Fixed: print(num,x,y) always prints numbers num in hexidecimal
Fixed: .p8.png decoder can enter an infinite loop (caused exports to freeze on boot)
Fixed: Can't save screenshot/gif when running a BBS cart with illegal characters in title.
Fixed: INSTALL_GAMES is broken
Fixed: Mouse is broken in HTML exports

P#78912 2020-07-05 00:51 ( Edited 2020-07-05 00:52)

@zep Awesome, can't wait to try this out! But I'm not entirely sure about split(str,""), if you're going to have a way to split by a single character, why not have a way to split by two, or three, or etc? As far as I can tell, split(str,num) wouldn't conflict with anything, but I could be wrong. Anyway, thanks for all your hard work!

P#78916 2020-07-05 01:45

@Spaz48 I second this, personally I think even split("hello",1) is more readable and as was said allows the convention of splitting things up by groups of more than one character.

P#78919 2020-07-05 03:31

@zep @Spaz48

I agree with Spaz, that would be a better implementation. If the second argument's type is "number", split records at those intervals. I wish I'd thought of that in the first place, so you wouldn't have wasted your time on my method.

P#78921 2020-07-05 03:54 ( Edited 2020-07-05 03:56)

Lovely stuff, thank you very much!

P#78933 2020-07-05 16:02

@sol @Spaz48 @Felice
ok, done! I'll add this change the next time the cart format version is bumped, which depends on what other version-breaking issues show up.

@ilkke Thanks, and my pleasure!

P#78955 2020-07-05 22:44 ( Edited 2020-07-07 03:02)

When I try to import a PNG sheet, it is always loaded shifted one column to the right.
Is this a bug or am I doing something wrong?

P#78968 2020-07-06 06:18

OK turns out I had the second sprite selected and it would import with an X offset.
Weird thing is, the second sprite was selected by default :\

P#78972 2020-07-06 07:09

Thanks, @zep.

Is the index-out-of-bounds error the intended behavior when feeding add() with an index that's greater than the table size?
(as opposed to appending to the end instead)

P#79045 2020-07-08 09:00 ( Edited 2020-07-08 09:00)


You actually can add past the size of the table, but only by 1 entry, which matches the default behavior of appending. You basically have to be growing the sequence without any gaps (nil/uninitialized values), or else it's not a proper Lua sequence, whose definition is a little stricter than what we typically think of as an array.

I won't speak for zep but I would definitely assume that's the intended behavior.

P#79051 2020-07-08 10:04 ( Edited 2020-07-08 10:12)

Sorry for not being clear. I meant to ask if it wasn't meant to revert to appending instead of inserting if an index out of range was given. Then again, I suppose I can't really think of a sensible scenario where that would happen anyway. Thanks for clarifying.

On a side note, is there an existing function in PICO-8 that counts all items in a table including non-indexed keys? Or do we still tally through a for-loop with pairs(tbl) for that? Maybe count() will eventually be re-implemented as that?

P#79052 2020-07-08 10:45

@zep Small UI bug I found. The angle snap for line drawing has a small glitch. Seem most visible in 45° X+/Y- and X-/Y+ directions. See gif:

P#80019 2020-07-28 06:49 ( Edited 2020-07-28 06:49)

@zep Found a small issue with PRINTH saving to desktop. It doesn't seem to work if you specify a file extension. So this works correctly:


Whereas this won't work:


Instead, "test.txt" will be written to the cart directiory. Not sure if undocumented safety behavior or intentional?

Only tested it on Windows so far.

P#80020 2020-07-28 07:01

@zep to next version...how about adding a new 9th sound wave like the unique Atari 2600 noise/motor wave tone.

P#82676 2020-10-07 16:08 ( Edited 2020-10-07 16:09)

Could the png exporter automatically add the devkit mouse/keyboard logo on the cartridge if used ?

P#83427 2020-10-28 16:42


P#83437 2020-10-29 01:53

@zep Critical bug :
Disconnecting then reconnecting a controller twice will break the controller support.

On pocket devices it mean that PICO-8 will effectively lock the device if you put it it sleep mode twice in the same session.

I reproduced the behavior successfully on the windows, linux and raspberry pi version.

P#83574 2020-11-01 14:01 ( Edited 2020-11-01 14:09)


While I would certainly hope @zep would see your report in his own thread, ideally you should post bug reports in the support/bugs forum so they can be tracked and you can eventually mark your post as "fixed" (one hopes).

P#84005 2020-11-08 20:56 ( Edited 2020-11-08 20:57)

Will do, thanks.

P#84025 2020-11-09 08:26

Zep, I can't update versions. Even though I got the game on Lexaloffle, the site is denying it. It says something like "No products found.", and I thought I'd ask you. Can you help?

P#86992 2021-01-30 23:33

[Please log in to post a comment]