Log In  

I'm brand new to making music and I'm interested in hearing about how different people approach composing for their games. Not in terms of using the music editor—I'm fine with that—but more about your process/workflow for creating the music itself. You can assume that I understand about scales/modes, keys, chords, chord progressions, etc. and feel free to sling around jargon/technical terms if you want I can always go look up anything I don't understand.

I realize that everybody goes about things their own way; I'm not looking for a "right" way to make music, more just interested in peeking into people's brains while I try to figure out my own way of doing it.

The kinds of things I'm interested in (not an exhaustive list!):

  1. How do you start? With the main melody? With a beat? Chords? Something else? Do you always start the same way or does it depend? On what?
  2. Do you create your music from scratch directly in the PICO-8 music editor or do you use something else, like a DAW and then "translate" the music over into PICO-8?
  3. Do you have particular modes, chords, or whatever that you use regularly to get that characteristic video game-y feel? Any that you avoid?
  4. Any other tips/tricks you think might be useful.

I'm interested in hearing from anybody who feels like sharing whether you have a formal music background, are self-taught or just throw a bunch of random notes together until something sounds good.

I myself have zero formal music background and was mostly never really "got" music. Don't get me wrong, I like music well enough but it's always just kind of been a thing in the background. I might know all the words to a song but not what the song is called or who performs it. Fairly recently I started playing around with LMMS and found that, in fact, making music tickles my dopamine receptors and is pretty addictive. So I've been deep diving and learning all I can.

I can do individual things, like write a melody or a chord progression, etc. but the big picture where it all comes together eludes me. Ultimately the solution to that is to learn more and make/practice (much) more but, in the meantime, let me know how you do it!

P#101096 2021-11-30 18:52

I didn't know much about compression algorithms when I started looking into this stuff and, in truth, I still know very little. So this may all be old-hat to a lot of people but hopefully somebody finds it interesting.

What am I compressing and why do I want to compress it?

For the last month or so I've been playing around with Signed Distance Fields (SDFs) first by making some simple pictures with them and using them to handle collsiion detection and then using them as the basis of a procedural morphing animation.

I'm interested in SDFs for a few reasons: I'm not much of an artist but I do like math. If I can substitute math for art in certain situations, that potentially works to my benefit. Also, since SDFs can be used to handle collision detection and they can be updated on the fly with boolean-like operators—union, intersection and difference—they seem like they could be a good choice for modeling level geomoetry and, in particular, destructible level geometry. But mostly I just like playing with them.

In general, you create a function which returns the minimum distance from any point on screen to the surface of whatever object you're modeling and then use that distance to determine pixel colour, or detect collisions, or whatever. But calling functions, especially complex functions as SDFs tend to be, is really slow, especially if you're doing it for every single pixel on screen. To get the animations to actually animate at a reasonable speed I had to pre-calculate all the distances and store them in an array so distance checks became table look-ups. Much faster.

Creating those functions and generating those arrays required a fairly large number of tokens though. So I've been learning about compression algorithms to store those arrays directly and use a, hopefully, smaller number of tokens to decompress them.

To compress, or not to compress

Like most things, it's a trade-off: for a multi-cart system you can probably fit a decent amount of SDF data per cart; for single carts, it's almost certainly not worth it.

SDF data is big. Not as big as I had originally thought but still pretty big. Even though I was ultimately able to get quite good compression ratios we're still talking about thousands of characters worth of binary data per screen of data stored. With a fixed limit of 65535 characters, that adds up fast. In fact, as I'll discuss later, it actually adds up even faster than you'd think. Each compressed SDF only requires three tokens but saving all the tokens in the world doesn't do you any good if you don't have any characters left to use them.

Test data

I mostly used the SDFs from the animation linked above as my compression test data. Here's, sort of, what they look like as distance fields.

Left-to-right, top-to-bottom: Square, Repeated triangles, Repeated circles, Repeated squares, Star, Rotated Star, Line, and Palm tree.

It's worth noting, again, that I'm storing the actual distance data itself and not these images speciifically. The images just give a sense for how the distance fields change and how simple or complex they are. An advantage of working with distance fields in that you can use the same data in multiple ways. Here's a quick little cart which demonstates the idea:

Cart #geyukukaha-0 | 2021-11-08 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

Press 'x' to cycle through the different options. It's the same data in all cases, just being rendered differently.

How big is an SDF anyway?

