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

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:

P#77094 2020-05-24 00:11

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

It's not critical since its only real use is in debugging, but I think tostr(f, true) should probably emit hex addresses with standard PICO-8 uppercase hex digits, not smallcaps:

P#75005 2020-04-19 19:52

I was tinkering with the custom key repeat settings in 0x5f5c and 0x5f5d and I realized that they always seem to be expressed in 30ths of a second. Shouldn't this depend on whether or not the game has _update60(), or for games with custom loops, whether or not they've called _set_fps(60)?

I guess it's not a critical problem if it stays this way, it just seems like it ought to track the fps setting.

P#74999 2020-04-19 19:07

Hey @zep, me again. ;)

I looked through the rather comprehensive list at https://rosettacode.org/wiki/Bitwise_operations and couldn't spot anyone at all who had an operator for rotation, let alone find some kind of standard for it. Most languages use either a function or a textual operator (e.g. a ror b), but those end up making your expressions a lot larger and/or filled with whitespace.


I'll just propose a few ideas. Note that I bring the "|" operator into some of them because normally people have to do rotates by or'ing together two shifted values.

   rotl         rotr
----------   ----------
 a <<<< b     a >>>> b
  a <<> b     a <>> b    -- I like these best, personally
  a >|> b     a <|< b
  a |>> b     a <<| b
  a >>| b     a |<< b

I like <<> and <>> because they imply that part of the shift wraps back around. I also find them aesthetically pleasing.

Also, if this happens, please remember assignment operators and metamethods? :)

P#74985 2020-04-19 16:52 ( Edited 2020-04-19 16:55)

I was going through my old posts, gleefully marking bug after bug "resolved" (THANKS ZEP!:D), and ran across one I hadn't actually posted in the support section.

I know the chat section sees a lot of traffic and moves quickly, so I'm worried it was never seen, and I want to try posting it again in the right section, in hopes of seeing it added to 0.2.0 or a later version:


I'm trying to write a tool that has text editing, and I find that the devkit keyboard doesn't return a lot of the special keys that it easily could.

You're returning a string, so there's no reason why it has to be just one character. So pressing the Home key could simply return the string "home". No need for special characters, escape sequences, etc., since these verbose names would all be distinct from any single regular typed character.

I'm mainly thinking of the standard navigation cluster: up, down, left, right, ins, del, home, end, pgup, pgdn, but I'm sure there are others.

It'd also be nice if there were a second stat() value that had a bitmask of the modifier keys that are currently pressed, if the host has them. Like, left/right shift, left/right alt/cmd, maybe left/right win/opt, altgr, maybe capslock. Not crucial, but definitely helpful. It'd be nice to be able to support things like ^Z for undo in my tool.

I say all this because one of the things I love about PICO-8 is working entirely within PICO-8, so it's nice to write tools that work inside of the platform, but the devkit functionality is a little limited in this department.

P#74953 2020-04-19 01:45 ( Edited 2020-04-22 20:44)

There's currently no way to get the existing random seed, since srand() doesn't return anything.


Cart #yudodekegi-0 | 2020-03-29 | Code ▽ | Embed ▽ | No License

Could it return the existing seed so we can temporarily change to a sub-system's ongoing seed, then restore it? Like this, for instance:

function handle_ai(ai)
  local old_seed=srand(ai.seed)

It's minor, since you can always keep your seeds in shadow values from the outset and manually set them each time, but it'd still be handy.

Thanks! :)

P#74306 2020-03-29 13:02 ( Edited 2020-03-29 14:03)

Presently, to load+run you literally have to load blahblah.p8 and then run.

Would it be a bother to run the "run" code through the same line parsing that handles "load", and then auto-run it afterward?

I ask because I was running a bunch of local carts from the command line and it was a tiny bother to have to do two steps each time.

...YES, I realize this is a very minor thing, but hey, why not mention it? The worst that happens is that it doesn't get implemented, right? XD

... uh... r—right...?

P#74191 2020-03-25 14:49 ( Edited 2020-03-25 14:50)


If we type "x = -6", the "-6" part requires two tokens, which I assume is because the version of Lua you use was still internally treating negative literals as positive literals with a unary-minus operation applied to them, e.g. "x = unm(6)".

However, using a negative literal in PICO-8 appears to have the same cycle cost as using a positive literal, as far as I can tell with my benchmarks. For instance, "x = y / -6" costs the same as "x = y / 6".

So basically we're being charged a token for an operation that doesn't actually seem to occur, and really shouldn't need to occur anyway.

Would it be possible to delete the token cost for unary-minus if it's being applied to a literal?

The alternative is that people in dire token straits will write "x = y / 0xfffa", which is just gross. :P

P#74149 2020-03-23 17:49

View Older Posts
Follow Lexaloffle:        
Generated 2020-07-09 10:57 | 0.120s | 2097k | Q:123