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 discovered this while trying to reduce someone's not-quite-tweetcart, and I boiled it down to a simple repro case.

These compact lines both parse and function correctly:


But these don't:


Variable names don't seem to matter. I included versions with variable names swapped just so you know it's not the variable order at fault somehow.

Same thing happens both when executing editor/app code and at the shell prompt.

I tried on the BBS and the parser here isn't able to work it either.

I didn't try other operators, like *, /, or %.

Side note: On the same subject, would you mind adding ..= and ^= operators? I think that would make the set orthogonally-complete.

P#71615 2019-12-31 23:58 ( Edited 2020-01-01 00:05)

I was playing around with the slide effect on the volume for white noise and I realized that there was an awful lot of sputtering while it would ramp up the volume, but not while ramping down. It sounded as if maybe the waveform was being doubled in amplitude and thus clipping, or maybe something was messing up the randomizer. I really don't know. That's for @zep to figure out.

Here's a repro case, seems to work the same on the BBS as it did in the executable.

Pretty sure it's not local to my machine's audio hardware, but I'd appreciate others trying it and saying whether or not they hear the same issue.

Edit: Yeah, just checked out this post on my phone, it does the same thing there too. Definitely a bug.

Cart #buwihinuye-0 | 2019-12-26 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

P#71419 2019-12-26 14:54 ( Edited 2019-12-27 02:02)

Edit: Never mind, I think the sound driver on my system has some kind of volume compression enabled.

There seems to be something amiss with the SFX player code.

The cart below simply plays a single SFX. The SFX is a constant sine wave tone with a stepped volume envelope. The envelope steps up each half second, from 0 to 7, and then back down each half second, to 0.

On my PC, this results in the SFX immediately being nearly full volume, stepping up once, maybe twice, until it's actually full volume, and then remaining full volume almost until the end, when it then steps back down four times to medium volume and then abruptly turns off.

On the web player, it seems to be more symmetrical. I'm hearing it start at medium volume and step up four, maybe five times before holding steady for a while and then stepping back down four times to medium volume and abruptly turning off.

Cart #bikitetazo-1 | 2019-11-09 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

P#69745 2019-11-09 09:04 ( Edited 2019-11-09 09:35)

There was a discussion on twitter about not being able to get the stack trace for a dead coroutine, which is understandably frustrating. However, I was sure that I once figured out a way to do it, and I said so, but the code to do so is on a dead PC at the moment, so I had to spend some time figuring it out again.

This sample basically runs a coroutine that waits for 5 seconds and then does something fatal. Each frame it displays the known status and stack trace. Run it, you'll see.

I tried to set up the code to be as simple and understandable as possible, but if you have questions, please feel free to ask.

EDIT! For a VERY simple coroutine exception stack trace dump, see my follow-up post here

Cart #tehuyajiso-7 | 2019-10-19 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

P#69033 2019-10-18 23:43 ( Edited 2019-10-19 07:45)

As simple as the subject.

  • Write a cart that uses btnp().
  • Run the cart.
  • Press and hold a button to see it repeats.
  • Hit esc to stop the cart.
  • Enter 'resume' to resume the cart.
  • Press and hold a button and see if it fails.

(Either that or key repeat isn't supposed to be a thing with btnp() and it only works right after you resume.)

This code is sufficient to show it:

function _update()
  if btnp()!=0 then
    print("boop #"..boops)

Note: tested on win64 version.

P#66569 2019-08-11 20:53 ( Edited 2019-08-14 00:20)

The manual implies that the line-continuation syntax might allow for this...

line( 10,10, 20,10, 1 )
line(        20,20, 2 )
line(        10,20, 3 )
line(        10,10, 4 )

...which would make a rectangle with four differently-colored sides.

But PICO-8 seems to interpret line(x2,y2,col) as line(x1,y1,x2,0).

P#63851 2019-04-22 22:53

attn: @zep

I can't remember if this was fixed the last time we reported it, but if it was, it's broken again.

Using backticks to insert inline code in text is broken, causing the entire remainder of the paragraph to be highlighted as code, even if the closing backtick is present.

For instance, this is how I'll write the next paragraph:

If this is already fixed, `this highlight should stop` <-- HERE.

If this is already fixed, this highlight should stop <-- HERE.

Also, just want to mention that I wish the inline code were simply a mono-spaced font, and not inverse the way it is now. It's visually jarring the way it is. :/

P#63761 2019-04-21 13:02 ( Edited 2019-04-21 13:05)

attn: @zep

A tab character is supposed to move the cursor to the next column that's a multiple of the tab-width setting, but instead it just advances the cursor by tab-width columns every time.

In other words, it currently does this:

function do_tab()
  cursor_x += tab_width

And it should do this:

function do_tab()
  cursor_x = (cursor_x + tab_width - 1) % tab_width
P#63629 2019-04-17 23:40 ( Edited 2019-04-21 13:05)


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.

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#61062 2019-01-21 11:38 ( Edited 2019-01-21 15:02)


If I type this:

Look at my `inline code` please.

I get this, which I'm just going to let the BBS handle, so it might look right to anyone reading this after it's fixed:

Look at my inline code please.

The second backtick isn't closing the inline code.

As an aside, I personally would prefer it if inline code were simply a monospaced font, and not inverted as it is now. It makes using inline code very ugly and hard on the eyes. :/

I do like the old-printer-paper code block format though. :)