At first I thought I might have to store fractional values so I'd need 32 bits per pixel. But no. In reality, at least how I'm using them so far, I'm working with integer distances on a single screen. The farthest away something can possibly be on a 128x128 display is about 180 or so along the diagonal: 8 bits is plenty.

Eight bits is definitely an improvement over 32 but still, that's one byte of data per pixel or 16384 bytes per screen of SDF data. At that size, a direct encoding of four SDFs would bust the PICO-8 character limit. The animation linked above uses eight SDFs.

So that number, 16384 bytes, is the base/uncompressed size for all my test data.

Compression algorithms

I tried a variety of algorithms both individually and in combination. These are the main ones.

Run length encoding (RLE)

RLE compresses by replacing a run of identical distances with a single instance of that distance and a number representing how many times it occurs before changing.

It was my assumption that RLE would be a bad choice for SDFs because, although some have long runs of repeated distances, most distances change with every pixel. If your run length is always one then instead of storing one integer per pixel, you're storing two.

Even so, I figured I'd test my assumptions by actually trying it and, sure enough, RLE on its own makes distance data larger, not smaller.

Huffman Coding

A Huffman coding encodes each unique distance with a different binary representation. Not all distances are represented with the same number of bits and the encoding is built in such a way that values which occur often use fewer bits than values which occur more rarely.

On its own, Huffman coding gave similar levels of compression as the LZW algorithm below.

Lempel-Ziv-Welch (LZW) compression

LZW is sort of, but not really, similar to RLE. It doesn't look for runs of identical distances but instead looks for sequences which it has seen before. When it finds one it inserts a reference to that sequence, essentially saying, "take that thing over there and put it over here as well."

Vector Distance Transform (VDT)

Once I thought to search for distance field specific compression algorithms, I found this paper describing VDT and it's the basis for the approach I decided to take so I'll describe it in a little more detail.

Rather than assigning a distance to each pixel, VDT assigns a vector to each pixel. The vector indicates which other (previously calculated) pixels, if any, can be used to calculate the distance for the current pixel. If we calculate pixels left-to-right and top-to-bottom then there are four possible vectors: the pixels directly above the current pixel, the pixels directly to the left of the current pixel, the pixels diagonally up and to the left of the current pixel, and the null vector indicating that the current distance can't be calculated based on previous pixels.

Since there are four possible vectors, each vector can be represented by two bits and the entire array of vectors takes up a total of 4096 bytes. Each null vector indicates a distance that we can't calculate and have to store directly, adding an additional 8 bits each, while every non-null vector is a distance that can be entirely eliminated from our data for a net savings of 6 bits each.

VDT on its own can reduce the size of an SDF fairly dramatically. But a nice feature of VDT is that, once the distance prediction step is taken, the vector data and remaining distance data can be further compressed using other methods. For instance, although RLE doesn't do so well with raw SDF data, it does a great job on the resulting vector data.

I tried two approaches and they gave very comparable results. Both start by doing the vector distance transformation.

The first approach then applied the RLE algorithm to the vector data and a Huffman coding to the distance data, finally combining the result into a single binary string.

The second approach starts by combining the vector and distance data into a single binary string and then running the LZW algorithm on that string to compress it further.

Below is a summary of the results I got via various methods. The VDT+LZW columns could just as well be VDT+RLE+Huffman since the results were very similar.

Lossless vs Lossy

I suspect that the palm tree SDF compresses so poorly because it contains a bunch of non-linear transformations: sines, cosines, exponentials, etc. which means the distance field isn't "well behaved" and, therefore, difficult to predict.

The VDT algorithm is lossless by default—it only removes a distance which can be predicted exactly—but is easily modified to be lossy. I wanted to see if I could get the palm tree SDF down to a more reasonable size without degrading the quality too badly. Spoiler alert: not really. It's easier to see when rendered as an image:

The first image is the lossless version as listed in the table above. The distortions in the second could be acceptable in some situations but still only gives a 74% compression ratio with a maximum squared error of 5. The last image, which looks like Thanos had a personal vendetta against trees, has a max squared error of 10 and still only compresses down to about 60% or a little under 10000 characters!

Compressing less to compress more

One particularly interesting discovery I made was that—even though the VDT+LZW combination gives the best compression on average—the best way to fit more SDFs per cart was to use VDT only. Why should that be the case? VDT by itself gives the worst compression of those listed. How is that better?

