Log In  

PICO-8 0.2.3 is now up on lexaloffle, Humble, itch.io, and for PocketCHIP.

This update is focused on resolving runtime issues, with a couple of small but handy features thrown in for good measure:

Lucky Draw

To grab 32 random cartridges from the BBS, there is now a 'Lucky Draw' list in SPLORE. It is more likely to select cartridges that have more stars, but even unrated carts have a decent chance of appearing. Perhaps this will be a way to unearth some undiscovered gems, or just to find something new to play without scrolling back through several years of carts.

The list is cached, so it only changes every 2 minutes or so. But you can keep paging through the list to get new items forever.

Live Token / Character Count

Select some text in the code editor to view how many characters or tokens are contained within.

Shorthand Print Statements

The shorthand version of print (?"hello") used to require no preceding statement be on the same line, but you can now mix it with things like the shorthand IF statement, as long as it is the last thing on the line:

IF (PGET(X,Y)>0) HIT=1 ?"\ADAF"

Thanks to @Liquidream for suggesting this. It should be handy for things like tweetcarts and PICO-1K Jam (which coincidentally is also organized by Liquidream!)

New Manual

The PICO-8 Manual is still a work in progress, but it went through a large update last month, with new formatting, dark mode, linkable headers, and more printer-friendly. You can find it on the resources page:


The changelog has been separated into a text file here:


The pico-8.txt included in the distributables has changed to a short version that links to the online manual, but later on I'll bring back the full ascii version and keep it in sync with the html version.

.P8.ROM Format

This isn't a very useful feature unless you are manipulating PICO-8 cartridges with external tools, but it has always irked me that it is not possible to write 32k of PICO-8 cartridge to a file that is 32k!



Large Numbers

tostr() and tonum() now take format flags that make it easier to deal with large numbers.


PICO-8's numbers are all 16:16 fixed point, which basically means that every number is internally stored as a large 32-bit integer that is divided by 65536 to get the value in Lua. So for example, if you want to use that large integer for something like scores that need to go above the usual maximum of 32767, bit 0x2 can be used to display it in the raw integer form:

?TOSTR(3, 0x2)
?TONUM("196608", 0x2)

To add 10 points to a score stored in this way, you'd need to shift it right 16 bits:

SCORE += 10>>16

CPU Counting

0.2.3 contains two changed to cpu costs which does not effect many cartridges:

  1. In 0.2.2c, it was cheaper to write "local a=0+b" than "local a=b" and also "local a=0-b" than "local a=-b". This is because the Lua vm instruction for addition was cheaper than local assignment (OP_MOVE), and for unary minus (OP_UNM). The best solution I could find was simply to reduce the cost of those two instructions to 1 cycle instead of 2, resulting in a slight speed increase in some cases.

  2. peeking or poking multiple values in 0.2.2c did not cost any additional cpu, so doing something like: poke4(0,peek4(0x1000,0x1000)) was completely free! The same line in 0.2.3 now costs the same as doing an equivalent memcpy.

Future Plans

Next up for PICO-8 is a 64-bit Raspberry Pi Build, and also a preview of a web version that is also handy for running PICO-8 on Chromebooks. I'm also still working on online scores, which required developing some bespoke infrastructure that is optimised for PICO-8's usage patterns, and can handle traffic from exported carts without needing to start charging for PICONET subscriptions or anything like that. I'll test it next month with the release of https://www.doodlemud.com, which is a multiplayer drawing game designed to battletest PICO-8's backend before exposing it to precious cartridge data!



