blog by rae

Playdate Exit Animations

Jun 16th, 2024   •   blog


If you’ve come across this blog post, you’re likely a Playdate dev who’s just seen something like this —

— a cool animation that happens after the player exits back to the Launcher, providing a nice smooth transition. And you may be wondering how to do that for yourself! Luckily, this is the authoritative guide on Making a Cool Exit Animation for your Playdate Game. Let’s go!

The base of this trick is in the playdate.gameWillTerminate() function. This allows you to run code after the player exits the game to go home.¹ Ordinarily, this function is used for reasonable things, like saving the game’s state. However, with some funny little trickery, we can use this for whatever we want before the game closes.

There are two more special things that make this trick possible: while loops, and playdate.display.flush().

If you know Lua code already, you should be familiar with while loops — they catch the code and keep it running a specific loop until its condition is not met anymore. For example, something like this…

local foo = 0
while foo ~= 10 do
	foo = foo + 1

…will loop through 10 times, incrementing foo by 1 each time. When it reaches 10, the while loop’s condition stops being met, and so it stops iterating.

playdate.display.flush() is responsible for manually updating the Playdate screen. When you’re running functions such as playdate.gameWillTerminate(), your game’s usual update code is not being run. On one hand, this is nice because it means you don’t have to worry about your game’s code continuing to update and possibly messing stuff up while your game is paused or closing. On the other hand, it means playdate.update(), which is responsible for updating the screen normally, isn’t running at all. So this function’s important to make sure things are displaying.

We can combine all these components to make something graphical happen over a period of time, upon the playdate.gameWillTerminate() function being called. Here is an example code snippet demonstrating a basic use case:

function playdate.gameWillTerminate()
	local img = playdate.graphics.getDisplayImage()
	local byebye = playdate.graphics.imagetable.new('byebye')
	local byebyeanim = playdate.graphics.animator.new(1500, 1, #byebye)
	playdate.graphics.setDrawOffset(0, 0)
	while not byebyeanim:ended() do
		img:draw(0, 0)
		byebye:drawImage(math.floor(byebyeanim:currentValue()), 0, 0)

Let’s break this down quickly.

When the playdate.gameWillTerminate() function is called:

After the animation has ended, the while loop will stop iterating, and the OS will close your game as normal.

A couple more notes about usage:

Swap Machina was the first game to apply this tactic, adding it in a post-launch update. This game’s dev, NaOH, gets all the credit for discovering how to put all this together and make a super cool effect! I just like spreading the word about how to use it because it’s a really neat trick and every dev I’ve seen put it in a game seems to use it in a super unique way. You can see the original source code⁴ for the effect in Swap Machina here (link to within the Playdate Squad Discord server). You should also check out Swap Machina on Catalog — it’s pretty great!

Here are a few more examples of games with cool-as-heck exit animations:

Anyway, that should be all you need to know to get started! If you make a rad exit animation for your game, feel free to let me know! I’d love to see it.

¹It should be noted that there are similar functions for things such as locking & unlocking the device, opening and closing the system menu, and letting the device go to sleep as it runs out of battery. This tactic could work with these as well, but just keep in mind the player’s patience level as these can be more repetitive actions (for example, a player wouldn’t wanna see a 10-second animation play out every time they open the menu). Also, when it comes to device unlocking, your regular update code starts running just before the function is called.

²When using this technique, make sure that your code doesn’t run for over 10 seconds consecutively. Playdate has a “watchdog” that constantly checks the last time playdate.update() has been called. If it hasn’t run in over 10 seconds, it will assume your game is stuck in an endless loop or otherwise frozen (because it, technically, is); and crash the whole system to bail the user out of it.

³Combine this with the points brought up in footnote 1, and you can do hilarious stuff like this.

⁴Swap Machina was created with Pulp, and then converted to Lua to add extra juice using their Pulp Mill tool. Unfortunately, I don’t think it’s possible to implement such an effect in a Pulp game without first converting it to Lua. Sorry!