I figured this one out by accident when I copied the binary strings, except for the palm tree, into a cart, ran INFO and saw this:

The raw character count and the compressed character count are nearly the same. Which actually makes sense: I've compressed the data significantly so whatever algorithm PICO-8 is using to compress code isn't able to squeeze much more out of it. That particular screen shot is when using VDT+LZW but the same thing happens when using LZW only, Huffman coding only, and VDT+RLE+Huffman coding.

That got me thinking: PICO-8's compression is probably better than mine. So what if I only used VDT and let PICO-8 compress it the rest of the way for me? Here are the same seven SDFs with only VDT encoding.

It uses up a lot more of the raw character count but the compressed character count is much lower and there's still room to spare!

Edit: I forgot to actually post the compression/decompression code anywhere so here's a cart demonstrating it. The code is also on github

Cart #sdf_compression_demo-0 | 2021-11-10 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

P#99798 2021-11-08 18:19 ( Edited 2021-11-10 11:22)

Cart #demo_realtime_sdf-0 | 2021-10-23 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

The final image is an adaptation of the image from the Principles of painting with math tutorial by Inigo Quilez.

I'm using fixed time-steps and the animation is intentionally a bit slow so "real time" might be fudging the truth a bit but I thought I wouldn't be able to animate these things at all and it turns out I was wrong. I just needed to trade off time for space in time honoured fashion. That's why there's a "loading screen" at the start: most of the time intensive calculations are being done up front. I used the SDF utility I posted the other day—not strictly necessary of course, you can just write the functions directly—to create all the signed distance functions used for the animations and then converted them to arrays by pre-calculating all the distances and turning distance checks into a simple table lookup.

This animation uses 8 such tables and takes up about 661kB (including all the code, other variables, etc.) so fits fairly comfortably in the 2MB of available lua memory.

This is mostly a proof of concept thing so the code isn't super well documented or elegant and I'm kind of just manually stringing together all the individual animations. Improvements could certainly be made but overall I'm pretty happy with how it came out.

Ideally I'd like to be able to generate the SDFs and convert them to tables from a separate cart (or another language/external tool/whatever) and export the data so it could be used in other carts. I don't know much about image/data compression algorithms so if anyone can point me towards one that might be suitable it would be greatly appreciated.

  1. The data is a 1D array of distances—which can be fractional—so each array stores a full 32-bits per pixel for every pixel on the PICO8 display. It's not image data as such so there's no requirement for it to be viewable as an image, though that might be neat.
  2. I'm not too worried about how long it takes to compress the data but fast decompression with a low-ish token count would be great.
  3. By the same token, I don't necessarily need optimal compression. Good enough is...good enough.

I thought about run length encoding but I don't think distance fields are good candidates for that in general since many rows change value every pixel.

Anyway, I hope you enjoy it. Comments, criticism, suggestions all welcome!

P#99070 2021-10-23 17:33

PICO-8 has been my obsession for the last few months but math is my always obsession and lately I've been having a lot of fun playing around with various aspects of geometry so when I hit a weird bug in my other project I decided to take a break and shift gears a bit. So I threw together a little interface for working with 2D signed distance fields/functions.

It's fairly hefty at a little over 500 tokens so probably not super useful generally speaking but pretty fun to play with, if I do say so myself.

Here are a couple series of images I've created so far. In each, all of the images are made from the same SDF and just messing with how colours are assigned based on the distance.

That last one is my attempt at a ripply/watery reflection which, I think, didn't come out too badly.

And some hearts.

If you're not familiar with them, SDFs are frequently used with a ray marching technique to render 3D scenes as a faster alternative to ray tracing. (See Inigo Quilez's work for some stunning examples of this in action. Or basically anything on shadertoy.) Anyway, ray marching is basically just a collision detection algorithm so, apart from making pretty pictures, an SDF can also double as actual level geometry.

In the cart below I took the same SDF that was used to generate the image and used it to turn the pool/fountain thing into a collide-able part of the scenery. (In fairness, I'm not actually using ray marching here I'm really just checking for when the distance goes from positive to negative. But you could do better/smarter things if you really wanted to.)

Cart #punujehibi-0 | 2021-10-18 | Code ▽ | Embed ▽ | No License

In this one I just threw together a bunch of random geometry. Move around with the arrow keys and press Z to cycle which distance field(s) are visible: none, interior, exterior, or both.

Cart #ragahegaro-0 | 2021-10-18 | Code ▽ | Embed ▽ | No License

