Last week I decided I wanted to port Upward to the Playdate. So, now what? Well, other than creating a new Visual Studio Code project using my Playdate project template for Zig (shameless plug), the first thing I wanted to do is write out a list of questions to start with. Here are the questions I started with:
- What resolution is the game going to be in?
- What is the target framerate?
- Should I keep the PICO-8 API in the game code and write a small PICO-8 API implementation, or modify the game code to directly call out to the Playdate APIs?
- Should I use 16.16 fixed point numbers like PICO-8, or use floating point?
- What is the first thing I want to get on the screen?
The first question I wanted to tackle was the resolution question:
What resolution is the game going to be in?
PICO-8 has a resolution of 128×128 pixels (1:1 aspect ratio) and the Playdate has a resolution of 400×240 pixels (5:3 aspect ratio). With the aspect ratios being completely different, I had 2 choices:
- Just keep the resolution of the PICO-8 and stick the game in the middle of the screen and deal with the large border around it.
- Try to modify the graphics and levels to take advantage of the extra resolution
Choice 2. is probably the ideal choice, but unfortunately, that would take a significant rewrite of everything and the whole point of this project was to keep things manageable. So choice 1. it is. So lets see what a 128×128 view would look like on the Playdate:

Uhh, yea that’s a bit small. So what’s the highest resolution we can get to that would we can get to that would fit on the screen? Ideally, we’d like it to be a whole number multiple of 128×128. So, 2x? Well, 2×128 = 256, which _just_ over Playdates 240 pixel vertical resolution. That’s a shame.
What now? Well, PICO-8 graphics are made up of of 8×8 pixel cells, and luckily all of the main sprites of Upward are drawn to be 8×8. How high can we can we extend the cell resolution? Can’t do 16×16 since that make the resolution 256×256, which is too big. We can try 15×15 (which actually ends up fitting perfectly), but I felt that making the resolution an odd number would add more work into redoing the graphics (feel free to call me out on this if I’m wrong!). Thus, the best resolution for a cell is 14×14. The new resolution of the game would be (14/8)*128 = 224 -> 224×224! Let’s see what 224×224 looks like:

That’s much better! Of course there is a lot of unused screen real estate, but won’t strain your eyes at least. With that decided, let’s move on to the next question.
What is the target framerate?
PICO-8 supports 2 target framerate modes: 30 FPS (when the _update
function is defined) and 60 FPS (when _update60
is defined). The Playdate supports a max of 48-49 FPS (if you do partial frame refreshes, you can get 50 FPS, but that’s out of scope here). So, if I can support 48-49 FPS, why not?
Upward targets 30 FPS. I modified the code to run at 60 FPS (defining the _update60
function), and it turns the player and enemies move far too quickly to be playable. So as of now, I am targeting 30 FPS. I don’t believe it’d be too difficult to support 48-49 FPS, but it would take some gameplay code modification.
Should I keep the PICO-8 API in the game code and write a small PICO-8 API implementation, or modify the game code to directly call out to the Playdate APIs?
I first looked to see what PICO-8 API calls were being used. Before I even started this project, I vetted the code to make sure that tricky APIs like memcpy
, peek
, and poke
weren’t used. PICO-8 is a virtual machine that has it’s own memory map. For this project, I wanted to avoid having to implement these memory-mapped facilities since they are typically used for interesting graphics and sound effects. Luckily only APIs that map well to Playdate’s API are used. Here are some examples:
spr
— Maps well toplaydate->graphics->drawBitmap
.sspr
— Maps well to usage ofplaydate->sprite->setClipRect
andplaydate->graphics->drawBitmap
together.map
— Maps (pun intended) well to usage ofplaydate->graphics->getTableBitmap
Since these APIs have a simple mapping, I decide to keep the PICO-8 API calls and write implementations of them, inlining when possible.
Should I use 16.16 fixed point numbers like PICO-8, or use floating point?
PICO-8 only has 1 number data type which is a signed 16.16 fixed point number. However, the Playdate has an FPU which means using 32-bit floating point is also an option. Looking at the instruction timings, fixed point seems to be potentially 2 times faster due to the dual-issue instructions here (but only in very specific situations, otherwise, they appear to be the same speed). In addition, fixed point is more “true-to-the-code” here and you don’t have to deal with any potential rounding issues floating point gives you.
But, for ease of porting I decided to use floating point, for now. Why is it easier to use floating point on the Playdate, even though PICO-8 uses fixed point? Well, consider the following PICO-8 code:
rectfill(0, 56, 127, 72,0)
This code draws a black rectangle where the top left is at (22, 56) and the bottom right is at (127, 72). With when porting using floating point, the Zig code looks like:
rectfill(0, 56, 127, 72, .BlackColor, game);
Zig auto converts all the number literals to floating point values. But, with fixed point, the integer lives in the upper 16 bits of the 32-bit number. So, I’d have write the code to look something like this:
rectfill(fixed(0), fixed(56), fixed(127), fixed(72), .BlackColor, game);
Kind of annoying to deal with. Plus, the fact that I am targeting 30 FPS means that the win for using fixed point is probably not as big here. I am open to changing to fixed point later if I encounter issues, however. And finally, our last (and probably our most important) question…
What is the first thing I want to get on the screen?
Luckily Upward has a very simple game-state structure, a title screen and the game itself. That’s it. Even the pause screen is handled by the built-in PICO-8 pause screen, so we don’t really have to worry about that since the Playdate also has a built-in pause screen!
With this knowledge, the first thing I want to get on the screen is the title screen. In order to do this, I not only will do I have to write the code, but also modify the the 8×8 sprites to be 14×14 sprites. To do this I:
- Used PICO-8’s
EXPORT
command that to export all of the sprites into one 128×128 (16×16 sprites) PNG file. - Loaded that PNG file into Photoshop and enlarged it to 224×224 pixels with “Nearest Neighbor” resample selected, so there are no “fuzzy” pixels.
- Since we are not doing an integer multiple of 128, the sprites get a bit distorted, unfortunetly. So, I went in with a 1-pixel pencil tool and did the best I could.
Progress Report
I am happy to report that I do have the title screen working! (Unfortunately, I can’t embed it here because WordPress does something weird with GIF). As you can see the font is not PICO-8 font, but I plan on changing that soon. I’ll use Panic’s Caps tool to create the font. Also, if you look very closely, the right side of the cliff and the left side of the cliff are mismatched by a couple of pixels. You can see it more clearly here :

I do hope to fix this during this project. But, I feel it’s a relatively low priority since it’s not that noticeable (at least to me, let me know if it bothers you!)
Next up is to get level 1 working!
I think that’s it for this post. As always, all questions and feedback are welcome!