Log In  


I'm a prematurely-retired professional video game programmer. I have some health problems that ruin my concentration and keep me from doing the kind of programming people will pay you for. PICO-8 is really nice for me, because the limited scope of the platform tends to keep the scope of problems and solutions limited enough for my limited concentration to cope with. I don't think I'll ever manage to produce a game for PICO-8, but it sure is fun just to play with.

(If I've just handed you some ideas or advice, probably for the fifth time this week, and you're getting sick of my doing that, then I have a couple of things I should say: first of all, you should let me know, because I know unwelcome advice is annoying and I don't want to be annoying; but also, second, try not to be too irritated with me, because giving advice is the only way I still feel like I can be a productive member of the video game developer community. I mean well, I swear.)

Oh, and about the avatar... once upon a time, I chose a nice little image of Miku in glasses for my avatar, purely because it struck me as adorable. However, I kept it because I discovered it kept away those useless people who would judge a book by its cover. This is that avatar, but hand-pixeled into low-res, pico-8 palette format.

Sample code for getting dead coroutine's stack trace
by Felice

I noticed in this post that @choo-t doesn't turn into a user link.

Must be the hyphen?

P#84006 2020-11-08 20:59

I'm surprised I haven't noticed this before.

I'm not sure if this is a regression in 0.2.1b or if it got outright broken, but @zep, I swear you fixed it in the past. But in 0.2.1b pack() is always setting the table.n value to 0:

P#82428 2020-09-29 15:54 ( Edited 2020-09-29 15:57)

Hey @zep,

I know this is pretty minor, but I often find myself adding this to my programs so that I can use it in tables of functions/behaviors or other similar circumstances:

function nop() end

It'd be cool, and kind of in keeping with the half-lua/half-asm feel of PICO-8 to have a handy built-in nop() function for such occasions:

function assign_behavior(o,b,f)
  o.behavior[b] = f or nop

-- saves me having to do this elsewhere:
if(o.behavior[b]) o.behavior[b]()

I can't be sure, but I bet it might be useful for some tweetcarts too.

Seems like it'd be trivial to add as an efficient built-in C-side function as well. No need to handle args or closures or anything, just spot it and return the empty set immediately.

Anyway, I know it's about as minor a request as a person could possibly make, but still, I figured I'd at least ask. ;)

PS: Honestly, I think even vanilla Lua could use this as an actual built-in, like a pseudo-keyword. In some cases it could be as useful as nil.

P#82015 2020-09-20 13:34 ( Edited 2020-09-20 13:40)


Why does clicking the Forum link in the site ribbon always take me to the BBS > PICO-8 > Cartridges subforum?

I feel like it ought to be taking me to the BBS > PICO-8 root forum.

Even if your intent is to lead new users to the Cartridges subforum so they know it exists, it should really only do that from outside of the forum itself. Once I'm actually in the forum it feels very off to click Forum and end up with nothing but carts instead of general PICO-8 discussions. I keep doing this intuitively to return to the root forum, and then I remember I have to click the penultimate breadcrumb instead of the intuitive Forum link.

P#81840 2020-09-15 06:47 ( Edited 2020-09-16 14:06)

I saw this mentioned on Discord, so I tested it, and found it to be true. I can't think of a reason for it to be this way, so I figured it should be written up as a bug.

This code:

local c=x+y

is faster than this code:

local c=0

Here's a dead-simple test cart. Hold a button down to switch from running the first assignment per pixel to running the second. I get stat(1) = 0.7709 for c=x+y and 0.8296 for c=0, when at best they ought to be at least identical.

Cart #rutesujaju-0 | 2020-08-03 | Code ▽ | Embed ▽ | No License

P#80302 2020-08-03 10:03 ( Edited 2020-08-03 10:17)

Hey @zep,

I notice you can use the double-width katakana glyphs as identifiers, but I don't think you added the new printable single-width characters below chr(32) to the "legal chars" set for identifiers.

So I could use ta/た in a variable name, but because I need the single-width dakuten at chr(30) to make da/だ, I couldn't write a variable named, e.g., だくてん.

I figure if it's not a placeholder glyph (like the first 10 or so) and not reserved for Lua, it ought to be legal for identifiers, based on how every other glyph has been. Am I right?

I'm asking specifically because I'd like to use one particular character in place of 'self' in my code, to keep my code concise on the tiny PICO-8 screen, but I can't currently use it because it's not legal.

P#79992 2020-07-27 12:40 ( Edited 2020-07-27 12:41)

Hey @zep, not sure if this is a bug or intended, but I just realized that, in a fresh cart, sfx 0 is speed 1, while all other sfx are speed 16.

Is there some reason for this?

Seems like this is something that might lead to confusion at times. The user will probably figure out what's different pretty quickly, but still... I thought I should at least put it on your radar as a possible mistake.

As an aside, "Speed" really isn't the right term here. It should be "(note) duration", or something similar. I dunno, I don't have an audio background so there's probably a more concise term.

P#79217 2020-07-12 19:49 ( Edited 2020-07-12 19:50)

