Building Levels with Tilemaps in Phaser
Tile-based level design is the backbone of 2D game development. From classic platformers like Super Mario Bros to modern roguelikes and strategy games, tilemaps provide an efficient way to build large game worlds from a small set of artistic assets. Phaser's tilemap system integrates directly with the Tiled editor, giving you a visual workflow for designing levels and a powerful API for interacting with them in code.
Design Your Map in Tiled
Tiled is a free, open-source map editor that has become the industry standard for 2D tilemap creation. Download it from mapeditor.org and create a new map by specifying the tile size (commonly 16x16, 32x32, or 64x64 pixels), the map dimensions in tiles, and the orientation (orthogonal for standard top-down or side-view games, isometric for angled perspectives).
Import your tileset image into Tiled by creating a new tileset. A tileset is a grid of tile images, often called a tile sheet, where each cell contains one tile that you can paint onto the map. You can use multiple tilesets in a single map, which lets you combine terrain tiles, decoration tiles, and interactive element tiles from separate image files.
Create separate layers for different visual depths. A typical setup includes a background layer for sky or distant scenery, a ground layer for the main terrain the player walks on, a decoration layer for objects like bushes, signs, and crates that appear in front of the ground but behind the player, and a foreground layer for elements like tree canopies or cave entrances that render in front of the player.
Assign custom properties to tiles using Tiled's tile property editor. The most important custom property is a boolean called "collides" that you set to true on solid tiles like ground, walls, and ceilings. Phaser can read this property and automatically generate collision bodies for those tiles. You can also add properties like "damage" for hazard tiles, "slippery" for ice tiles, or "climbable" for ladder tiles, and read these properties in your game code to modify player behavior.
Export and Load the Tilemap
Export your map from Tiled as a JSON file. Make sure the tile layer format is set to CSV or Base64 (uncompressed) in the map properties, as Phaser reads these formats natively. Save the JSON file and your tileset images in your game's assets folder.
In your Phaser scene's preload method, load the tilemap JSON with this.load.tilemapTiledJSON, providing a key and the path to the JSON file. Then load each tileset image with this.load.image, using a key that matches the tileset name you defined in Tiled. Phaser needs this name match to associate the correct image with the correct tileset data when building the map.
In the create method, build the tilemap by calling this.make.tilemap with the key you used during loading. This returns a tilemap object that contains all the layer data, tileset references, and object information from your Tiled export. Next, add each tileset to the tilemap with map.addTilesetImage, passing the tileset name from Tiled and the Phaser image key.
If your tileset images use margins or spacing between tiles, specify these values in the addTilesetImage call to match the settings in Tiled. Mismatched spacing causes tiles to render with visible seams or display portions of neighboring tiles.
Create Tile Layers
Create each visual layer from the tilemap data with map.createLayer, passing the layer name as it appears in Tiled and the tileset or array of tilesets used by that layer. The method returns a TilemapLayer game object that you can position, scale, set depth on, and apply effects to like any other Phaser game object.
Layer depth determines rendering order. Set the depth property on each layer to control which layers appear behind the player and which appear in front. The background and ground layers typically have negative or zero depth, while foreground layers that should overlap the player have a higher depth value than the player sprite.
For parallax scrolling effects, set the scrollFactor on background layers to a value less than 1. A scrollFactor of 0.5 makes the layer scroll at half the speed of the camera, creating the illusion of distance. Distant mountains might use 0.2, mid-ground trees might use 0.5, and the main ground layer uses 1.0. This simple technique adds significant visual depth to side-scrolling games.
If your map is larger than the screen, the camera and tilemap system work together to render only the visible portion. Phaser automatically culls tiles outside the camera view, so a map that is 500 tiles wide performs just as well as one that is 50 tiles wide because only the visible tiles are drawn each frame.
Set Up Tile Collision
The most common approach to tile collision is property-based. If you added a "collides" property to solid tiles in Tiled, call map.setCollisionByProperty on the relevant layer, passing an object like { collides: true }. Phaser scans every tile in the layer and generates physics bodies for tiles matching the criteria.
Alternatively, you can set collision by tile index with map.setCollision or map.setCollisionBetween. This approach is quicker for simple tilesets where you know the index numbers of solid tiles, but it becomes harder to maintain as your tileset grows. Property-based collision is more robust because it stays correct even if you rearrange tiles in the tileset image.
After marking collidable tiles, add a collider between the player and the tilemap layer with this.physics.add.collider(player, groundLayer). Now the player will land on platforms, hit walls, and bump against ceilings defined in your Tiled map. The physics engine merges adjacent collidable tiles into larger rectangular bodies automatically, reducing the collision computation workload for large maps.
One-way platforms, which the player can jump through from below but land on from above, require special handling. Set the collision on only the top face of the platform tiles with layer.setTileCollision or by checking the collision direction in a process callback. This lets the player pass through the platform when jumping up but collide with it when falling down, a common mechanic in platformer games.
Use Object Layers for Game Elements
Object layers in Tiled let you place points, rectangles, polygons, and tile objects at precise positions on the map. These are not rendered as part of the tilemap but provide positional data that your game code reads to spawn entities. Common uses include player spawn points, enemy positions, collectible locations, level exit triggers, and camera boundary markers.
Read objects from a layer with map.getObjectLayer(layerName).objects, which returns an array of objects with x, y, width, height, and custom properties. Loop through this array to create the corresponding game objects in your scene. For example, iterate over objects in an "enemies" layer and create an enemy sprite at each position, using a custom "type" property to determine which enemy class to instantiate.
Tiled also supports tile objects, where you place a specific tile from your tileset as an object rather than painting it on a tile layer. This is useful for interactive items like doors, chests, switches, and signs that need both a visual representation and custom behavior. Phaser can create sprites from tile objects with map.createFromObjects, automatically positioning and sizing each sprite based on the Tiled data.
Polygon and polyline objects define custom shapes for trigger zones, patrol paths, and terrain boundaries. You can read the vertex data and create corresponding physics bodies, path followers, or zone triggers. This lets level designers define complex areas in Tiled's visual editor rather than hardcoding coordinates in JavaScript.
Modify Tiles at Runtime
Dynamic tile manipulation opens up gameplay possibilities that static maps cannot provide. Use layer.putTileAt(tileIndex, x, y) to place a tile at a specific grid position, and layer.removeTileAt(x, y) to clear a tile. These operations update both the visual display and the collision data immediately.
Destructible terrain is a popular application: when the player's attack or an explosion hits a breakable tile, remove it from the layer and optionally spawn debris particles. You can check what type of tile is at a position with layer.getTileAt(x, y) before deciding how it should react. Stone tiles might require multiple hits, dirt tiles break in one hit, and metal tiles might be indestructible.
Procedural level generation uses tile manipulation to build maps from algorithms rather than hand-designed layouts. Generate a 2D array of tile indices using cellular automata for cave systems, binary space partitioning for dungeon rooms, or wave function collapse for varied but coherent landscapes. Then paint the array onto a blank tilemap layer with nested loops and putTileAt calls.
Tile replacement is useful for environmental state changes. When the player pulls a lever, replace closed-door tiles with open-door tiles. When water rises, replace air tiles with water tiles row by row. When seasons change, swap the entire tileset from summer grass to autumn leaves. These changes are visually dramatic but computationally cheap because you are only swapping index values in the tile grid.
Design levels visually in Tiled, export as JSON, and let Phaser handle rendering, collision, and object placement. Use tile properties for collision and object layers for entity spawning so level designers and programmers can work with the same map file.