P#98824 2021-10-18 20:56 ( Edited 2021-10-18 20:56)

Building on my previous ray casting toy I've been playing around with shadow casting.

Cart #difepoduko-1 | 2021-10-10 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

Old (30fps):

New (60fps):

Most examples of this I've come across work with tiles/on a grid. This works with arbitrary line segments with the following caveat: the more line segments, the worse the performance. This example runs okay at 30fps but not so much at 60fps.

Most of my time seems to be lost in the actual casting of rays.

Things I plan to do but haven't done yet:

  1. Currently I'm casting two rays, slightly offset, towards every line segment's end-points (after removing duplicates so I'm not casting at the same point more than once.) I figure it shouldn't be too terribly difficult to modify it so a ray which hits a line's end-point is extended to the next wall instead of stopping. Then I'd only need one ray per end-point cutting my total number of rays in half.
  2. Culling the geometry before casting so instead of casting at everything, I only cast at everything within a visible radius. Bonus, that'll also make it easy to only cast in the direction the character is facing instead of casting in all directions.

I do sort things a few times and I'm using bubble sort because it was easy to implement but I'm sorting pretty short lists so I don't think it's a huge time sink.

Suggestions on how to optimize further are very welcome!

Optimized a couple things:

  1. Made casting rays more efficient by in-lining the wall/ray intersection calculations.
  2. Made removing duplicates more efficient by using a set-like data structure.
  3. There are enough line segments/end-points that a more efficient search makes sense so threw together a (probably pretty bad) merge sort.

The new version has approximately the same CPU usage at 60fps that the old version had at 30fps. I'm sure some optimization wizards out there can help me squeeze some more out of this.

P#98442 2021-10-09 20:41 ( Edited 2021-10-10 20:30)

Had a video on ray casting pass by in my Youtube feed the other day and thought I'd play around with it a bit.

There are the four outer walls and then four randomly generated interior walls. It's casting 27 rays so, when moving, that's 216 intersections calculated per frame at 60 fps. Either more walls or more rays forces it down to 30 fps so it's a bit of a performance hog (okay, a huge performance hog.) I'm sure it could be optimized somewhat, though I'm unlikely to do it.

Edit: Okay, so apparently I lied. I realized that I could have my rays do double duty by calculating two intersection points each, one in each direction along the line. So I get effectively twice as many rays for virtually no extra cost.

Cart #yirijahiwe-1 | 2021-09-27 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

Here's a quick little modification I made which uses the same logic to handle actual collisions with the walls. It's only casting one ray in the direction of movement so you could add a lot more walls before you notice performance issues. It's not perfect. Every once in a while the red dot will zip right through a wall. I suspect this is just because of decimal precision errors and in an actual game could be fixed by checking more than one point for collisions: the four corners of the sprite for instance. But, again, I probably won't bother.

Cart #tewetaripi-0 | 2021-09-25 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

Oh, and the video:

P#97805 2021-09-25 20:00 ( Edited 2021-09-27 15:33)

State Machines

A wrote (a couple variations of) a simple state machine manager. The code is on github under an MIT license. There are a couple demos down at the bottom of the page.

  • state-machines-du (107 Tokens): each state has its own draw and
    update methods
  • state-machines-st (111 Tokens): each state has setup and teardown
    methods which are run only when the state is entered and exited


Creating a state machine

To create a new state machine use the 'new' method:

    sm = state_machine:new()

Adding states

After creating the machine itself you need to add states. A state
consists of four things:

  • A unique name or identifier: Most likely a string but can be
    anything as long as it's unique.
  • A transition function: This function is called once per cycle
    and should return the identifier of the state the machine
    should switch to.
  • An update (or setup) function: The update function is called
    once per cycle and should update variables, etc. associated
    with the state. For state-machine-st.lua, this is instead a
    setup function which is only run once each time the machine
    enters this state.
  • A draw (or teardown) function: The draw function is called
    once per cycle and should draw everything relevant to the
    state. For state-machine-st.lua, this is instead a teardown
    function which is only run once each time the machine exits
    this state.

Add a state to the machine using the 'add_state' method:

       -- identifier
       'a state',

       -- transition function
          if btnp(5) then
         return 'some other state'
         return 'a state'

       -- update function
          if timer then
         timer += 1
         timer = 0

       -- draw function

Using the state machine

  1. state-machine-du.lua

    Once you've created a state machine and added some states using it
    is simple: Set the initial state then call the update and draw

        function _init()
           sm:set_state('a state')

        function _update()

        function _draw()

The update method calls the current state's transition function
and changes the current state if necessary and then calls the
current state's update function. The draw method calls the current
state's draw function.

  1. state-machine-st.lua

    The setup/teardown version is basically the same except there is
    no draw method and the update method does a bit more work, so all
    you need is this:

        function _init()
           sm:set_state('state 1')

        function _update()
           -- whatever other update stuff you need to do.

        function _draw()
           -- whatever draw stuff you need to do.

The update method in this version also calls the transition
function. If a state change is necessary, then the current state's
teardown function is called, then the current state is changed,
and finally the new state's setup function is called.



A very basic state machine with two states. Press X/V to switch from state 1 to state 2, Z/C to switch from state 2 to state 1.

Cart #basic_sm-0 | 2021-08-04 | Code ▽ | Embed ▽ | No License


A simple platformer demo with 5 states: intro, tut_movement, tut_jump, play, and gameover.

Cart #platform_tut_sm-0 | 2021-08-04 | Code ▽ | Embed ▽ | No License

P#95647 2021-08-04 09:55

I've finally started working on an actual game. The programming is not a problem but art, on the other hand, well...there be dragons.

I'm not an artist and it doesn't come naturally to me but pixel art, 8x8 pixel art in particular, seems much more approachable than more traditional types of art. I think because the feedback cycle is shorter and more obvious: with only 64 pixels to play with, it's pretty obvious when you put one in the wrong spot and there are a limited number of choices available for how to fix the problem. So I spent a few days poking around the sprite editor doing a bit of an "art study" to see what I could figure out. Comments, tips, tricks, etc. very much welcome.

This is very much not a tutorial. I'm not and artist and I'm not qualified to teach anybody anything about doing art of any kind. If people find it interesting or useful, great! But mostly I'm just using this as a place to record observations for future me to think about when I'm trying to figure out what the heck my game should actually look like. I'll add the full sprite sheet at the bottom if anyone's interested in having a look at it.

Basic proportions

One of the things I remember from my many failed attempts to learn how to draw is that people are generally about 8 heads tall, which seems ideal for an 8x8 box. One for the head (duh), three for the torso, and four for the legs, with the hand hanging just below the waist. Sketching that out as a sort of measuring stick, I got this:

Trying to make that into a front view though presented immediate problems:

The shoulders are a bit too broad compared to the head but the main problem is the pogo-stick leg. So obviously I had to mess with the proportions a bit to get something that looked acceptable.

The legs had to be shortened to allow for the larger head but at least now I get hair! This is, I think, probably about as close to "proper" human proportions as it's possible to get inside and 8x8 box.

Messing with proportions

Alright, so how does it change if I mess with the proportions further? Maybe other people will read these differently than I do but here's what I've got:

  1. Extending the torso an additional pixel gives him a solid, football player-ish kind of feeling.

  2. Making the legs one pixel longer, on the other hand, gives a more prim and proper, possibly slightly
    stuck-up vibe.

  3. Lowering only the shoulders gives him a "stylish" turtle neck.

  4. While making the head larger makes the character feel a bit more heavy-set. Especially if the torso is
    also extended an extra pixel making the legs shorter.


  5. I did a couple characters at 8x16 with the same basic proportions where each pixel in the 8x8 becomes a
    group of 4 pixels in the 8x16 which can then be edited to keep the proportions more in-line. Didn't do
    many of these though

  6. And then, of course, with a bit of understanding of the proportions and how to balance them, I went for
    the big-head, small-body look. I tried a few variations:


    But eventually ended up with this cute little guy. With a whole pixel of headroom to spare!


Okay, so I'm getting the hang of things but now I need them to move. I went back to my original "measuring stick" guy and tried a walk cycle.

Not too bad. The torso stays straight up and down so I also tried angling it forward and backward to see how that changed the feel of the walk.

The forward tilted one feels like it moves faster, even though it doesn't, and feels like it has more of a sense of purpose about where it's going. The backward tilted one feels slower and like it's ambling, lazily, almost clumsily from place to place. Honestly, I feel like I could do almost anything with the legs and as long as I've got that bit of a head bob and the arm swinging, it'll pretty much read like a walk.

Tried the little cute guy next and even added an idle animation and played with the position of his head.

Feet don't do much. Still looks like a walk. Nice.

And finally, having just watched a video on sub-pixel animation I'd thought I'd give that a try in a small way by making a couple guys with weapons.


I decided to try making 1-bit black-and-white characters because I'd have to figure out how to suggest shape and separation of body parts with as few pixels as possible since I couldn't use colour to distinguish the sections. Mostly just rehashed everything I did above but in black-and-white only but a few things I learned: The colour stick-guy walking looked okay in all three versions. In 1-bit, the forward tilt of the body was essential to suggesting the direction of motion. Everything else just looked vaguely off no matter how I tweaked the frames.

For characters with faces, outlining the head but leaving it open on one side worked best and, depending on the size of the head, eyes can go right up into the hairline or not. Also, I found for some reason that a black pixel behind the hand really helped to sell the idea of the hand hanging by the waist even though that black pixel didn't really represent a part of the character's body. More like just a shadow which helped to sell the idea of a hand there.

And then, once I've got a general character I'm happy with I can always colourize the sprite if I want to.


How do you make something look hard or soft? Rough or smooth? I have literally no idea. I started by just trying to sprinkle some random pixels around and try to figure out if it looked rough or smooth. What it looked like was a bunch of random pixels.

So instead, I created a few regular patterns, copied them and then made some tweaks trying to change the texture somehow. What I mostly ended up with were just a bunch of different patterns. I guess some looked rough and some looked smooth but mostly they just looked either more or less regular.

As individual tiles they don't look like too much but some of them made nice patterns when tiled together. And I played around and found a few others that I liked as well:





Didn't really get me closer to texture though. So here's the process that I figured out, basically applying a manual noise filter, that's giving me fairly decent results. I think, anyway.

I take copy of the "clean" tile and overlay some other pattern with a different colour. Usually diagonal lines but sometimes other patterns too, it takes some trial and error to find a good one. Anyway, once I've done that, I apply some rule like, "all grey pixels in the top half of the tile get coloured white while all the grey pixels in the bottom half get coloured black." That gives me a "dirty" tile which still has some recognizable structure to it but is a bit messed up.

Tiled together on their own the dirty tiles are a bit too messy but mixing a few clean tiles in with them gives the eye enough to pick out the underlying pattern and make sense of the whole thing.

And then, just like with the characters, I can colourize them later. Here are a couple I quite like. Though I make no claims that my colour choices are any good or that these are great or anything. But, I think, not a terrible starting point, at least.



So that's the beginning of my pixel art journey so far. Still much to learn but I'm starting to get a handle on it.

P#94449 2021-07-04 23:44

I hate fiddling with meaningless magic numbers trying to get a behaviour right. I prefer a bunch of knobs with predictable effects. So I made this little tool for fine tuning variable-height jumping behaviour for platformers.

Note: As written, it's possible for the cart to get stuck in an infinite loop for certain values so read below on how to, hopefully, avoid that issue.

Cart #jump_tuner_1-0 | 2021-06-25 | Code ▽ | Embed ▽ | No License


  • Up/Down to select a value
  • Left/Right to modify the value
  • X to test the jump, tap for small jumps, hold for higher jumps

There are three tunable values and a fourth value which is calculated for you and can't be changed manually. Once you get something you're happy with, note down the values, and use or modify the jumping code from this cart in your own project. I think I've clearly indicated all the parts of the code that can be deleted and which parts should be modified. If you do use it, attribution is appreciated but not necessary.

The tunables are pretty self-explanatory. They are:

  • height
  • gravity
  • initial acceleration

The fourth value, alpha, is calculated based on the other three.


This should be a negative number (upwards direction) in pixels. It's actually more like "requested height." Depending on the other values there is a certain amount of error in the calculations so the maximum height of the jump won't be exactly the height you specify but it should be close.


This should be a positive number (downwards direction). Technically, the units are pixels per frame per frame but that doesn't really matter. As you'd expect, this controls how fast you fall. Higher values make you fall faster; smaller values, slower.

Initial acceleration:

This should be a negative number (upwards direction). This is the upwards acceleration of the character when you press the jump button. While you're holding the jump button, this acceleration slowly decreases until the character is once again only under the influence of gravity. If you let go of the jump button before the jump reaches its full height, the acceleration is cut immediately and the character starts to fall sooner.


Alpha is calculated from the other three values and controls how quickly the initial acceleration decays.


Given the specifics of the physics, it turns out that alpha can't be determined analytically: There's no nice simple formula for it. Or maybe there is and I'm just not patient enough to grind through the equations to figure it out...Anyway, it can be solved computationally, essentially using a binary search algorithm. Which is fine except that PICO-8's limited floating point precision as well as the way lua handles division by zero (turns out 0/0 = 32768, take that math!) means that the algorithm sometimes gets stuck in an infinite loop because it can't find a good enough solution.

Avoiding problems:

Height error

Gravity and initial acceleration should have similar magnitudes, just in opposite directions. The greater the difference between them, the more error creeps into the requested height. Basically, if the difference is too great then once the upward acceleration cuts out, it takes longer for gravity to slow the character down and it over-shoots the target height. The closer the two are in magnitude, the closer to the target height you'll get. However...

Infinite loops

Though it is by no means obvious, when gravity and initial acceleration have the same magnitude the code which calculates alpha will try to divide by zero and the program won't be able to find a suitable solution. But it'll keep trying. Forever.

PICO-8's limited floating point precision means that this actually become a problem when gravity and initial acceleration are even close to being the same magnitude. If gravity = 0.3 and acc=-0.3, that's going to cause problems.

In general, gravity and acceleration should be close to the same size (but in opposite directions) if you want accuracy in height but not too close or the cart will freeze up.

This is only a problem when calculating alpha based on the other values. When you copy/paste the code you'll just assign alpha and can tweak the other values to be as close as you want.

P#94022 2021-06-25 19:05

I've been playing around with various procedural generation techniques and wrote a little tool for creating generative string grammars. It's 104 tokens and the code is on github under an MIT license.


This first one generates side-scroller/platformer levels. Each character of the generated string represent a four tile wide column of the map. I didn't add a player because I just wanted to showcase the level generation itself so the map just scrolls automatically from the start to the end. Reload the cart to generate a new level. It's just an example so the levels aren't particularly interesting but you could add a few "post-production" passes over the generated strings to clean things up, generate enemies, add additional rules for pre-made features, etc.

Cart #jdelaat_stringgram_scroller-0 | 2021-06-21 | Code ▽ | Embed ▽ | No License

The next one is a vastly simplified version of this process to generate names of things/places in a made-up language. The grammar constructs some syllables then makes words of either two or three syllables. A final pass over the generated word cleans up some messy, hard-to-pronounce double consonants. For a quick, not very sophisticated implementation it comes up with almost but not quite decent sounding place names.

Cart #jdelaat_stringgram_words-0 | 2021-06-21 | Code ▽ | Embed ▽ | No License


string-gram provides a number of functions to help you build a grammar and it's easily extensible via custom rule functions. Grammar rules are just functions which take no arguments and return a string.

  1. lit

    The lit function takes a string as input and produces a rule which will generate that string when called:

h = lit('hello') -- h is a rule, aka a function of 0 arguments.
print(h()) -- prints 'hello'
  1. seq

    The seq function takes any number of rules as input and produces a new rule which outputs the result of each rule in sequence.

h = lit('hello')
c = lit(', ')
w = lit('world')
hw = seq(h, c, w)
print(hw()) -- prints 'hello, world'
  1. choice

    The choice function takes any number of rules as input and outputs a new rule which outputs the result of one of those rules chosen at random.

a = lit('a')
b = lit('b')
ab = choice(a, b)
print(ab()) -- prints either 'a' or 'b' at random
  1. copy

    The copy function takes a single rule and an integer, n as input and outputs a new rule which applies the given rule n times.

a = lit('a')
aaa = copy(a, 3)
print(aaa()) -- prints 'aaa'
  1. sym and register

    Grammars are often defined recursively so you may find yourself needing to include one rule, which you haven't yet defined, inside the definition of some other rule. sym and register solve this problem by allowing you to insert a 'symbolic' rule which will be looked up at a later time when it's actually called.

one = lit('1')

-- This won't work!
-- many_ones = choice(one, seq(one, many_ones))

-- Instead, we use a symbolic rule...
many_ones = choice(one, seq(one, sym('1s')))

-- ...and then use register to insert the rule into a lookup table.
register('1s', many_ones)

print(many_ones()) -- prints random number of 1s
                   -- ex: 1, 111, 1111111111111, etc
  1. Custom rules

    Rules are just functions which take no arguments and return a string so you can easily create your own rules or functions which create rules. For instance, suppose you want to create a rule that randomly returns the output from another rule or else returns an empty string. One way to do that would be like so:

function zero_or_one(rule)
   return function()
      if rnd() > 0.5 then
     return rule()
     return ''

a = lit('a')
a_or_not = zero_or_one(a)
print(a_or_not()) -- prints either one 'a' or nothing with 50/50 probability
P#93825 2021-06-21 17:54

This is the second of the two little utilities I've made, the first being stream-ecs (in a separate post.)
The github project page.


This one's a bit more straight-forward. Prototype based inheritance/OOP. It basically just defines the base object and a few methods.

Use create to use an object as a prototype for some other object:

dog = object:create() -- use the base object as prototype
dog.sound = 'woof!'
function dog:talk()

cat = dog:create() -- use the dog object as prototype
cat.sound = 'meow'

dog:talk() -- woof!
cat:talk() -- meow

You can create classes (well, not really, because prototypes, but sort of) by defining a method called init and using the new method.

vec2D = object:create()
function vec2D:init(x, y)
  self.x = x
  self.y = y
function vec2D:length()
  return sqrt(self.x^2 + self.y^2)

v = vec2D:new(3, 4)
v:length() -- 5

The new method creates a new object with vec2D as the prototype and then passes its arguments to init which initializes the object.

And sub-classes:

vec3D = vec2D:create()
function vec3D:init(x, y, z)
  self.proto.init(self, x, y) -- calls vec2D's init
  self.z = z
function vec3D:length() -- overrides vec2D's length
  return sqrt(self.x^2 + self.y^2 + self.z^2)

v3 = vec3D:new(1, 2, 3)
v3:length() -- 3.74-ish

And that's about it. Enjoy!

P#93466 2021-06-15 20:39

I picked up a copy of PICO-8 a few months ago on a whim and have been playing around with it, following a tutorial here and there, etc. Really enjoying it and I love seeing the creativity of people in the community.

I have yet to try my hand at an actually complete game and, as I'm basically infinitely distractable, may never actually accomplish it. In the meantime, to learn some of the ins and outs of Lua, with which I was not previously familiar, I've combined two of my favourite software writing pastimes: re-inventing wheels and making tiny utilities.

In that spirit, I give you:

  • stream-ecs (267 tokens), a (sort of) reactive stream based entity-component-system with automatic entity queue management, and
  • prot-oo (72 tokens), prototype based object-oriented programming (in a separate post)

I'll describe them a bit below and there's more info at the github project page. Each tool has its own README describing it in detail.


I know there are a few different ECS frameworks floating around the PICO-verse. This one's larger than some and smaller than others but should, I hope, save you tokens in the long run by managing queues of game entities for you.

You start by creating a "world" and then spawning systems off of it:

world = ecs()
   :system({'timer'}, increment_timer)

   :system({'timer', 'position'}, move_character)

Each system creates and manages its own queue and when you add entities to the world...


... the world passes it to each system which either stores it in its queue, if it matches the selectors, or discards it.

You can also chain systems together to create a sort of filter. This example is functionally identical to the one above:

world = ecs()
   :system({'timer'}, increment_timer)
   :system({'position'}, move_character)

Entities with a timer component go into the first system's queue then, of those entities with a timer, those which also have a position component go into the second system's queue.

You define components like so:

position = component('position', {'x', 'y'})

Which returns a constructor function:

p = position(1, 2)
p.x -- 1
p.y -- 2

And you create entities like so:

character = entity()
   :add(position(1, 2))

There's a pre-defined draw component and draw system so you don't have to manage those manually either, you can just do this:

function _draw()

And updating all your entities is this easy:

function _update()

Here are a couple demos:

Simple particle system

Spawns a new particle at the source each tick. Particles are removed from the system after 200 ticks so there are 200 particles on screen at any given time.

Cart #jdelaat_streamecs_particles-0 | 2021-06-15 | Code ▽ | Embed ▽ | No License

Simple platformer

Collisions are a bit wonky: the character occasionally falls into the floor some reason but that is, presumably, because of how I wrote collisions and not the ECS itself. It should be enough to give you an idea of how it works, anyway.

Cart #jdelaat_streamecs_platform-0 | 2021-06-15 | Code ▽ | Embed ▽ | No License

P#93530 2021-06-15 20:39

Follow Lexaloffle:        
Generated 2022-01-28 09:05:41 | 0.129s | Q:55