In my qsort() thread:


If you click the "Code" button on the embedded cart and scroll down to qsort() or iqsort(), you'll see a line near the top of each function that says, if l then, but the line inside the cart is actually if l<r then, so if anyone grabs the code for the function, they get a corrupted version of it.

I'm guessing this, and possibly other, reserved HTML elements are not being escaped properly...?

P#78960 2020-07-05 23:31 ( Edited 2020-07-05 23:35)

A while back I posted an implementation of an in-place dual-pivot quicksort, which is the default sort algorithm most standard libraries offer these days. I've since tweaked it a bit to save a few tokens, and today I wrote up a sample cart that does some primitive testing as an example of usage.

This includes both a general 221-token qsort(), which can accept a custom comparator function (defaults to a less-than comparator for ascending order), and a tweaked 199-token iqsort(), which inlines the comparisons as simple "<" operators, producing a smaller and faster sort in return for the sort order and index not being customizable.

See tab 0's header comment for more details.

(Side note: I realized while implementing comparators that you could sneak in shuffle functionality just by sorting with a random coin-flip comparator. Handy.)

Edit: WARNING! Do NOT grab the function from the "Code" flyout on the embedded cart below! There is a bug on the BBS right now that deletes a "<" comparison! Click the "Cart" button to download the .p8.png version!

Cart #putikutuke-0 | 2020-07-04 | Code ▽ | Embed ▽ | No License