Added: Lucky draw list in splore -- gives a random selection of carts
Added: load/save carts in .p8.rom format (raw binary 32k block)
Added: tostr(), tonum() take format_flags parameter to convert to and from 32-bit signed ints
Added: ord(str, pos, num) returns num results starting from character at pos (similar to peek)
Added: FOLDER takes an optional parameter to open other host directories: BACKUPS | DESKTOP | CONFIG | BBS
Added: Live character / token count of selected text shown at bottom right of code editor
Changed: Removed collaboration list from splore (can still search for sub:collab)
Changed: 0x808 audio has a slight lpf filter on it by default // turn off by setting bit 0x20 at 0x5f36
Changed: tonum(boolean_value) returns 1 or 0 instead of nil
Changed: cursor CR x position set only by print(str,x,y) or cursor(), but not by print(str) (0x5f24)
Changed: character wrap is on by default when using print(str)
Changed: force-pause-menu hold duration is 500ms instead of 300ms to prevent accidentally triggering it
Changed: default gif length for new install is 16 seconds
Changed: ? shorthand can be used anywhere on a line e.g. if (true) ?"yes"
Changed: allow while/if shorthand with no statement, using colon separator: WHILE(BTN()==0);
Changed: added warning to fullscreen_method 2 in config.txt (gives erratic behaviour under some drivers)
Changed: cheaper OP_MOVE, OP_UNM lua vm instructions so that e.g. "local a=0-b" is not faster than "local a=-b"
Fixed: peek() / poke() do not charge extra cpu when reading or writing multiple values
Fixed: fget(n) returns junk when n is out of range (0..255); should return 0 in that case
Fixed: dropped .PNG files not detected as images when filename uses uppercase extension
Fixed: line()/tline() illegal writes caused by faulty clipping when (x1-x0) or (y1-y0) >= 0x8000
Fixed: -accept_future 1 only worked with .p8.png files; now also applies to .p8
Fixed: ?"\a7f" does not play f (happens only when f is the first note)
Fixed: abs(0x8000) return 0x0.0001 (should be 0x7fff.ffff)
Fixed: parameter string (stat(6)) is dropped when passed via RUN command
Fixed: preprocessing of form: "x += x<0 and -1 or 1" broken for operators <, >
Fixed: tab not accepted as whitespace for some preprocessor operations
Fixed: stat(1) wraps around when cpu is >= 2.0 (regression in 0.2.2)
Fixed: pressing SHIFT+ENTER on "local function foo()" or after "if (..) else" doesn't insert "end"
Fixed: pal() does not reset secondary palette to system default
Fixed: 0x808 audio does not respect pausing / volume / is not recorded with extcmd("audio_rec")
Fixed: 'h' pressed in sprite editor toggles hex mode in map editor
Fixed: After pressing shift-tab to toggle 128x128 map view, active draw area is still only 128x112
Fixed: Attempt to navigate to non-existant tab after running: function _update60() _update60=nil end
Fixed: stat(101) not returning cart id when running from BBS web player
Fixed: print() wrapping + scrolling; e.g. from commandline: while(true) print(chr(254+rnd(2)).."\0")
Fixed: integer divide assignment operator (\=) costs 2 tokens instead of 1

P#96897 2021-09-06 21:26


Next up for PICO-8 is a 64-bit Raspberry Pi Build

Oh my oh my, I am very much excited for this!

P#96972 2021-09-06 21:38

very glad that the 0x808 audio lowpass filter can be optionally turned off, i actually like the crunch of the high frequency artifacts

P#96976 2021-09-06 22:43

Thanks for another great update @zep! 🤓
Many thanks especially for the quality-of-life improvements for #Pico1k jam and #Tweetcart's, etc. 🙏

P#96987 2021-09-07 04:13

A Webversion !!! YESS!!! This way i can use Pico8 soon on my Samsung Tablet TabS6 and Keyboard on the go!

P#96988 2021-09-07 05:15

@StinkerB06 thanks (as if all my games did not use reload... :facepalm: )

