Doodle Insights #15: Masking and Layering! (Sprite-Sheet Extravaganza part 3)

These Doodle Insights are brought to you by my super generous patrons on Patreon!

This is the third part about reading and writing to the Pico-8 sprite-sheet! It is recommended that you read the first part first and I also recommend that you look at the second part, just cause it’s good too!

Two weeks ago we looked at the different methods to write to the sprite-sheet and last week we saw a bunch of rendering trickery based on these methods! Today we look at more rendering trickery!

The subject of today is masking/layering in Pico-8!

Masking and layering are about drawing things to different render targets and then put those render targets together in a pile so that it forms just one picture. In Pico-8 we only have one rendering target, the screen. But as we saw previously, we can save that screen to some other place in the Pico-8 RAM memory, thanks to ‘memcpy()’, and especially to the sprite-sheet space which we can use with ‘spr()’, and then draw new things on the screen! That’s gonna be the idea behind our masking/layering tricks.


This is one of the earliest doodles I made! It was based off Tim Soret’s tweet about Kung-Fu Panda’s fire! The idea is to have a colored screen and carve the fire from it by drawing rounded black shapes and do that on several layers with different base colors. Then put the layers together.

And that’s exactly what the doodle does! There’s a list of circles for each of the three layers and the layers are rendered one by one each frame. We render the front layer first and put it in the sprite-sheet. Then we render the middle layer and use ‘spr()’ to draw our first layer on it and we store that in the sprite-sheet. Finally, we render our background layer and use ‘spr()’ again to draw our other layers (already combined) on top of it.

Because I made this version pretty long ago, I’m fairly sure that it’s performing way more poorly than it could. Still, it works quite well, even at 10fps! (I think I might go and try to make it faster after writing this)


In this doodle we’re stacking steps of a custom 2D cellular automata!

In the code, the sprite-sheet is considered as a 4×4 grid with 32×32 cases. Each ‘_draw()’, every case is copied to the next one in the grid and then the first one gets the next step of the cellular automata. Afterwards, we simply draw all the 32×32 frames on top of each other (applying a vertical offset) from the oldest at the bottom to the earliest at the top! (the order is important when doing layering)


This is another case of drawing everything to the screen, then storing it to the sprite-sheet and then animating certain colors from the sprite-sheet, like in the first Doodle Insights about sprite-sheet reading/writing with the cave generation.

The difference is in the animation. I’m using good old procedural dithering on the white, green, dark green and black colors to create the rustling effect. But because I’m using it on the black, it was making the trunk weird too.

The solution was to use the ‘palt()’ function which lets you make colors transparent when using ‘spr()’ and ‘sspr()’. And you can make multiple colors transparent, a simple feature yet missing in most game engines! Using that function with all the rustling colors listed previously, we can draw the whole sprite-sheet at the end of our frame and that will get us just our trunk back without messing with the leaves’ animation!


This isn’t masking nor layering. But it could be and it would look just the same!

The P animation here is only made with ‘pal()’. You can find out some more about this in my guide to palette swapping!

For each window in this doodle, we draw the frame with lines and then the content of the window with ‘pal()’ and ‘sspr()’.

I do not know whether or not using masking here would perform better. (I didn’t try it) With masking, we would draw our windows as holes on the scrolling background and that would be the front layer, above the animated ‘P’ on the background layer. While this would save us from calling ‘pal()’ and ‘sspr()’ for every single window, it would also require us to save the sprite-sheet somewhere and reload it every frame, as we need also it for regular sprites, along with the usual business of copying the screen to the sprite-sheet for the layering.

The difference is that masking relies on a few heavy tasks that have to be done every frame, while our faked masking largely depends on the number of windows currently on screen.


Another case of faked layering! This one uses a very interesting function, ‘clip()’!

Calling ‘clip(x,y,w,h)’ will let your next drawing operations only modify the portion of the screen at ‘x,y’ and that is ‘w’ wide and ‘h’ high. It will affect all your drawing operations until you call it again without any arguments, much like ‘camera()’ or ‘pal()’.

In this doodle, we draw the background and the frames’ borders in the ‘_init()’ and then we draw the content of the frames in the ‘_draw()’. But we don’t want the content of the frames to get outside of the frames. (especially with the long noses) Here’s what we do!

for f in all(frames) do
 --draw colorful background (from sprites)
 --draw the people

The ‘clip()’ and ‘camera()’ calls lets us draw our content as if we were drawing it on a new screen, at the 0,0 position and with nothing happening if we draw anything outside the screen. It’s super handy. ‘clip()’ is very underrated.


Let’s finish off with something rather simple.

This is the second simplest way to do fade-outs/fade-ins in my opinion. Store your screen in the sprite-sheet and then draw the whole sprite-sheet using ‘spr(0,0,0,16,16)’ but with this snippet beforehand:


for c=0,15 do
 local nc=c
 for i=0,fadestep do

‘drk’ is a map of the 16 colors to their darker counterpart. Multiple variants of that map are valid. You can read some more about the Pico-8 palette and the relation between the colors in these Doodle Insights!

This will effectively draw your screen with darker colors until it’s completely black!

By the way, the actual simplest way to do fade-ins/fade-outs in my opinion is to use that same snippet somewhere in your ‘_draw()’, but with ‘pal(c,drk[nc],1)’. That ‘1’ parameter at the end will make the palette swap apply only when the frame displays, letting you use regular ‘pal()’s in the rest of your ‘_draw()’!


The biggest challenge with masking and layering is to correctly coordinate your memory copying and drawing so that everything works correctly and performs well. But the idea behind it is very simple and it can prove very useful, even if a bit tough on the CPU. And in Pico-8 you can even change what colors are transparent or not when drawing your layers/masks, which can be super useful! But those effects can also be faked in some cases, with just ‘sspr()’ or with ‘clip()’. Also remember that when using layers, masks or simple screen-saves, you really should feel free to apply more effects to them, like palette swapping!


And that’s our final Doodle Insights about sprite-sheet reading and writing in Pico-8! I hoped you liked it! Those tricks are some of my favorites and I’m happy I could share them with you!

Next week we’ll be seeing something very different I think! It will probably be about data-oriented stuff and under-lying systems! Those are more fun than they sound, I promise!

I want to thank my awesome Patreon patrons for supporting this series, among the rest of my works! Here are the names!

Joseph White, Adam M. Smith, Ryan Malm, Matthew, Giles Graham, Luke Davies, Jake Meanwell, Tim and Alexandra, Sasha Bilton, berkfrei, Nick Hughes, Christopher Mayfield, Jearl, Dave Hoffman, Thomas Wright, Morgan Jensen, Zach Bracken, Cole Smith, Paul Nicholas, Marty Kovach, Anne Le Clech, Flo Devaux, Brent Werness, babyjeans, Emerson Smith, Cathal O’Keeffe, Dan Sanderson, Andrew Reist, vaporstack, Dzozef, Jared Butowsky, Tony Sarkees and Justin TerAvest!

If you enjoy this series and are not a patron yet, please consider supporting it too!

Thank you for reading and enjoy the layering!


One thought on “Doodle Insights #15: Masking and Layering! (Sprite-Sheet Extravaganza part 3)

Add yours

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Blog at

Up ↑

%d bloggers like this: