technique
Isometric pixel art
How to draw on the 2:1 pixel line, build the diamond tile and the three-face cube, place and sort tiles from their map coordinates, and keep line angle and light consistent.
Before this
This page assumes you are comfortable with:
- prerequisiteProjections and viewsOrthographic versus perspective, the four camera angles 2D games actually use, what each one shows and hides, and why isometric pixel art runs on a 2 to 1 line instead of true 30 degrees.
- techniquePixel art fundamentalsPick one canvas resolution and one pixel size for the whole game, then place every pixel on purpose so lines, curves, and clusters read cleanly at 1x.
Why you need this
Isometric is the view that shows a top face and two side faces of everything at once, which is why strategy games, city builders, and dungeon crawlers use it: the player reads depth and height without a moving camera. In pixel art it comes with one unbreakable rule, the 2:1 line, and a small amount of arithmetic to place tiles on screen. This page belongs to stage 2 of the pipeline, draw the still, and its placement math feeds stage 4, build the world. Read Projections and views first for what isometric shows and hides compared with side view and top-down.
The idea
The 2:1 line
True isometric projection puts the ground axes at 30 degrees from horizontal. A pixel line at 30 degrees has an uneven stair pattern that looks broken at 1x. Pixel isometric instead uses a line that rises 1 pixel for every 2 pixels across, about 26.6 degrees. Every ground edge in the whole game is drawn with this stair, two across then one up, and nothing else: not 1:1, not 3:1, not a run of three here and a run of two there.
. . . . . . 1 1
. . . . 1 1 . .
. . 1 1 . . . .
1 1 . . . . . .
1 line, . empty. Two pixels across, one up, repeated.
Because every run is exactly 2, the line is a rhythm the eye locks onto, and any run of 1 or 3 in a ground edge reads as a bump.
The tile diamond
An isometric ground tile is a diamond twice as wide as it is tall, because that is the shape a square makes when drawn with the 2:1 line. Call the tile width and the tile height . Common sizes are 32x16 and 64x32; this page draws at 16x8 so the grids fit. The width must be a multiple of 4, so that both and , which the placement math needs, are whole pixels.
The filled diamond, with its four edges labeled:
. . . . . . a a b b . . . . . .
. . . . a a t t t t b b . . . .
. . a a t t t t t t t t b b . .
a a t t t t t t t t t t t t b b
. . d d t t t t t t t t c c . .
. . . . d d t t t t c c . . . .
. . . . . . d d c c . . . . . .
. . . . . . . . . . . . . . . .
t top face, a upper-left edge, b upper-right edge, d lower-left edge, c lower-right edge, . transparent.
Row widths are 4, 8, 12, 16, 12, 8, 4: 64 pixels, and the eighth row is empty. That is not an accident. Neighbors sit at offsets of , and 64 pixels is exactly the area each tile owns in that arrangement, so these diamonds fit together with no gaps and no overlap. If you draw a symmetric outline with two rows of width 16, the widest rows of neighboring tiles overlap by one row, which is fine for outlines drawn back to front and wrong for opaque fills.
The cube
A block is a diamond with two faces hanging under it. Draw the top diamond, then from its lower-left edge (d) drop the left face, and from its lower-right edge (c) drop the right face, each a parallelogram pixels tall. Shade the three faces with three values from one ramp. With light from the upper left, the top face is the lightest, the left face is mid, and the right face is darkest.
A 16x8 cube with , on a 16x16 canvas:
. . . . . . 3 3 3 3 . . . . . .
. . . . 3 3 3 3 3 3 3 3 . . . .
. . 3 3 3 3 3 3 3 3 3 3 3 3 . .
3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
2 2 3 3 3 3 3 3 3 3 3 3 3 3 1 1
2 2 2 2 3 3 3 3 3 3 3 3 1 1 1 1
2 2 2 2 2 2 3 3 3 3 1 1 1 1 1 1
2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1
2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1
2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1
2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1
2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1
. . 2 2 2 2 2 2 1 1 1 1 1 1 . .
. . . . 2 2 2 2 1 1 1 1 . . . .
. . . . . . 2 2 1 1 . . . . . .
. . . . . . . . . . . . . . . .
3 top #cbbfae, 2 left face #9a8a86, 1 right face #6b5a66, . transparent.
Three checks. Every slanted edge is 2:1. The vertical edges (columns 0 and 1 on the left, 14 and 15 on the right, and the split between columns 7 and 8 down the middle) are straight. The three values are three distinct steps of one ramp, not one color at three brightnesses picked by eye. The bottom seven rows of the cube are a diamond the same shape as the top, which is the cube's footprint on the ground; the cube image is wide and tall, and you draw it pixels above where the ground tile would go so that footprint lands on the tile. A unit cube has , so a 32x16 tile gets a 16-pixel-tall cube.
Placing tiles on screen
Map coordinates are : increases toward the lower right of the screen, toward the lower left. Screen coordinates are , right, down, origin top-left, and the formulas place the top-left corner of the tile's bounding box:
For a 32x16 tile, and . Tile lands at and . Since goes negative when , add an origin offset of , where is the map height in tiles, so the leftmost tile starts at .
Draw order
Tiles and sprites overlap, so they must be drawn back to front, which is the painter's algorithm. The back of an isometric map is the top of the screen, where is small. Sort everything by ascending, and within one cell by height ascending, so a block is drawn before the block stacked on it. A sprite standing on a tile sorts by that tile's , not by its own pixel position.
const W = 32, H = 16; // tile size in pixels
function toScreen(tx, ty, z, mapH) {
const originX = (mapH - 1) * (W / 2);
return { x: (tx - ty) * (W / 2) + originX, y: (tx + ty) * (H / 2) - z * (H / 2) };
}
function drawOrder(things) { // things: [{tx, ty, z, ...}]
return [...things].sort((a, b) => (a.tx + a.ty) - (b.tx + b.ty) || a.z - b.z);
}
toScreen(2, 1, 0, 3); // { x: 48, y: 24 }
Height and stacking
One elevation level is pixels: a thing at level is drawn pixels higher on screen than the same thing at level 0, which is the term in the code. A unit cube is two levels tall, since its vertical edge is , so a block stacked on a block sits at and is drawn pixels higher. A half-height step or a ramp is one level. Nothing about a thing's map coordinates changes with height; only its screen and its sort tie-break do.
Curves and circles
A circle lying on the ground becomes an ellipse exactly twice as wide as it is tall, because the ground plane is squashed 2:1. A round well, a pond, a coin on the floor: all 2:1 ellipses. The runs along an ellipse are not constant, they shorten toward the sides, which is how you tell a drawn ellipse from a diamond drawn lazily:
. . . . . 1 1 1 1 1 1 . . . . .
. . . 1 1 . . . . . . 1 1 . . .
. 1 1 . . . . . . . . . . 1 1 .
1 . . . . . . . . . . . . . . 1
1 . . . . . . . . . . . . . . 1
. 1 1 . . . . . . . . . . 1 1 .
. . . 1 1 . . . . . . 1 1 . . .
. . . . . 1 1 1 1 1 1 . . . . .
1 outline, . empty. A 16x8 ellipse; runs of 6, 2, 2, 1 from the top down to the widest row.
A vertical cylinder, a tower or a barrel, is an ellipse on top, two straight vertical sides, and the lower half of an ellipse at the bottom, shaded with the ramp running from light on the upper-left side to dark on the right.
Characters
Characters in isometric games are almost never drawn in true isometric. They are drawn upright, as in a side view or three-quarter view, standing on the tile's center, with their pivot at the bottom-center of the sprite. What makes them belong is the facing set: 4 directions (along the four tile edges) at minimum, 8 if the game moves diagonally, each with its own walk cycle. A 32x16 tile usually carries a character 24 to 40 pixels tall; taller than the tile is normal, since the character stands on it rather than inside it. See Character design for sprites for the silhouette work.
Worked example
A 3x3 map of 32x16 tiles, , so the origin offset is . Screen position of each tile's bounding-box corner at level 0:
| raw | with offset | draw order | ||||
|---|---|---|---|---|---|---|
| (0, 0) | 0 | 0 | 0 | 32 | 0 | 1 |
| (1, 0) | 1 | 1 | 16 | 48 | 8 | 2 |
| (0, 1) | -1 | 1 | -16 | 16 | 8 | 3 |
| (2, 0) | 2 | 2 | 32 | 64 | 16 | 4 |
| (1, 1) | 0 | 2 | 0 | 32 | 16 | 5 |
| (0, 2) | -2 | 2 | -32 | 0 | 16 | 6 |
| (2, 1) | 1 | 3 | 16 | 48 | 24 | 7 |
| (1, 2) | -1 | 3 | -16 | 16 | 24 | 8 |
| (2, 2) | 0 | 4 | 0 | 32 | 32 | 9 |
The whole map is 96 pixels wide, three tiles across its widest row, and pixels tall. Tiles with the same sum, such as and , sit side by side at the same and never overlap, so their relative order does not matter.
Put a unit cube on . Its image is 32 wide and tall, drawn 16 pixels above the ground tile, at . Its top diamond covers parts of the ground tiles at , , and , which have sums 0 and 1 and were drawn earlier, so the cube correctly hides them. Its two faces fit exactly against the diamonds of and without overlapping them, which is the tessellation from the diamond section doing its job. Stack a second cube on the first at : it draws at , after the first because of the tie-break, and the negative says the map needs a top margin at least as tall as its tallest stack.
In a game's art pipeline
Isometric is a stage 1 decision before it is a stage 2 drawing: choosing it fixes the tile shape (a diamond with width a multiple of 4), the light direction (upper left, so top, left, right are light, mid, dark on every block), and the character facing count (4 or 8). Stage 2 draws the diamond, the cube, and the character. Stage 4 places them with the formulas above, and the map cluster's generators supply the grid exactly as they do for top-down; only the screen mapping changes. Stage 5 exports each tile with its bounding box and pivot recorded, since a tile's drawn pixels no longer fill its rectangle.
Common mistakes
- A ground edge with a kink in it. A 1:1 or 3:1 run crept into a 2:1 line. Redraw the edge as strict pairs.
- Hairline gaps or doubled edges between tiles. The tile width is odd or not a multiple of 4, or the diamond has two rows of full width. Use 16, 32, or 64, and the 4, 8, 12, 16, 12, 8, 4 row pattern.
- A character pops in front of a wall it is behind. Sprites are sorted by screen instead of by , or a sprite is sorted by its own pixel position rather than the tile it stands on.
- Blocks look inside-out or lit from two directions. The left and right faces have the wrong values, or one block uses a different ramp order from its neighbors. Top lightest, left mid, right darkest, every time.
- Round things look like diamonds. A circle on the ground was drawn as a 2:1 diamond instead of a 2:1 ellipse.
- Characters drawn in true isometric. They read as tilted. Draw them upright and let the facing set do the work.
Cost
Isometric is the most expensive view per asset. Every block shows three faces, so a tile that would be one drawing in top-down is three shaded regions that must agree with every neighbor. Every character needs 4 or 8 facings, each with its own walk cycle, where a side-scroller needs one facing and a flip. A 32x16 tileset with cubes, slopes, and a few props is a few dozen drawings; a character with 8 facings and a 6-frame walk is 48 frames before idle and attack. Screen space costs too: a 3x3 map of 32x16 tiles is 96 by 48 pixels, and a map of by tiles is wide, so isometric shows fewer cells per screen than top-down at the same tile size. Placement and sorting are cheap by comparison: one multiply and add per tile, and one sort per change.
Going further
- Shading and lighting, for the ramp the three faces are drawn from.
- Character design for sprites, for the upright characters that stand on the tiles.
- Tileable textures and tilesets, for making the top face texture repeat across the diamond grid.
- Sprite sheets and export, for recording the bounding box and pivot of a diamond tile.