P#59801 2018-12-07 14:05 ( Edited 2018-12-07 17:34)


I notice Benjamin Soulé's username doesn't show up right, even after your emoji fix.

See here:


Looks like this at my end:

It seems to work fine inside this post's text though.

Edit: Yeah, there's some kind of encoding issue, not a font issue, because if I copy and paste it here, it still comes out "Benjamin Soul´┐Ż", even though I was able to write it correctly above.

P#59792 2018-12-07 09:17 ( Edited 2018-12-07 09:21)

Re: https://twitter.com/johanpeitz/status/1024750757944410112

Cart #54585 | 2018-08-02 | Code ▽ | Embed ▽ | No License

P#54586 2018-08-01 20:25 ( Edited 2018-08-02 00:25)

I've seen a lot of threads where people talk about using mini JSON (JavaScript Object Notation) parsers, but there are always caveats about the differences betwen JS and Lua.

I've prototyped a little parser for what I call LTN (Lua Table Notation, pronounced "loot'n"), which effectively gives you the ability to specify a Lua table's contents, in Lua source format, inside a string. For instance, if you had this:

    name="lone wanderer",

You could swap out the outer curlies for Lua's multi-line string braces ("[[ ... ]]") and add a call to my parser, omitting the parens since it's a single string param and Lua lets you do that:

    name="lone wanderer",

This particular case saves 41 tokens.

Problem is, Lua's a bit squidgy as a language, so parsing its format, even with the assumption that everything is cleanly formatted and with support for comments and "[literal]=value" notation not included, costs about 310 tokens. (It's about 350-390 tokens with those, if you need either/both of them, but I think most people wouldn't.)

Would that be a decent quality-of-life tradeoff for anyone, allowing you just to swap Lua init code for a string representing it like that, but at the cost of 310+ tokens? It feels like a lot of tokens for a table parser, but on the other hand, sometimes you can afford to be a little spendy as long as it saves enough tokens overall AND saves you time reformatting the data.

Should I work on this further to clean it up and make it worthy of release? Or nah?

Technical notes:

One reason why my parser assumes everything is cleanly formatted is because you can test the formatting by swapping ltn[[]] back to {} and running it directly to test. I do have a version with error-checking, but again, it adds a lot of tokens to do that.

Also, I could probably make it a little smaller if I removed the option to specify a simple array table like this:


Depends on whether a person needs to initialize arrays or just objects.

P#53817 2018-06-26 13:46 ( Edited 2018-06-27 17:58)

In the expandable textarea you get when you click Code under a cart, tabs are defaulting to 8 spaces, which is really ugly and rapidly pushes off of the right.

I found info here on how to fix it. If we assume that you're setting tabs to a default of 2 spaces in 0.1.12, then you'd want to do this for the code textarea:

style: -moz-tab-size: 2; -o-tab-size: 2; tab-size: 2;

Because intarwebz haz standardz. I mean, look at that crap. Not just three versions, but two start with hyphens. WTF.

You should also do this for the div surrounding [code]...[/code] regions of posts.

PS: Remember also to fix the broken glyph translation in the textarea.

P#53425 2018-06-09 16:36 ( Edited 2018-06-10 11:52)

I've noticed that with palt() you can leave off the boolean argument and it will default to true, letting you say "palt(11)" if you want index 11 to be transparent. Edit: Uh, turns out I was wrong about this, but the rest of this is still a good suggestion, and as I say below, it'd actually be nice if palt(x) did work this way as well.

There's no equivalent for pal() though. If you leave off the second argument, it acts like you didn't give any arguments and resets the entire palette.

Suggestion: It'd be nice if pal(x) simply acted like pal(x,x), a quick way to reset a single entry to default.

P#53303 2018-06-05 20:49 ( Edited 2018-06-07 04:08)

(Hoping to catch you before you finalize 0.1.12)

Similar to how I felt ceil() was missing from the fundamental math toolset in PICO-8, I feel like there are a couple of fundamentals missing from the table toolset.

Would it be reasonable to add these to the built-in table operators?

function ins(table, index, elem)
  local temp = elem
  while temp ~= nil do
    table[index],temp = temp,table[index]
    index = index + 1
  return elem