Feel free to use it in any way you wish. I didn't invent the algorithm, and the implementation was inspired by looking at several other people's implementations (which in turn were inspired by other people's implementations, and so on). I'm not going to claim any original work here. I just did some grunt work tidying it up and applying ducktape where needed.

Please let me know ASAP if you find any bug. I think it's solid, but I know I'm not perfect.

P#78903 2020-07-04 19:20 ( Edited 2020-07-05 22:51)

Yo @zep,

This thread isn't the first time I've seen people ask about inserting values into the middle or start of arrays/sequences:


It occurs to me that a lot of carts probably have similar boilerplate code like the ins() function I wrote for that person. I think it would be nice, and better for PICO-8's host-machine performance, if there were a C-side implementation of that insert code, and I think you could do it simply by taking an optional third argument to add(), effectively implementing something like this Lua code on the C side:

function add(t,v,i)
  i=i or #t+1 -- default to extending the list
  for j=#t,i,-1 do
  return v -- return the added value for convenience

This shouldn't break any existing code, since there hasn't previously been a third arg, and the default value produces the existing behavior.

(Come to think of it, I'm not sure if you have add() on the C side or as hidden Lua. If it's not already on the C side, you might want to put it there, because it happens a lot in carts and doing it through interpreted code is definitely going to slow things down on the host hardware. Same goes for any other oft-called hidden Lua code.)

P#77570 2020-06-03 00:18 ( Edited 2020-06-03 00:23)

I have a cart used as an ongoing reminder alarm, simply showing the previous alarm, the current time, and the next alarm. It runs continuously for very long periods, e.g. weeks. Indeed, this instance has been running since the week 0.2.0i came out.

Today, for the first time I've ever noticed, the display was showing a pattern of corruption. I took a screenshot, but there was no corruption in the screenshot, so I tried saving a gif. I have my gif len set to a couple of minutes, so the result is quite long.

If you pay attention, the gif shows the parts of the pattern changing every second or three:

And yet when I saved a screenshot at the same time, there was no evidence of the corruption:

I assume this is because the PNG is saved from a point in the pipeline that comes before the corruption, while the GIF is saved after the corruption.

This means the cart itself isn't producing the corruption. It's happening somewhere in the frame presentation pipeline.

Not long after I saved these, I tried to save another GIF, and the actual executable crashed.

P#77564 2020-06-02 23:13 ( Edited 2020-06-02 23:26)

I'm surprised I haven't noticed this before today.

If I have my tabs set to two spaces, and then I cursor up or down across lines with varying numbers of tabs indenting them, the cursor column will shift left or right depending on the number of tabs at the start of the line. See this example gif for a demonstration:

Edit: as I discovered below, this also happens with double-wide glyphs:

The editor should be trying to maintain a virtual on-screen column, not an in-document column.

P#77094 2020-05-24 00:11 ( Edited 2020-08-10 21:17)

Yo @zep,

Instead of just turning the corner performance/stats graph on and off, maybe cycle through off, current size, and something closer to the full screen?

This could allow showing twice as much history on the graph, as well as showing a finer resolution of time within each tick. Right now we're only seeing 1-2 seconds (depending on _update/_update60) and the vertical resolution is very limited.

The large view might also accommodate more information that's not currently in the corner view. I'm not sure what information to choose—I'm just throwing the idea out there for you to think about.

Whatcha think?

P#77090 2020-05-23 21:50 ( Edited 2020-05-23 21:50)

Hey @zep,

So we've discovered that 0x5f5e is a destination write mask if it's between 1 and 15, such that the write is now presumably something like this:

dest = (dest & ~writemask) | (color & writemask)

It'd be really great if the top four bits of 0x5f5e were a read mask, allowing us to select bitplanes from source pixels/colors:

dest = (dest & ~writemask) | (color & writemask & readmask)

This would be nice for packing fonts and such. It's already doable with pal(), that's true, but you have to set the entire palette to make it work, whereas you could set just the relevant colors with a read mask. Like, if you have a 1-bit font encoded into four layers, and you're picking the second layer, you'd only need to set pal(2,col) to make it work, instead of all 16.

P#76045 2020-05-07 14:00


I often have several instances of PICO-8 running. It would be nice if their window titles reflected what was running, at least to some extent, so I could select the right minimized app from the taskbar.

Seems like "documentname - appname" is the de facto standard for this kind of thing, so it'd be nice to click on the taskbar and see this list:

  • UNTITLED - PICO-8 (or maybe just PICO-8 when nothing's been saved)
  • TIMER.P8 - PICO-8

I converted the filenames to uppercase here because it feels on-brand, so to speak, and also because making uppercase the default would interact nicely with this optional upgrade:

You could extend the titling algorithm so it uses the same method a .p8.png image uses, where if the first line is a comment, it treats it as the cart's title.

For instance, when running/editing this infloop.p8 cart:

The window would be titled "Infinite Loop - PICO-8", rather than "INFLOOP.P8 - PICO-8", because I used a comment (with mixed case) to make a nice, elegant title for my cart.

(You'd probably want to remember to do unicode conversion too, so kana would show properly on Japanese carts.)

P#75878 2020-05-05 21:02 ( Edited 2020-05-06 01:09)

As the subject says, it'd be nice if ~1 simply became 0xfffe at parse time and thus cost only one token, the same as the recent change where -1 simply becomes 0xffff.

Purty please? :)

P#75863 2020-05-05 17:50 ( Edited 2020-05-05 17:51)

The slide effect works great on a loop that starts at a note greater than 0, sliding smoothly from the final note of the loop back to the first, but if the loop starts at note 0, it steps instead of sliding.

This doesn't feel like expected/reasonable behavior to me.

Here's a repro/demo that lets you swap loop points, swap volume vs. pitch sliding, and swap sine wave vs. noise.

Cart #siyifutoga-0 | 2020-05-03 | Code ▽ | Embed ▽ | No License

P#75767 2020-05-03 02:35

If you display the code on this cart, you'll see some unknown symbols in the header comment.

Here's a comparison of how they look in PICO-8 vs. how they look on the BBS:

I assume this means the BBS doesn't do the glyph->unicode conversion trick.

P#75678 2020-05-01 01:28

Note: @zep, please have patience reading this, as I think there's more than one thing going on here.

I had to fix @Reload's "Pico-Sprint" cart here.

For whatever reason, they were choosing to make empty-arg func() calls as func"", possibly in an obsolete attempt to save a token.

The problem is, the call to clip"" screwed up the clip rectangle, because apparently clip() is interpreting the single blank string argument instead of ignoring it. I'm not sure if that qualifies as a bug, since Lua and PICO-8 expect strings to be coerced into numbers in many instances, so it may just be expected behavior that wasn't previously working as expected. I checked to see if maybe the single-argument format was meant to work with a table, e.g. clip{0,0,128,128}, but that did not seem to work.

However, there's more...and here begins the second thing

In the process of debugging this, I tried getting the existing clip rectangle from clip()'s return value. I tried these:

x,y,w,h = clip""
x,y,w,h = clip()
x,y,w,h = clip(0,0,128,128)

In each case, only 'x' received a value. On examination, it seemed to be all four x,y,w,h values encoded into one 32-bit value: 0xhhww.yyxx. This is at least valid in a way, but it made me wonder if that's why clip() is now accepting a single argument: is it possible to pass a value like that back into clip to set the rectangle with a single value?

Well, no, it isn't, at least not in the version I have.

So, is this an incomplete feature? Should clip() be returning a 4-tuple or the single value? Or maybe a 4-tuple if called with 4 args, and a single all-in-one value if called with one arg? But then which type should no args return? Right now it always just returns the single all-in-one value.

Personally I expected it to be a 4-tuple, and I suspect most people would. That would also make it much easier to manipulate the resulting values. Anyone using the all-in-one value might as well just poke4 or peek4/$ the corresponding memory registers directly.


Report #1: clip("") does something, possibly the equivalent of clip(0,0,0,0). This may or may not be expected behavior for a single improper argument.

Report #2: clip() returns an encoded tuple in one value instead of a proper lua tuple, which while useful, cannot be fed back into clip().

P#75172 2020-04-22 20:03 ( Edited 2020-04-22 20:04)

If the first thing you do after loading a cart is to to delete some source code, you can't undo the deletion.

I suspect this is as simple as needing an undo state/fence at load time.

P#75165 2020-04-22 15:40

View Older Posts
Follow Lexaloffle:        
Generated 2021-02-27 13:02 | 0.164s | 2097k | Q:126