Rendering a Map

Rendering converts a 2D array into a visual grid. For each value in the map, draw a coloured rectangle at the corresponding pixel position.

Array:           Visual result:
[1, 1, 1, 1] β†’  🧱🧱🧱🧱
[1, 0, 0, 1] β†’  🧱  🌟🌟🧱  
[1, 1, 1, 1] β†’  🧱🧱🧱🧱

Each 1 becomes a solid wall, each 0 becomes walkable space.
Loading editor…

Map data and tile definitions

The map and tile type definitions from the previous tutorial, ready to use:

const myMap = [
    [1, 1, 1, 1, 1, 1, 1, 1],
    [1, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 1, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 1, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 1],
    [1, 1, 1, 1, 1, 1, 1, 1]
];

const game = {
    tileW: 30,
    tileH: 30
};

const TileTypes = {
    FLOOR: { id: 0, walkable: true,  color: 0x003311, name: 'floor' },
    WALL:  { id: 1, walkable: false, color: 0x00ff41, name: 'wall'  }
};

game.tileW and game.tileH define the pixel size of each tile. Tiles don’t have to be square β€” a 64Γ—64 tile gives a chunky retro look, a 32Γ—8 strip works for precision platforming. Changing these numbers changes the scale of the entire world.

Rendering the map

The buildMap function loops over every position in the array, creates a graphic for the tile type at that position, and places it at the correct pixel coordinates:

async function buildMap(map, containerId) {
    const app = new PIXI.Application();
    await app.init({
        width: map[0].length * game.tileW,
        height: map.length * game.tileH,
        backgroundColor: 0x2c3e50,
        antialias: true
    });

    document.getElementById(containerId).appendChild(app.canvas);

    const tileContainer = new PIXI.Container();
    app.stage.addChild(tileContainer);

    for (let row = 0; row < map.length; row++) {
        for (let col = 0; col < map[row].length; col++) {
            const tileId = map[row][col];
            const tileType = TileTypes[tileId === 0 ? 'FLOOR' : 'WALL'];

            const tileGraphics = new PIXI.Graphics()
                .rect(0, 0, game.tileW, game.tileH)
                .fill(tileType.color)
                .rect(0, 0, game.tileW, game.tileH)
                .stroke({ width: 1, color: 0x00ff41, alpha: 0.3 });

            tileGraphics.x = col * game.tileW;
            tileGraphics.y = row * game.tileH;

            tileContainer.addChild(tileGraphics);
        }
    }

    return app;
}

The position formula is col * tileW for x and row * tileH for y. Tile at column 3, row 2 appears at pixel 90, 60 (with 30px tiles). Every tile in the grid knows exactly where it belongs.

The PIXI.Container for tiles isn’t strictly necessary at this scale, but grouping all tile graphics into one container makes it easy to clear or move the entire tile layer later β€” useful when implementing room transitions.

Call it like this:

const mapApp = await buildMap(myMap, 'my-canvas-container');

What you built:

  • A buildMap function that translates a 2D array into a PixiJS canvas
  • Tile type definitions that map array values to colours and properties
  • The nested-loop pattern that powers tile rendering in any tile-based game

Next: The Hero