function pop(table)
  local elem = table[#table]
  table[#table] = nil
  return elem

-- (and possibly this alias, just for the sake of being symmetrical with pop)
push = add

(Note that I was tempted to suggest an extension to add() where we simply append an optional index, but I've looked at the underlying code you have for add(), and I think the additional code to check for that optional index could slow down existing games too much, so it's probably better to have a new, bespoke ins() function instead.)

With just these two (and a half) additions, it becomes much more intuitive and simple to use tables as stacks, queues, and sets. I think that would be a real boon to both budding and established programmers without really taking the API to a higher level of abstraction.

P#53035 2018-05-26 11:44 ( Edited 2018-05-26 16:33)

I discovered tonight that the modulo (%) operator is treating the nadir value, 0x8000, as if it is positive, whereas other math ops treat it as negative:

> ?0x7ffe/10
> ?0x7fff/10
> ?0x8000/10
-3276.8            <-- as expected, -32768/10 == -3276.8
> ?0x8001/10
> ?0x8002/10

> ?0x7ffe%10
> ?0x7fff%10
> ?0x8000%10
8                  <-- unexpected, -32768%10 should be 2
> ?0x8001%10
> ?0x8002%10

I'm guessing this is an edge-case side effect of whatever you do to make modulo not flip directions at 0 (which I'm very glad you do).

P#52738 2018-05-14 14:57 ( Edited 2018-05-14 19:02)

Cabledragon was talking to me about perlin and simplex noise generation, and we were looking at various web pages on the subject. One of them was a novel implementation of a simplex noise generator, written by Kurt Spencer, a game dev who did not wish to be subject to Perlin's patent on his own version of simplex noise:


With a public-domain javascript implementation of 2D, 3D, and 4D noise here:


The 3D and especially the 4D code is rather long and spaghettilike, though I suspect for good reason, but I noticed the 2D code was not too bad, really, so I took a pair of Lua shears to it and made it fit on our little platform.

It performs pretty well. It looks good, and while you can't efficiently use it to set every pixel on your screen, it's quite adequate for caching your terrain at app launch, or even for real-time generation of the area immediately around you, if you can be somewhat economical with how many times you sample the noise.

Note that, as mentioned in the code, the shell I wrote around this adaptation isn't particularly useful. It's just a custom-made and optimized-to-the-point-of-being-ugly viewer for the noise generator.

The generator code is on the second tab, and that's what you'll want to look at if you want to use it yourself.

Usage of the generator code is simple:


  • Initializes the noise generator with the given seed. Different seeds produce different noise patterns.


  • Evaluate the noise pattern at x,y, returning a fraction between -1 and +1.

Edit: It's probably worth noting that the noise repeats, cleanly, at the length of the seed array generated by os2d_noise(). For larger fields, you'd want to increase the size of the array and accommodate that by adjusting the index masking that's done when referencing the array. I might actually make this configurable at some point.

Edit: Note that this demo is set up to show off the results as slickly as possible, not as a sample. If you want to see a sample usage case, where I also add multiple layers of noise for a more crinkly look, see a later comment below.

Here's the demo. Use ⬅️/➡️ to change the seed, ⬆️/⬇️ to change zoom, ❎ to cycle colors, 🅾️ for help. Note that it's slowly caching the zoomed images in the background, so zooming in quickly might produce some tearing at first.

Cart #52171 | 2018-04-30 | Code ▽ | Embed ▽ | No License

V1.1: Turned off color cycling due to a complaint and put a toggle on X.

V1.2: Caching indicator, because communicating with the 0 users who run this is important!

P#52128 2018-04-29 05:16 ( Edited 2018-08-27 07:02)

So you've told us we can suppress the system pause menu with this:


And that works great. However, I wanted to write something that allows you to access both the game pause and the system pause, by using short presses for game pause and long presses for system pause. Unfortunately, there's a bug/misfeature/oversight that prevents this:

You're still debouncing the button at the system level, even if the system menu is disabled, so if the button is held down, it's only reported down on the first frame. This prevents me from detecting a long press.

I'm guessing this is just because you check the menu-suppression flag after you've recognized and absorbed the button press, rather than wrapping the button check as well.

Any chance of a fix in the next version? :)

P#52014 2018-04-25 14:19 ( Edited 2018-04-26 02:01)

I seem to recall that you said you might break some elements of backwards-compatibility in 0.2. If so, I have a request. I'm not sure it'd even break anything, to be honest.

Current PICO-8 executables allow the use of // instead of -- for comments. This has the unfortunate side effect of preventing us from using lua's // operator, which is the 'idiv' operator, e.g. a//b == flr(a/b).

I have never seen anyone upload code that uses "//" comments, so I think it might not even break anything. You'd probably know better, since you presumably have all of the uploaded carts in some kind of database. If nothing else, maybe you could disable it based on the version number you added to files recently?

It'd just be nice sometimes to use that operator, and if it's already supported but masked by the alternate comment form, I'd hope it wouldn't be a big chore to unmask it.


P#50570 2018-03-18 16:21 ( Edited 2018-03-21 12:48)

View Older Posts
Follow Lexaloffle:        
Generated 2020-01-28 10:10 | 0.107s | 2097k | Q:125