.rom question - is the goal to have 32k data carts or only as a convenient way to generate carts (without the $ù@@## sfx/music format?)

correction: reload does work with p8.rom files
feature request: ability to reload the full 32k of a p8.rom files (now possible with 0x8000 region)
(should multicart games be limited by size rather than number of files?)

P#96990 2021-09-07 11:20 ( Edited 2021-09-09 05:58)

@freds72 .p8.rom is only supported by LOAD/SAVE.

I was imagining only using it for authoring single carts, but yes -- I suppose it should really be treated the same by CSTORE/RELOAD. I'll wishlist that for the next update. I'd like to keep the code vs. data section rules the same though (as oddball as they are), so it won't be a way to get the full 32k as readable data.

P#96992 2021-09-07 12:18


I have a problem with some code that worked up to the previous version:

  if stat(30)  then
      local k = ord(stat(31))

ord(stat(31)) always returns null but was returning the correct value before.

It works if changed to:

  if stat(30)  then
      local k = stat(31) k=ord(k)
P#97001 2021-09-07 14:03

Hi @zep, thanks for the update!

Could you perhaps support .p8.rom via commandline "-export" like .p8.png?

I'm new to PICO-8 and I find SAVE vs EXPORT slightly confusing, eg. you use SAVE to write a .p8.png cartridge, but -export to do the same via commandline.

Just a suggestion! Useful for scripting and external tools.

P#97012 2021-09-07 17:08

So excited for the web version!! Thank you zep!

P#97019 2021-09-07 20:26

A New Version!!!Tanks @zep

P#97050 2021-09-08 11:37

@freds72 It looks like you messed up the order of the arguments to reload().

P#97079 2021-09-08 21:59

Touk> it used to be clearer: we used 'save' (in the console) for the cart itself (P8 or P8PNG format), and 'export' for bits like the spritesheet or a music track.
HTML and native executables are also 'export'; in a way it makes sense that it’s not 'save' because this is compiling to a format that can be distributed but not edited, so 'save' is for pure carts and 'export' for derived things.

Now on the command line, there is no option for saving, so all operationes use '-export', even though it can be used to create pure carts in P8PNG or P8ROM format that can be loaded and edited again!

P#97087 2021-09-09 00:31 ( Edited 2021-09-09 00:32)

Looking forward to the online hiscores feature. It will give a new lease of life to a lot of existing Pico-8 games.

P#97189 2021-09-11 10:04

@zep program crashes (segfaults) with Scrap Boy

SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x1b}

P#97513 2021-09-19 01:01

Hi to all.
I have an issue with this new version. the PRINT function works differently from previous version in handling '\n' character.
In previous version when PRINT encounter '\n' character the line start from the beginning of the previous, now allway starts from 0. I post this screenshot as example.

This works with 0.2.2c version but the same code give me this issue with 0.2.3.
Thanks to @zep for pico-8 that allways remain a great program :)

P#97545 2021-09-19 12:50 ( Edited 2021-09-19 12:53)

Yep, I found this version of Pico-8 does indeed NOT run some of my carts correctly as they ran before, @ViperSnake75.

Also if it's ideas people are after, I have several:


While a good number of these ideas have been read and implemented by @zep since my initial request of them 2-years ago, glad to be of service, there are still several I think are also good ideas still in the list not yet done.

Of course pinnacle to all of this would be the ability to compile your .P8 to native Android/cellphone format of APK or OPK as the case might be. Some of the competing Fantasy Consoles have this ability already so it is not an impossibility. Of course I would never leave Pico-8 and am always delighted to see each new version @zep produces.

P#97570 2021-09-20 01:28 ( Edited 2021-09-20 01:32)

I am so excited!

P#97578 2021-09-20 09:53

Nice! Relatively new to pico-8, so don't know about all of the bugs other people are talking about, but I'm excited to see what else @zep adds!

And yes, I've already been utilizing live token count to my benefit. Thanks!

P#97733 2021-09-24 00:07 ( Edited 2021-09-24 00:08)

Is Pico 8 and the other 2 fantasy consoles always going to be paid? Because if you can run it on the web, is it a custom link so you still have to pay for it?

P#97841 2021-09-26 08:32

Changed: cursor CR x position set only by print(str,x,y) or cursor(), but not by print(str) (0x5f24)

What's the purpose of this?

P#97915 2021-09-28 05:09

@zep Dude! All of these updates are awesome!

I'm kind of excited about P8.ROM. It might motivate me to see what sort of tools I could put into making cartridges, now that there's an easy way to export cartridge data and make it accessible to other programs. Great feature!

Also super stoked about arm64! I'm fighting for free time to dig more into the new features that keep PICO-8 exciting year after year.

