Celeste Tilesets, Step-by-Step
Celeste is one of my all-time favorite games, not just because it’s perhaps the first hugely popular indie game made by trans developers with a trans protagonist or an extremely thoughtful depiction of mental illness, but also because it’s ✨gorgeous.✨
When I needed to make some pixel-art tilesets for my Metroidvania Generator, I spent some time reverse-engineering Celeste’s levels to understand how Pedro Medeiros made each room look so dang good. What I found was a really ingenious blend of procedural, randomized and hand-placed content that’s surprisingly easy to mimic once you know how it all works.
These tilesets are designed for efficiency; making a game with almost a thousand unique rooms is a ton of work, and the good folks at EXOK developed some really clever techniques that can save us all a lot of time and effort in both art and level design. I think these 8x8 tilesets look way better than many 16x16 tilesets while requiring an order of magnitude less work. Seriously, they’re that good.
To make this guide, I deconstructed and re-built the first room of the Forsaken City so I can show you how the art for these rooms is made, step-by-step. Along the way I’ll provide analysis and design rules that will help you replicate this game signature design style.
Big thanks up front to Pedro for his help proofreading my animations and observations! This guide is a lot more accurate thanks to his input. It also obviously wouldn’t exist without his original artwork and the efforts of everyone at EXOK, so thanks to all of them too!
With all that said, let’s bust this level open and see what makes it tick.
Size, Shape, and Resolution
Let’s start by opening up a screenshot in Aseprite and uniformly downscaling the image until one in-game pixel matches one pixel in our canvas. Then we can adjust the grid settings until the squares match up with the level geometry.
Doing so reveals some useful technical information about the game’s pixel art graphics:
The in-game camera renders at 320x180 pixels
The level geometry perfectly matches an 8x8 grid, which means each tile is an 8x8 pixel square.
Some things like the sign and snow piles don’t fit perfectly into a grid square — that’s important, and we’ll come back to it later!
There are some really good reasons why EXOK chose these particular shapes and sizes:
The 8x8 tile aesthetic is reminiscent of retro games — in fact, it’s the same tile size as the original Pico-8 version of Celeste!
320x180 is a 16:9 aspect ratio which upscales perfectly for display on HD displays without your pixels becoming non-square: 4x for 720p, 6x for 1080p, 8x for 1440p, etc.
Using 8x8 tiles instead of 16x16 means that level geometry can be way more detailed. Assuming rooms want to match the camera resolution, that means they can fit 40x22.5 tiles into each room instead of 20x11.25.
These tiny tiles come with a price, though — their small size means that they have less room for details… But Pedro has a clever solution for that which we’ll see later!
Basic Level Geometry
If we remove all the detail from the image and show only the elements that are relevant to gameplay, we’re left with just a background color, some blocky shapes representing the terrain (which we’ll call solids), and some spikes.
This kind of layout and visualization — sometimes called grayboxing — is a common first step in designing video game levels. It’s essentially the minimum amount of information needed for the level to be “playable.“ It’s way less expensive to discover gameplay problems with your level designs before you go through the process of making them look pretty!
While I don’t know that EXOK started with levels that looked like this, they definitely started building levels long before all the tilesets were done, and this is the easiest kind of tileset to make, since it’s made up of only one tile — an 8x8 solid gray square.
(Note that I’m not including the spikes in tileset, since they’re placed and rendered more like props, which we’ll cover later.)
Tileset Layout
In the original screenshot, we can see four different kinds of solids: brown dirt, blue rocks, gray metal beams, and green-gray stones (hiding at the top-right). These represent our tilesets — batches of similar tiles that are combined to form larger structures.
To visualize where these four tilesets will go, we can replace our gray tiles with four colored tiles — one matching each of the terrain types in the original screenshot.
At this stage, the player only gets a vague impression of what each color might represent, but this is still very useful to level designers and artists to help visualize the shapes and spaces that need to be filled with more detailed art.
Highlighting Edges
One way to improve the the readability of this level is to highlight edges in order to create contrast between the playable area and the background.
Looking back at the original screenshot, we can see that the tilesets actually blend into one another quite nicely because they all use the same dark color for the inside of solids.
Let’s try that ourselves by replacing all the internal tiles of all four tilesets with a dark, flat color, which we’ll call the infill.
That’s starting to look much more Celeste-y, and it only required creating one new tile, shared across all four tilesets!
The contrast between some of the colors and the background still isn’t great, though, so let’s try adding a one-pixel border around the external edge of each solid.
That looks pretty good — it’s way easier to tell what we can walk, stand, jump and climb on!
There’s a problem, though — adding that 1px border to the exterior edge of each solid requires a huge increase in the complexity of our tilesets. We need to make way more unique tiles — each color now needs a bunch of different variants to handle highlights on nearly all combinations of its edges and corners. Even if we mirror and rotate our tiles, that’s still easily 10x more unique tiles than we had last time!
That’s not just more work for the artist, either— the level designer also has to pick and choose which variant to use at each spot in the grid, which makes levels way more tedious to build. There’s a solution to that, though…
Auto-Tiling
Most modern engines used to make pixel art games (including Unity!) have some support for auto-tiling, which uses a set of rules to automatically determine which tile to place when painting a tileset onto a tilemap. These rules are usually pretty simple — they generally check the tile’s neighbors — the tiles above, below, left and right of it, and often its diagonals as well.
The awesome thing about auto-tiling is that the same rules can be used for different tilesets, as long as they’re designed around the same layout. I like this free auto-tiling layout and ruleset for Unity, but the actual arrangement of tiles in your sprite atlas isn’t as important as the fact that all your tilesets use the same layout, which lets you swap out the underlying sprites without changing any of the logic.
Auto-tiling is really the tool that allows level designers to stop thinking about tilesets when designing levels — they can basically paint the geometry in without ever thinking about how it needs to be rendered. This is also why I said EXOK almost certainly didn’t use graybox tilesets for very long — once you have an auto-tiling setup working, it’s so much easier to prototype using the tilesets you already have!
I really can’t recommend this enough. If you want to make pixel art games and you use tilesets, get familiar with these tools, especially since you’ll likely need to write some custom rules to support the particular visual style of your game. (More on that later!)
Edge Details
To better communicate the kind of terrain that each tileset is intended to represent, we need to add more details to our tiles.
This stage looks intimidating, but if we look closely, we can see that they follow some pretty straightforward patterns:
Each tileset uses only 3-5 colors
1-2 light colors (primarily used as edge highlights, like we did before!)
1 mid-tone (usually the most frequently used color in the tileset, generally used for the “bulk” of each tile)
1-2 dark colors (often placed between the mid-tone and the common infill color
If you’re having trouble choosing colors, just copy them from a tileset you like and hue-shift them into the color you want!
Areas of light and dark color can be used to imply 3D forms
Notice how the slightly-darker brown makes the lighter brown blobs feel like rounded rocks, and the blue-gray turns the white blobs into mounds of snow. Elsewhere in the game, some tilesets use dark colors for bottom edges and light colors for top edges, which gives the impression of light shining down onto the tiles from above.
You don’t have to totally understand this to get the vibe — play with it until it looks cool!
The tiles don’t usually take up the whole 8x8 square
Most edges have some transparent pixels that help break up the outer contour a little
The edge between tile and infill is never perfectly straight
The tiles themselves are tiny and impressionistic
There’s no room for fine detail in eight pixels
The low color count makes clusters of pixels blend together into bigger shapes
Thankfully, we’ve already built out all the auto-tiling variants for each color, so we already have a full tileset that we can use as a template.
If you’re feeling intimidated, try copying a tile directly out of the game, and then start making your own modifications. Notice how the brown tiles have repeated oblong “chunks” of pixels — those represent rocks, which don’t have fixed shapes, so you can’t really get them wrong!
Seriously, I think you will be surprised at how approachable this style is with the right scaffolding and the originals as reference. :)
You might notice something unfortunate about our tiles, though — when you line up a bunch of edge tiles in a long row, you start to notice the fact that they repeat perfectly every 8 pixels. Thankfully, Pedro has a solution for this…
Randomized Edge Variants
Compare the original screenshot to the previous step and you’ll notice that Celeste’s tilesets actually have1-4 variants of each straight edge tile, which are swapped in at random.
Randomized Infill Tiles
To add even more variation and depth to our tilesets, we can add an extra tile type between the exterior edge of each solid and the infill.
Each uses only the 1-2 dark colors from the tileset
Notice how the dark colors are usually toward the bottom — this provides some subtle shading
These tiles are generally just abstract collections of blobs, circles, diamonds, and rectangles. Again, the impressionist approach works really well here — it tricks our eyes into doing the hard work of filling in the details!
They never fill the entire tile, but they usually touch 1-2 edges. This is super important, because it combines with the low color count to blend infill tiles together into larger cohesive shapes. With enough random shapes touching and merging at random spots, you stop seeing the individual tiles altogether!
Some of them are just straight-up empty! Sometimes the best tile is no tile at all.
In Celeste, empty infill tiles seem to make up about 10-15% of all internal edges — find a number that feels right to you.
These are a little trickier to auto-tile because they require looking at neighbors two tiles away, which can become a problem with really inefficient tilemap systems like the native ones in Unity. One easy workaround would be to use two separate tilesets for each type of solid — one for the external edge, and different one for all the infill.
If we do the same math from earlier but for a 2x2 collections of two edge and two infill tiles, we can generate 1600 unique 16x16 tile equivalents. This is really where these tilesets shine — they provide just enough variation to avoid being repetitive while being relatively easy to design.
Foreground Props
Our tiles are looking really good, but they’re still trapped inside an 8x8 grid. Even with some transparent pixels along the edges, we’re still left with several long horizontal and vertical lines that make the solids feel just a little too rigid. We can solve this by adding in props — these are small sprites that can be overlaid on top of or behind our tilesets to disrupt their contours, providing just enough enough visual interest to make things feel more natural.
Notice how much work those piles of snow are doing — they’re just blobs of white pixels that blend into or contrast with the solid tilesets they’re placed against, and the same sprites are used multiple times within the same screen!
The green plants are just short vertical lines of green that our eyes interpret as vegetation.
Again, the tiny color palette is doing so much work here — if you’re not great at color, just start by stealing palettes from games you like and hue-shift them to match the tone of your game.
Background Tiles
Looking back at the original screenshot, we can see that there’s a layer of dark, blob-like shapes between the room solids and the painterly background. When Madeline moves, she passes in front of these shapes, which tells us that they exist on a separate background layer.
If you look closely, you’ll see some familiar friends — mostly-straight edges with with a hint of variation, slightly rounded corners, a limited color palette with a single common fill color… This is just another auto-tileset! It’s still low color-count, still very impressionistic, and it can even use the same auto-tiling rulesets!
The only unique thing about this tileset is that it has holes in it — they’re hard to see in this example because I specifically chose the flat background color to match this tileset, but you’ll see that there are transparent areas where the painted background can show through, later on.
Background Props
Like we did with the solid tilesets, we can add some props to the background layer to help break up the straight lines and add a bit of color contrast.
Notice how much narrative these details add to the room — suddenly it feels like this room has something to say, and it’s… just a little bit ominous? The blobby background tiles are now contextualized — they’re the crumbling remains of city buildings, complete with propaganda posters, pipes, tattered banners, and even a sign!
I can’t tell you how to draw more larger, more detailed like these, but Pedro’s made a collection of really incredible pixel art tutorials that’ll help you get there. In general, though:
Use as few colors as you can get away with, using light and dark to imply edges and details
Start with rectangles and round off the corners
Copy artists you like — pixel by pixel if you have to! — just don’t use exact replicas of their designs in games you’re going to release
Static + Parallax Backgrounds
It’s hard to tell from a single screenshot, but the painted backgrounds in Celeste level are a combination of both static and parallax layers, which peek through the background tiles and provide a far-off reference point to add even more depth to the level.
One thing to notice is that while the green trees are more vibrant than the background tiles, they’re still “pushed back” through the use of desaturaed colors, which simulates the effect of atmospheric interference, giving them a far-off look. They’re also significantly less bright and saturated than the solid tilesets, which ensures the playability of the level is preserved even as we add more and more artistic details.
This is really the place where the line between pixel-art and fine-art starts to blur! Pedro’s own tutorials can get you started, but there are plenty of YouTube tutorials on the subject, too!
Thankfully, you really can leave this to the end — I expect this stage is “icing on the cake” for most games, and this guide is structured to put the most important steps first. Even if you never end up with gorgeous hand-painted masterpieces as the backdrops for your levels, you’re still going to end up with rooms that look pretty dang cool.
Also consider just hiring a pro! It’ll cost way less to pay them to paint you something awesome than for you to become a badass landscape artist. ;)
Particles and Motion
To really make this room come alive, we can add snowy particle effects, as well as some subtle animation to the foreground and background props.
There’s actually at least two different particle systems going on here — one in front of the solids layer, and another between the background tiles and the painted backdrop.
Particles start out past the right edge of the camera, with some subtle variation in their gravity and velocity that causes them to curve subtly as they move across the screen. The particles themselves are just single pixels in white and light blue for the foreground, with a darker blue and gray in the background.
Because a single pixel is the smallest any one particle could get, the background particles are slower, giving them them the illusion of moving over a longer distance, even though they’re exactly the same size as their speedy foreground cousins.
Obviously not all games will need snow particles, but adding some kind of generative, procedural effect can really help differentiate game environments without adding any extra work to each individual room — once you build the particle system, you can use it anywhere you want. Efficiency!
There’s also a really subtle gradient filter applied to the entire scene through some kind of shader— I didn’t even know it was there until I started building out the animations for this guide and realized that Celeste’s tiles are slightly different colors on the left side of the screen than on the right!
I really do hope you’ll try this out for yourself! If you do, please do tweet the results at me so I can cheer you on. I had a blast figuring all this out.
If you want to check out my own versions of these tilesets, go check out my metroidvania generator.