Thanks for all your continued effort!

P#98027 2021-09-30 04:56

Ah the new way pal() works breaks my project. Trying to use the secondary palette but because of the "fix" to pal() I have to set the secondary palette every time I use pal()

Could pal() with no arguments continue to not reset the secondary palette? Or using pal() with just one argument, you can use to choose which to reset, the four options being transparency, secondary palette, draw palette, and the display palette?

P#98539 2021-10-12 15:11 ( Edited 2021-10-12 15:36)

I just installed pico 8 on my stock pi400 and it runs slowly. When I start pico8 -show_fps 1 the fps counter in the main boot screen is only 16 to 18!

Is it supposed to be this slow on what is basically an rpi4?

Can I do anything to make it faster? I thought it should run in 30 fps!

P#98540 2021-10-12 17:14

Also I think pico 8 should output a warning if it cannot maintain spec fps. I thought that is the whole point of the fantasy console that it has fixed spec and is the same everywhere. If you allow it to run with reduced fps, then everybody is getting a different experience.

P#98541 2021-10-12 17:15

If I run in a window on my pi400 I get almost 80 fps. If I switch to full screen it goes down to <20 fps!!! How can the simple act of scaling which should be possible with zero overhead using the gpu, habe such an enormous and embarrassingly bad effect on fps?

P#98542 2021-10-12 17:30

I found the fix for slow fps on pi400 in full screen mode:
It was posted here: https://www.lexaloffle.com/bbs/?pid=93083#p

It works. The fix is to start “sudo raspi-config”, go to advanced and set the gl driver to “legacy”, now I get 80 fps in fullscreen.

P#98543 2021-10-12 17:42 ( Edited 2021-10-12 17:42)

Another problem: screen tearing in lots of games. Is there no vsync on/off option? How can people stand this?

P#98544 2021-10-12 17:50

@rsn8887 not sure what is going on with your pi, but it runs perfectly on my Raspberry Pi 3 Model B, i.e. 60 FPS with vsync in fullscreen 1920x1080 (running in retropie)

anyway you should start a new thread, unless this is directly related to this specific release

P#98547 2021-10-12 18:04

i found a bug
whenever you ctrl-x patterns in the pattern editor, the patterns appear to be empty but music() thinks that they're filled with sfx's 1-4

P#98797 2021-10-17 18:33

thank you oh thank you @zep

P#99563 2021-11-03 18:41

Thought I would add this, @zep. Current Pico-8 does not show the standard touch keyboard when requested on either my Android or cellphone's P8. I didn't know if there was a way to fix this.

Adding LOAD/SAVE state would be epic I believe. If that "state" could be converted and read/pasted to and from clipboard, pointing out errors in existing games would also be that much easier. No need to post a picture. Just get the game prior to the error. SAVE STATE. Copy that save state as HEX to your message. Then when the message is read later, if that "state" is clicked it jumps to that game right at the exact position they saved it at.

Then it's a simple matter to take a step forward or what have you causing the error to appear - greatly increasing pointing out bugs and errors in existing carts.

LOAD/SAVE state would be good for games on the go. There are a great many carts for Pico-8 that take more than an easy hour to play and LOAD/SAVE state for those would be invaluable.

P#100059 2021-11-13 19:46 ( Edited 2021-11-13 20:42)

Thanks Zep! Does the (not) PICONET thing provide a backend for online multiplayer PICO-8 games? Or is it more a replacement to BBS as the backend for SPLORE, or something else? (Personally I'm hoping for the first one)

P#101265 2021-12-02 19:59

zep says he does not want to charge for an hypothetical piconet.

Online scores are supposed to have level ID, score, data (512 bytes), and (implicit parameters) game ID and user ID (from explicit login, handled by the console). They should be usable to add some online multiplayer for limited turn-based games, but not real-time or MMO. Also not a replacement for the bbs.

P#101280 2021-12-03 02:33

Very happy to see the Large Numbers functions.

P#101293 2021-12-03 06:25

no segfault with scrap boy on 0.2.4!

P#101346 2021-12-03 18:04

[Please log in to post a comment]