Skip to main content

How to Use the Map System

v2 Written February 2026 Beginner

The engine has a built-in tilemap system. You place tiles on a grid with mset(), render a whole layer with map(), and the engine handles the rest — no manual loops through 2D arrays, no calling spr() for every cell.

We're gonna build a grass field, add layers for trees, manage separate maps for different areas, and scroll the camera across worlds bigger than the 128x128 screen. This one's purely about building levels — no player movement or collisions. If you want that stuff, the platformer tutorial has you covered.

All the tile art is from the Kenney 1-Bit Pack (CC0 — free for any use). The pack's tiles are 16x16, but the engine works in 8x8 cells, so each tile gets split into four quadrant sprites (_tl, _tr, _bl, _br) and placed as a 2x2 block. A tile() helper handles this throughout — you pass a position and a tile name, it calls mset() four times.

Your First Tilemap

mset(x, y, tile) places a sprite at a cell position by name. The screen is 128x128 pixels and each cell is 8x8. Our compound tiles are 16x16 — two cells wide, two cells tall — so we get an 8x8 tile grid. The tile() helper places all four quadrants in one call. Let's fill the grid with grass and carve out a dirt path:

engine.scope(({ start, cls, mset, map }) => {
    const sprites = {
        grass_tl: [[
              0,  0,  0,  0,  0,  0,  0,  0,
              0, 11,  0,  0,  0,  0,  0,  0,
              0, 11, 11,  0,  0,  0,  0,  0,
              0,  0, 11,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0, 11,  0,  0,
              0,  0,  0,  0,  0, 11, 11,  0,
              0,  0, 11,  0,  0,  0, 11,  0,
        ], null],
        grass_tr: [[
              0,  0,  0,  0,  0,  0,  0,  0,
              0, 11,  0,  0,  0,  0,  0,  0,
            11, 11,  0,  0,  0,  0, 11,  0,
            11,  0,  0,  0,  0, 11, 11,  0,
              0,  0,  0,  0,  0, 11,  0,  0,
              0,  0,  0, 11,  0,  0,  0,  0,
              0,  0,  0, 11, 11,  0,  0,  0,
              0,  0,  0,  0, 11,  0,  0,  0,
        ], null],
        grass_bl: [[
              0, 11, 11,  0,  0,  0,  0,  0,
              0, 11,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0, 11,  0,
              0, 11,  0,  0,  0,  0, 11, 11,
              0, 11, 11,  0,  0,  0,  0, 11,
              0,  0, 11,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
        ], null],
        grass_br: [[
              0,  0, 11,  0,  0,  0,  0,  0,
              0, 11, 11,  0,  0, 11,  0,  0,
              0, 11,  0,  0,  0, 11, 11,  0,
              0,  0,  0,  0,  0,  0, 11,  0,
              0,  0,  0,  0, 11,  0,  0,  0,
              0,  0,  0, 11, 11,  0,  0,  0,
              0,  0,  0, 11,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
        ], null],
        dirt_tl: [[
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  5,
              0,  0,  0,  0,  0,  5,  0,  5,
              0,  0,  5,  0,  0,  0,  0,  0,
              0,  0,  5,  5,  0,  0,  0,  0,
              0,  0,  0,  5,  0,  0,  0,  5,
              0,  0,  0,  0,  0,  0,  0,  5,
              0,  0,  0,  0,  0,  0,  0,  0,
        ], null],
        dirt_tr: [[
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  5,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  5,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              5,  0,  0,  0,  5,  5,  0,  0,
              5,  5,  0,  5,  5,  5,  0,  0,
              5,  5,  0,  5,  5,  0,  0,  0,
        ], null],
        dirt_bl: [[
              0,  5,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  5,  0,
              0,  0,  5,  0,  0,  5,  5,  0,
              0,  0,  5,  5,  0,  5,  0,  0,
              0,  0,  0,  5,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
        ], null],
        dirt_br: [[
              0,  5,  0,  5,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  5,  0,  0,  0,
              0,  0,  0,  0,  5,  5,  0,  0,
              5,  0,  0,  0,  0,  5,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
        ], null],
    };

    function tile(tx, ty, name, layer = 0, mapId) {
        const cx = tx * 2, cy = ty * 2;
        mset(cx, cy, name + '_tl', layer, mapId);
        mset(cx + 1, cy, name + '_tr', layer, mapId);
        mset(cx, cy + 1, name + '_bl', layer, mapId);
        mset(cx + 1, cy + 1, name + '_br', layer, mapId);
    }

    function init() {
        for (let y = 0; y < 8; y++) {
            for (let x = 0; x < 8; x++) {
                tile(x, y, 'grass');
            }
        }
        for (let x = 0; x < 8; x++) { tile(x, 3, 'dirt'); }
        for (let y = 0; y < 8; y++) { tile(3, y, 'dirt'); }
    }

    function draw() {
        cls(0);
        map(0);
    }

    start({ sprites, sounds: {}, init, draw, target });
});
Map System: First Tilemap
A grass field with a dirt crossroads rendered entirely from tilemap data

Two nested loops fill every position with grass. Two more overwrite the middle row and column with dirt, forming a cross. Each tile() call places four 8x8 sprites in a 2x2 block — the quadrant math is all internal. cls(0) clears to black and map(0) renders every cell on layer 0. Set the data once in init() and it persists across frames.

Adding Layers

Layers are separate grids that share the same coordinate space. Layer 0 is the ground. Layer 1 goes on top — trees, buildings, anything that should draw over the terrain. The fourth argument to tile() (which passes it to mset()) sets the layer:

engine.scope(({ start, cls, mset, map }) => {
    const sprites = {
        grass_tl: [[
              0,  0,  0,  0,  0,  0,  0,  0,
              0, 11,  0,  0,  0,  0,  0,  0,
              0, 11, 11,  0,  0,  0,  0,  0,
              0,  0, 11,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0, 11,  0,  0,
              0,  0,  0,  0,  0, 11, 11,  0,
              0,  0, 11,  0,  0,  0, 11,  0,
        ], null],
        grass_tr: [[
              0,  0,  0,  0,  0,  0,  0,  0,
              0, 11,  0,  0,  0,  0,  0,  0,
            11, 11,  0,  0,  0,  0, 11,  0,
            11,  0,  0,  0,  0, 11, 11,  0,
              0,  0,  0,  0,  0, 11,  0,  0,
              0,  0,  0, 11,  0,  0,  0,  0,
              0,  0,  0, 11, 11,  0,  0,  0,
              0,  0,  0,  0, 11,  0,  0,  0,
        ], null],
        grass_bl: [[
              0, 11, 11,  0,  0,  0,  0,  0,
              0, 11,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0, 11,  0,
              0, 11,  0,  0,  0,  0, 11, 11,
              0, 11, 11,  0,  0,  0,  0, 11,
              0,  0, 11,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
        ], null],
        grass_br: [[
              0,  0, 11,  0,  0,  0,  0,  0,
              0, 11, 11,  0,  0, 11,  0,  0,
              0, 11,  0,  0,  0, 11, 11,  0,
              0,  0,  0,  0,  0,  0, 11,  0,
              0,  0,  0,  0, 11,  0,  0,  0,
              0,  0,  0, 11, 11,  0,  0,  0,
              0,  0,  0, 11,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
        ], null],
        dirt_tl: [[
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  5,
              0,  0,  0,  0,  0,  5,  0,  5,
              0,  0,  5,  0,  0,  0,  0,  0,
              0,  0,  5,  5,  0,  0,  0,  0,
              0,  0,  0,  5,  0,  0,  0,  5,
              0,  0,  0,  0,  0,  0,  0,  5,
              0,  0,  0,  0,  0,  0,  0,  0,
        ], null],
        dirt_tr: [[
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  5,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  5,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              5,  0,  0,  0,  5,  5,  0,  0,
              5,  5,  0,  5,  5,  5,  0,  0,
              5,  5,  0,  5,  5,  0,  0,  0,
        ], null],
        dirt_bl: [[
              0,  5,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  5,  0,
              0,  0,  5,  0,  0,  5,  5,  0,
              0,  0,  5,  5,  0,  5,  0,  0,
              0,  0,  0,  5,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
        ], null],
        dirt_br: [[
              0,  5,  0,  5,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  5,  0,  0,  0,
              0,  0,  0,  0,  5,  5,  0,  0,
              5,  0,  0,  0,  0,  5,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
        ], null],
        tree1_tl: [[
            -1, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, 11, 11, 11, 11, 11,
            -1, -1, 11, 11, 11, 11, 11, 11,
            -1, -1, 11, 11, 11, 11, 11, 11,
            -1, -1, 11, 11, 11, 11, 11, 11,
            -1, -1, 11, 11, 11, 11, 11, 11,
            -1, -1, 11, 11, 11, 11, 11, 11,
            -1, -1, 11, 11, 11, 11, 11, 11,
        ], null],
        tree1_tr: [[
            -1, -1, -1, -1, -1, -1, -1, -1,
            11, 11, 11, 11, 11, -1, -1, -1,
            11, 11, 11, 11, 11, 11, -1, -1,
            11, 11, 11, 11, 11, 11, -1, -1,
            11, 11, 11, 11, 11, 11, -1, -1,
            11, 11, 11, 11, 11, 11, -1, -1,
            11, 11, 11, 11, 11, 11, -1, -1,
            11, 11, 11, 11, 11, 11, -1, -1,
        ], null],
        tree1_bl: [[
            -1, -1, 11, 11, 11, 11, 11, 11,
            -1, -1, -1, 11, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, 11, -1, 11,
            -1, -1, -1, -1, -1, -1, 11, 11,
            -1, -1, -1, -1, -1, -1, -1, 11,
            -1, -1, -1, -1, -1, -1, -1, 11,
            -1, -1, -1, -1, -1, -1, -1, 11,
            -1, -1, -1, -1, -1, -1, -1, -1,
        ], null],
        tree1_br: [[
            11, 11, 11, 11, 11, 11, -1, -1,
            -1, -1, -1, -1, 11, -1, -1, -1,
            11, -1, 11, -1, -1, -1, -1, -1,
            11, 11, -1, -1, -1, -1, -1, -1,
            11, -1, -1, -1, -1, -1, -1, -1,
            11, -1, -1, -1, -1, -1, -1, -1,
            11, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, -1,
        ], null],
        tree2_tl: [[
            -1, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, 11,
            -1, -1, -1, -1, -1, -1, 11, 11,
            -1, -1, -1, -1, -1, -1, 11, 11,
            -1, -1, -1, -1, -1, 11, 11, 11,
            -1, -1, -1, -1, -1, 11, 11, 11,
            -1, -1, -1, -1, 11, 11, 11, 11,
            -1, -1, -1, -1, 11, 11, 11, 11,
        ], null],
        tree2_tr: [[
            -1, -1, -1, -1, -1, -1, -1, -1,
            11, -1, -1, -1, -1, -1, -1, -1,
            11, 11, -1, -1, -1, -1, -1, -1,
            11, 11, -1, -1, -1, -1, -1, -1,
            11, 11, 11, -1, -1, -1, -1, -1,
            11, 11, 11, -1, -1, -1, -1, -1,
            11, 11, 11, 11, -1, -1, -1, -1,
            11, 11, 11, 11, -1, -1, -1, -1,
        ], null],
        tree2_bl: [[
            -1, -1, -1, -1, 11, 11, 11, 11,
            -1, -1, -1, -1, 11, 11, 11, 11,
            -1, -1, -1, -1, 11, 11, 11, 11,
            -1, -1, -1, -1, -1, 11, 11, 11,
            -1, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, 11,
            -1, -1, -1, -1, -1, -1, -1, 11,
            -1, -1, -1, -1, -1, -1, -1, -1,
        ], null],
        tree2_br: [[
            11, 11, 11, 11, -1, -1, -1, -1,
            11, 11, 11, 11, -1, -1, -1, -1,
            11, 11, 11, 11, -1, -1, -1, -1,
            11, 11, 11, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, -1,
            11, -1, -1, -1, -1, -1, -1, -1,
            11, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, -1,
        ], null],
    };

    function tile(tx, ty, name, layer = 0, mapId) {
        const cx = tx * 2, cy = ty * 2;
        mset(cx, cy, name + '_tl', layer, mapId);
        mset(cx + 1, cy, name + '_tr', layer, mapId);
        mset(cx, cy + 1, name + '_bl', layer, mapId);
        mset(cx + 1, cy + 1, name + '_br', layer, mapId);
    }

    function init() {
        for (let y = 0; y < 8; y++) {
            for (let x = 0; x < 8; x++) {
                tile(x, y, 'grass');
            }
        }
        for (let x = 0; x < 8; x++) { tile(x, 3, 'dirt'); }
        for (let y = 0; y < 8; y++) { tile(3, y, 'dirt'); }

        tile(1, 1, 'tree1', 1);
        tile(6, 1, 'tree2', 1);
        tile(2, 5, 'tree1', 1);
        tile(5, 2, 'tree2', 1);
        tile(0, 6, 'tree1', 1);
        tile(6, 6, 'tree2', 1);
        tile(2, 2, 'tree2', 1);
    }

    function draw() {
        cls(0);
        map(0);
        map(1);
    }

    start({ sprites, sounds: {}, init, draw, target });
});
Map System: Adding Layers
Trees on layer 1 drawn over the grass and dirt on layer 0

Layer 0 renders first, layer 1 draws on top. The tree sprites use -1 (transparent) for empty pixels, so the ground shows through wherever there's no foliage.

Draw order matters:

// inside draw()
cls(0);
map(0); // ground layer renders first
map(1); // object layer renders on top

Tip: You can use as many layers as you need. A common setup is layer 0 for ground, layer 1 for objects, and layer 2 for overhead elements like tree canopies that render in front of a player sprite.

Setting Up Multiple Maps

So far everything's gone into the default map. But mset() takes a fifth argument — a mapId string that names the map. Our tile() helper passes it through, so tile(x, y, 'grass', 0, 'forest') puts grass on the forest map. Each mapId gets its own tile grid with its own layers. Leave it out and the engine uses 'default'.

Let's populate two maps — a forest and a village — each with their own ground and object layers:

// inside init()

// --- forest map ---
for (let y = 0; y < 8; y++) {
    for (let x = 0; x < 8; x++) {
        tile(x, y, 'grass', 0, 'forest');
    }
}
for (let x = 0; x < 8; x++) { tile(x, 3, 'dirt', 0, 'forest'); }
tile(1, 1, 'tree1', 1, 'forest');
tile(6, 1, 'tree2', 1, 'forest');
tile(2, 5, 'tree1', 1, 'forest');
tile(5, 2, 'tree2', 1, 'forest');

// --- village map ---
for (let y = 0; y < 8; y++) {
    for (let x = 0; x < 8; x++) {
        tile(x, y, 'stone', 0, 'village');
    }
}
for (let x = 0; x < 8; x++) { tile(x, 4, 'dirt', 0, 'village'); }
tile(1, 1, 'house', 1, 'village');
tile(5, 2, 'house', 1, 'village');

The data stays in memory until you clear it with mclear(). Both maps use the same sprite definitions — they just store different tile layouts.

Switching Between Maps

We've got two maps in memory. Let's switch between them at runtime — press 1 for the forest, 2 for the village. The only thing that changes is which mapId we pass to map():

engine.scope(({ start, cls, mset, map, rectfill, text, btnp }) => {
    const sprites = {
        grass_tl: [[
              0,  0,  0,  0,  0,  0,  0,  0,
              0, 11,  0,  0,  0,  0,  0,  0,
              0, 11, 11,  0,  0,  0,  0,  0,
              0,  0, 11,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0, 11,  0,  0,
              0,  0,  0,  0,  0, 11, 11,  0,
              0,  0, 11,  0,  0,  0, 11,  0,
        ], null],
        grass_tr: [[
              0,  0,  0,  0,  0,  0,  0,  0,
              0, 11,  0,  0,  0,  0,  0,  0,
            11, 11,  0,  0,  0,  0, 11,  0,
            11,  0,  0,  0,  0, 11, 11,  0,
              0,  0,  0,  0,  0, 11,  0,  0,
              0,  0,  0, 11,  0,  0,  0,  0,
              0,  0,  0, 11, 11,  0,  0,  0,
              0,  0,  0,  0, 11,  0,  0,  0,
        ], null],
        grass_bl: [[
              0, 11, 11,  0,  0,  0,  0,  0,
              0, 11,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0, 11,  0,
              0, 11,  0,  0,  0,  0, 11, 11,
              0, 11, 11,  0,  0,  0,  0, 11,
              0,  0, 11,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
        ], null],
        grass_br: [[
              0,  0, 11,  0,  0,  0,  0,  0,
              0, 11, 11,  0,  0, 11,  0,  0,
              0, 11,  0,  0,  0, 11, 11,  0,
              0,  0,  0,  0,  0,  0, 11,  0,
              0,  0,  0,  0, 11,  0,  0,  0,
              0,  0,  0, 11, 11,  0,  0,  0,
              0,  0,  0, 11,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
        ], null],
        dirt_tl: [[
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  5,
              0,  0,  0,  0,  0,  5,  0,  5,
              0,  0,  5,  0,  0,  0,  0,  0,
              0,  0,  5,  5,  0,  0,  0,  0,
              0,  0,  0,  5,  0,  0,  0,  5,
              0,  0,  0,  0,  0,  0,  0,  5,
              0,  0,  0,  0,  0,  0,  0,  0,
        ], null],
        dirt_tr: [[
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  5,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  5,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              5,  0,  0,  0,  5,  5,  0,  0,
              5,  5,  0,  5,  5,  5,  0,  0,
              5,  5,  0,  5,  5,  0,  0,  0,
        ], null],
        dirt_bl: [[
              0,  5,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  5,  0,
              0,  0,  5,  0,  0,  5,  5,  0,
              0,  0,  5,  5,  0,  5,  0,  0,
              0,  0,  0,  5,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
        ], null],
        dirt_br: [[
              0,  5,  0,  5,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  5,  0,  0,  0,
              0,  0,  0,  0,  5,  5,  0,  0,
              5,  0,  0,  0,  0,  5,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
        ], null],
        water_tl: [[
              0,  0, 12, 12, 12, 12, 12, 12,
              0,  0,  0, 12, 12, 12, 12, 12,
            12,  0,  0, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
        ], null],
        water_tr: [[
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
        ], null],
        water_bl: [[
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
        ], null],
        water_br: [[
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
        ], null],
        stone_tl: [[
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  6,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  6,  6,  6,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  6,  6,
              0,  6,  0,  6,  6,  0,  6,  6,
        ], null],
        stone_tr: [[
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              6,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              6,  6,  0,  6,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  6,  0,  6,  0,  6,  6,  0,
              0,  0,  6,  0,  0,  0,  0,  0,
        ], null],
        stone_bl: [[
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  6,  6,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  6,
              0,  0,  0,  0,  0,  6,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
        ], null],
        stone_br: [[
              0,  0,  0,  0,  0,  0,  0,  0,
              6,  0,  0,  6,  6,  0,  0,  0,
              0,  6,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  6,  0,  0,  0,  0,
              6,  6,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
        ], null],
        tree1_tl: [[
            -1, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, 11, 11, 11, 11, 11,
            -1, -1, 11, 11, 11, 11, 11, 11,
            -1, -1, 11, 11, 11, 11, 11, 11,
            -1, -1, 11, 11, 11, 11, 11, 11,
            -1, -1, 11, 11, 11, 11, 11, 11,
            -1, -1, 11, 11, 11, 11, 11, 11,
            -1, -1, 11, 11, 11, 11, 11, 11,
        ], null],
        tree1_tr: [[
            -1, -1, -1, -1, -1, -1, -1, -1,
            11, 11, 11, 11, 11, -1, -1, -1,
            11, 11, 11, 11, 11, 11, -1, -1,
            11, 11, 11, 11, 11, 11, -1, -1,
            11, 11, 11, 11, 11, 11, -1, -1,
            11, 11, 11, 11, 11, 11, -1, -1,
            11, 11, 11, 11, 11, 11, -1, -1,
            11, 11, 11, 11, 11, 11, -1, -1,
        ], null],
        tree1_bl: [[
            -1, -1, 11, 11, 11, 11, 11, 11,
            -1, -1, -1, 11, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, 11, -1, 11,
            -1, -1, -1, -1, -1, -1, 11, 11,
            -1, -1, -1, -1, -1, -1, -1, 11,
            -1, -1, -1, -1, -1, -1, -1, 11,
            -1, -1, -1, -1, -1, -1, -1, 11,
            -1, -1, -1, -1, -1, -1, -1, -1,
        ], null],
        tree1_br: [[
            11, 11, 11, 11, 11, 11, -1, -1,
            -1, -1, -1, -1, 11, -1, -1, -1,
            11, -1, 11, -1, -1, -1, -1, -1,
            11, 11, -1, -1, -1, -1, -1, -1,
            11, -1, -1, -1, -1, -1, -1, -1,
            11, -1, -1, -1, -1, -1, -1, -1,
            11, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, -1,
        ], null],
        tree2_tl: [[
            -1, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, 11,
            -1, -1, -1, -1, -1, -1, 11, 11,
            -1, -1, -1, -1, -1, -1, 11, 11,
            -1, -1, -1, -1, -1, 11, 11, 11,
            -1, -1, -1, -1, -1, 11, 11, 11,
            -1, -1, -1, -1, 11, 11, 11, 11,
            -1, -1, -1, -1, 11, 11, 11, 11,
        ], null],
        tree2_tr: [[
            -1, -1, -1, -1, -1, -1, -1, -1,
            11, -1, -1, -1, -1, -1, -1, -1,
            11, 11, -1, -1, -1, -1, -1, -1,
            11, 11, -1, -1, -1, -1, -1, -1,
            11, 11, 11, -1, -1, -1, -1, -1,
            11, 11, 11, -1, -1, -1, -1, -1,
            11, 11, 11, 11, -1, -1, -1, -1,
            11, 11, 11, 11, -1, -1, -1, -1,
        ], null],
        tree2_bl: [[
            -1, -1, -1, -1, 11, 11, 11, 11,
            -1, -1, -1, -1, 11, 11, 11, 11,
            -1, -1, -1, -1, 11, 11, 11, 11,
            -1, -1, -1, -1, -1, 11, 11, 11,
            -1, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, 11,
            -1, -1, -1, -1, -1, -1, -1, 11,
            -1, -1, -1, -1, -1, -1, -1, -1,
        ], null],
        tree2_br: [[
            11, 11, 11, 11, -1, -1, -1, -1,
            11, 11, 11, 11, -1, -1, -1, -1,
            11, 11, 11, 11, -1, -1, -1, -1,
            11, 11, 11, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, -1,
            11, -1, -1, -1, -1, -1, -1, -1,
            11, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, -1,
        ], null],
        house_tl: [[
            -1, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1,  8,
            -1, -1, -1, -1, -1,  8,  8, -1,
            -1, -1, -1,  8,  8, -1, -1, -1,
            -1, -1,  8, -1, -1, -1, -1, -1,
            -1,  8, -1, -1, -1, -1, -1, -1,
            -1,  8, -1, -1, -1, -1, -1, -1,
            -1,  8, -1, -1, -1, -1, -1,  8,
        ], null],
        house_tr: [[
            -1, -1, -1, -1, -1, -1, -1, -1,
              8, -1, -1, -1, -1, -1, -1, -1,
            -1,  8, -1, -1, -1, -1, -1, -1,
            -1,  8,  8, -1, -1, -1, -1, -1,
            -1, -1,  8,  8, -1, -1, -1, -1,
            -1, -1,  8,  8,  8, -1, -1, -1,
            -1, -1, -1,  8,  8,  8, -1, -1,
            -1, -1, -1,  8,  8,  8,  8, -1,
        ], null],
        house_bl: [[
            -1,  8, -1, -1, -1,  8,  8, -1,
            -1,  8, -1,  8,  8, -1, -1,  8,
            -1,  8,  8, -1, -1,  8,  8,  8,
            -1,  8, -1,  8,  8,  8,  8,  8,
            -1, -1,  8,  8, -1,  8,  8, -1,
            -1, -1,  8,  8, -1,  8,  8, -1,
            -1, -1,  8,  8,  8,  8,  8, -1,
            -1, -1, -1, -1, -1, -1, -1, -1,
        ], null],
        house_br: [[
            -1,  8,  8,  8,  8,  8,  8, -1,
              8, -1, -1,  8,  8,  8,  8, -1,
              8,  8,  8, -1, -1,  8,  8, -1,
              8,  8,  8,  8,  8, -1,  8, -1,
            -1,  8,  8, -1,  8,  8, -1, -1,
            -1,  8,  8, -1,  8,  8, -1, -1,
            -1,  8,  8,  8,  8,  8, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, -1,
        ], null],
    };

    let currentMap = 'forest';

    function tile(tx, ty, name, layer = 0, mapId) {
        const cx = tx * 2, cy = ty * 2;
        mset(cx, cy, name + '_tl', layer, mapId);
        mset(cx + 1, cy, name + '_tr', layer, mapId);
        mset(cx, cy + 1, name + '_bl', layer, mapId);
        mset(cx + 1, cy + 1, name + '_br', layer, mapId);
    }

    function init() {
        for (let y = 0; y < 8; y++) {
            for (let x = 0; x < 8; x++) {
                tile(x, y, 'grass', 0, 'forest');
            }
        }
        for (let x = 0; x < 8; x++) { tile(x, 3, 'dirt', 0, 'forest'); }
        for (let y = 0; y < 8; y++) { tile(3, y, 'dirt', 0, 'forest'); }
        tile(6, 6, 'water', 0, 'forest');
        tile(1, 1, 'tree1', 1, 'forest'); tile(6, 1, 'tree2', 1, 'forest');
        tile(2, 5, 'tree1', 1, 'forest'); tile(5, 2, 'tree2', 1, 'forest');
        tile(0, 6, 'tree1', 1, 'forest'); tile(2, 2, 'tree2', 1, 'forest');

        for (let y = 0; y < 8; y++) {
            for (let x = 0; x < 8; x++) {
                tile(x, y, 'stone', 0, 'village');
            }
        }
        for (let x = 0; x < 8; x++) { tile(x, 4, 'dirt', 0, 'village'); }
        for (let y = 0; y < 8; y++) { tile(2, y, 'dirt', 0, 'village'); }
        tile(1, 1, 'house', 1, 'village'); tile(5, 1, 'house', 1, 'village');
        tile(5, 6, 'house', 1, 'village');
        tile(1, 6, 'tree1', 1, 'village'); tile(6, 3, 'tree1', 1, 'village');
    }

    function update() {
        if (btnp('1')) currentMap = 'forest';
        if (btnp('2')) currentMap = 'village';
    }

    function draw() {
        cls(0);
        map(0, 0, 0, undefined, undefined, 0, 0, currentMap);
        map(1, 0, 0, undefined, undefined, 0, 0, currentMap);
        text(currentMap, 1, 1, 7);
        text('1=forest 2=village', 1, 8, 6);
    }

    start({ sprites, sounds: {}, init, update, draw, target });
});
Map System: Switching Maps
Press 1 for forest or 2 for village — both maps share the same tile sprites

When passing a mapId to map(), you need to fill in all the arguments before it. The full signature is map(layer, cellX, cellY, cellW, cellH, screenX, screenY, mapId). For screen-sized maps, pass undefined for cellW and cellH — the engine auto-calculates them.

Scrolling with the Camera

An 8x8 tile grid fills the screen exactly — no room to scroll. Let's make the map bigger and use camera() to pan across it. This one's 16x12 tiles (32x24 cells — 256x192 pixels), giving 128 pixels of horizontal scroll and 64 of vertical:

engine.scope(({ start, cls, mset, map, rectfill, text, btn, camera, creset }) => {
    const sprites = {
        grass_tl: [[
              0,  0,  0,  0,  0,  0,  0,  0,
              0, 11,  0,  0,  0,  0,  0,  0,
              0, 11, 11,  0,  0,  0,  0,  0,
              0,  0, 11,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0, 11,  0,  0,
              0,  0,  0,  0,  0, 11, 11,  0,
              0,  0, 11,  0,  0,  0, 11,  0,
        ], null],
        grass_tr: [[
              0,  0,  0,  0,  0,  0,  0,  0,
              0, 11,  0,  0,  0,  0,  0,  0,
            11, 11,  0,  0,  0,  0, 11,  0,
            11,  0,  0,  0,  0, 11, 11,  0,
              0,  0,  0,  0,  0, 11,  0,  0,
              0,  0,  0, 11,  0,  0,  0,  0,
              0,  0,  0, 11, 11,  0,  0,  0,
              0,  0,  0,  0, 11,  0,  0,  0,
        ], null],
        grass_bl: [[
              0, 11, 11,  0,  0,  0,  0,  0,
              0, 11,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0, 11,  0,
              0, 11,  0,  0,  0,  0, 11, 11,
              0, 11, 11,  0,  0,  0,  0, 11,
              0,  0, 11,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
        ], null],
        grass_br: [[
              0,  0, 11,  0,  0,  0,  0,  0,
              0, 11, 11,  0,  0, 11,  0,  0,
              0, 11,  0,  0,  0, 11, 11,  0,
              0,  0,  0,  0,  0,  0, 11,  0,
              0,  0,  0,  0, 11,  0,  0,  0,
              0,  0,  0, 11, 11,  0,  0,  0,
              0,  0,  0, 11,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
        ], null],
        dirt_tl: [[
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  5,
              0,  0,  0,  0,  0,  5,  0,  5,
              0,  0,  5,  0,  0,  0,  0,  0,
              0,  0,  5,  5,  0,  0,  0,  0,
              0,  0,  0,  5,  0,  0,  0,  5,
              0,  0,  0,  0,  0,  0,  0,  5,
              0,  0,  0,  0,  0,  0,  0,  0,
        ], null],
        dirt_tr: [[
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  5,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  5,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              5,  0,  0,  0,  5,  5,  0,  0,
              5,  5,  0,  5,  5,  5,  0,  0,
              5,  5,  0,  5,  5,  0,  0,  0,
        ], null],
        dirt_bl: [[
              0,  5,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  5,  0,
              0,  0,  5,  0,  0,  5,  5,  0,
              0,  0,  5,  5,  0,  5,  0,  0,
              0,  0,  0,  5,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
        ], null],
        dirt_br: [[
              0,  5,  0,  5,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  5,  0,  0,  0,
              0,  0,  0,  0,  5,  5,  0,  0,
              5,  0,  0,  0,  0,  5,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
        ], null],
        water_tl: [[
              0,  0, 12, 12, 12, 12, 12, 12,
              0,  0,  0, 12, 12, 12, 12, 12,
            12,  0,  0, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
        ], null],
        water_tr: [[
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
        ], null],
        water_bl: [[
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
        ], null],
        water_br: [[
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
        ], null],
        tree1_tl: [[
            -1, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, 11, 11, 11, 11, 11,
            -1, -1, 11, 11, 11, 11, 11, 11,
            -1, -1, 11, 11, 11, 11, 11, 11,
            -1, -1, 11, 11, 11, 11, 11, 11,
            -1, -1, 11, 11, 11, 11, 11, 11,
            -1, -1, 11, 11, 11, 11, 11, 11,
            -1, -1, 11, 11, 11, 11, 11, 11,
        ], null],
        tree1_tr: [[
            -1, -1, -1, -1, -1, -1, -1, -1,
            11, 11, 11, 11, 11, -1, -1, -1,
            11, 11, 11, 11, 11, 11, -1, -1,
            11, 11, 11, 11, 11, 11, -1, -1,
            11, 11, 11, 11, 11, 11, -1, -1,
            11, 11, 11, 11, 11, 11, -1, -1,
            11, 11, 11, 11, 11, 11, -1, -1,
            11, 11, 11, 11, 11, 11, -1, -1,
        ], null],
        tree1_bl: [[
            -1, -1, 11, 11, 11, 11, 11, 11,
            -1, -1, -1, 11, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, 11, -1, 11,
            -1, -1, -1, -1, -1, -1, 11, 11,
            -1, -1, -1, -1, -1, -1, -1, 11,
            -1, -1, -1, -1, -1, -1, -1, 11,
            -1, -1, -1, -1, -1, -1, -1, 11,
            -1, -1, -1, -1, -1, -1, -1, -1,
        ], null],
        tree1_br: [[
            11, 11, 11, 11, 11, 11, -1, -1,
            -1, -1, -1, -1, 11, -1, -1, -1,
            11, -1, 11, -1, -1, -1, -1, -1,
            11, 11, -1, -1, -1, -1, -1, -1,
            11, -1, -1, -1, -1, -1, -1, -1,
            11, -1, -1, -1, -1, -1, -1, -1,
            11, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, -1,
        ], null],
        tree2_tl: [[
            -1, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, 11,
            -1, -1, -1, -1, -1, -1, 11, 11,
            -1, -1, -1, -1, -1, -1, 11, 11,
            -1, -1, -1, -1, -1, 11, 11, 11,
            -1, -1, -1, -1, -1, 11, 11, 11,
            -1, -1, -1, -1, 11, 11, 11, 11,
            -1, -1, -1, -1, 11, 11, 11, 11,
        ], null],
        tree2_tr: [[
            -1, -1, -1, -1, -1, -1, -1, -1,
            11, -1, -1, -1, -1, -1, -1, -1,
            11, 11, -1, -1, -1, -1, -1, -1,
            11, 11, -1, -1, -1, -1, -1, -1,
            11, 11, 11, -1, -1, -1, -1, -1,
            11, 11, 11, -1, -1, -1, -1, -1,
            11, 11, 11, 11, -1, -1, -1, -1,
            11, 11, 11, 11, -1, -1, -1, -1,
        ], null],
        tree2_bl: [[
            -1, -1, -1, -1, 11, 11, 11, 11,
            -1, -1, -1, -1, 11, 11, 11, 11,
            -1, -1, -1, -1, 11, 11, 11, 11,
            -1, -1, -1, -1, -1, 11, 11, 11,
            -1, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, 11,
            -1, -1, -1, -1, -1, -1, -1, 11,
            -1, -1, -1, -1, -1, -1, -1, -1,
        ], null],
        tree2_br: [[
            11, 11, 11, 11, -1, -1, -1, -1,
            11, 11, 11, 11, -1, -1, -1, -1,
            11, 11, 11, 11, -1, -1, -1, -1,
            11, 11, 11, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, -1,
            11, -1, -1, -1, -1, -1, -1, -1,
            11, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, -1,
        ], null],
    };

    const mapW = 32, mapH = 24;
    let camX = 0, camY = 0;
    const camSpeed = 2;

    function tile(tx, ty, name, layer = 0, mapId) {
        const cx = tx * 2, cy = ty * 2;
        mset(cx, cy, name + '_tl', layer, mapId);
        mset(cx + 1, cy, name + '_tr', layer, mapId);
        mset(cx, cy + 1, name + '_bl', layer, mapId);
        mset(cx + 1, cy + 1, name + '_br', layer, mapId);
    }

    function init() {
        for (let y = 0; y < 12; y++) {
            for (let x = 0; x < 16; x++) {
                tile(x, y, 'grass');
            }
        }
        for (let x = 0; x < 16; x++) { tile(x, 5, 'dirt'); }
        for (let y = 0; y < 12; y++) { tile(7, y, 'dirt'); }
        for (let x = 10; x < 13; x++) {
            for (let y = 9; y < 11; y++) {
                tile(x, y, 'water');
            }
        }
        tile(1, 1, 'tree1', 1); tile(2, 2, 'tree2', 1);
        tile(5, 2, 'tree1', 1); tile(14, 1, 'tree2', 1);
        tile(1, 8, 'tree1', 1); tile(4, 10, 'tree2', 1);
        tile(12, 3, 'tree1', 1); tile(15, 7, 'tree2', 1);
        tile(0, 4, 'tree2', 1); tile(6, 11, 'tree1', 1);
        tile(13, 4, 'tree1', 1); tile(9, 2, 'tree2', 1);
    }

    function update() {
        if (btn('a') || btn('ArrowLeft')) camX -= camSpeed;
        if (btn('d') || btn('ArrowRight')) camX += camSpeed;
        if (btn('w') || btn('ArrowUp')) camY -= camSpeed;
        if (btn('s') || btn('ArrowDown')) camY += camSpeed;
        camX = Math.max(0, Math.min(mapW * 8 - 128, camX));
        camY = Math.max(0, Math.min(mapH * 8 - 128, camY));
    }

    function draw() {
        cls(0);
        camera(camX, camY);
        map(0, 0, 0, mapW, mapH);
        map(1, 0, 0, mapW, mapH);
        creset();
        text('WASD to scroll', 1, 1, 7);
        text(camX + ',' + camY, 1, 8, 6);
    }

    start({ sprites, sounds: {}, init, update, draw, target });
});
Map System: Camera Scrolling
WASD to scroll the camera across a 16x12 tile map larger than the screen

camera(x, y) offsets all drawing by that amount. Tiles outside the 128x128 screen get clipped automatically. The clamping math keeps the camera from scrolling past the map edges.

We pass mapW and mapH to map() explicitly — those are in cell units (32x24), not tile units. The auto-calculated cell dimensions (17x17) only cover the screen-sized region. With camera scrolling, visible cells can extend well past that, so you need the full map size.

Call creset() before drawing HUD text so it stays fixed on screen instead of scrolling with the world:

// inside draw()
camera(camX, camY);
map(0, 0, 0, mapW, mapH);
map(1, 0, 0, mapW, mapH);

creset();
text('Forest', 1, 1, 7);

Tip: The engine renders every tile you ask it to, including off-screen ones — they get clipped at the edges. For small maps (under ~64x64 tiles) that's fine. For very large maps, pass cellX, cellY, cellW, cellH to map() to render only the visible region: map(0, Math.floor(camX / 8), Math.floor(camY / 8), 17, 17).

Putting It All Together

Let's combine everything: two scrollable multi-layer maps (16x12 tiles each), press 1/2 to switch, WASD to scroll, and a HUD showing which map you're on. The camera resets to (0, 0) when you switch maps so you always start at the top-left corner:

engine.scope(({ start, cls, mset, map, rectfill, text, btn, btnp, camera, creset }) => {
    const sprites = {
        grass_tl: [[
              0,  0,  0,  0,  0,  0,  0,  0,
              0, 11,  0,  0,  0,  0,  0,  0,
              0, 11, 11,  0,  0,  0,  0,  0,
              0,  0, 11,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0, 11,  0,  0,
              0,  0,  0,  0,  0, 11, 11,  0,
              0,  0, 11,  0,  0,  0, 11,  0,
        ], null],
        grass_tr: [[
              0,  0,  0,  0,  0,  0,  0,  0,
              0, 11,  0,  0,  0,  0,  0,  0,
            11, 11,  0,  0,  0,  0, 11,  0,
            11,  0,  0,  0,  0, 11, 11,  0,
              0,  0,  0,  0,  0, 11,  0,  0,
              0,  0,  0, 11,  0,  0,  0,  0,
              0,  0,  0, 11, 11,  0,  0,  0,
              0,  0,  0,  0, 11,  0,  0,  0,
        ], null],
        grass_bl: [[
              0, 11, 11,  0,  0,  0,  0,  0,
              0, 11,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0, 11,  0,
              0, 11,  0,  0,  0,  0, 11, 11,
              0, 11, 11,  0,  0,  0,  0, 11,
              0,  0, 11,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
        ], null],
        grass_br: [[
              0,  0, 11,  0,  0,  0,  0,  0,
              0, 11, 11,  0,  0, 11,  0,  0,
              0, 11,  0,  0,  0, 11, 11,  0,
              0,  0,  0,  0,  0,  0, 11,  0,
              0,  0,  0,  0, 11,  0,  0,  0,
              0,  0,  0, 11, 11,  0,  0,  0,
              0,  0,  0, 11,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
        ], null],
        dirt_tl: [[
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  5,
              0,  0,  0,  0,  0,  5,  0,  5,
              0,  0,  5,  0,  0,  0,  0,  0,
              0,  0,  5,  5,  0,  0,  0,  0,
              0,  0,  0,  5,  0,  0,  0,  5,
              0,  0,  0,  0,  0,  0,  0,  5,
              0,  0,  0,  0,  0,  0,  0,  0,
        ], null],
        dirt_tr: [[
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  5,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  5,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              5,  0,  0,  0,  5,  5,  0,  0,
              5,  5,  0,  5,  5,  5,  0,  0,
              5,  5,  0,  5,  5,  0,  0,  0,
        ], null],
        dirt_bl: [[
              0,  5,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  5,  0,
              0,  0,  5,  0,  0,  5,  5,  0,
              0,  0,  5,  5,  0,  5,  0,  0,
              0,  0,  0,  5,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
        ], null],
        dirt_br: [[
              0,  5,  0,  5,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  5,  0,  0,  0,
              0,  0,  0,  0,  5,  5,  0,  0,
              5,  0,  0,  0,  0,  5,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
        ], null],
        water_tl: [[
              0,  0, 12, 12, 12, 12, 12, 12,
              0,  0,  0, 12, 12, 12, 12, 12,
            12,  0,  0, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
        ], null],
        water_tr: [[
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
        ], null],
        water_bl: [[
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
        ], null],
        water_br: [[
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
            12, 12, 12, 12, 12, 12, 12, 12,
        ], null],
        stone_tl: [[
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  6,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  6,  6,  6,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  6,  6,
              0,  6,  0,  6,  6,  0,  6,  6,
        ], null],
        stone_tr: [[
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              6,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              6,  6,  0,  6,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  6,  0,  6,  0,  6,  6,  0,
              0,  0,  6,  0,  0,  0,  0,  0,
        ], null],
        stone_bl: [[
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  6,  6,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  6,
              0,  0,  0,  0,  0,  6,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
        ], null],
        stone_br: [[
              0,  0,  0,  0,  0,  0,  0,  0,
              6,  0,  0,  6,  6,  0,  0,  0,
              0,  6,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  6,  0,  0,  0,  0,
              6,  6,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,
        ], null],
        tree1_tl: [[
            -1, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, 11, 11, 11, 11, 11,
            -1, -1, 11, 11, 11, 11, 11, 11,
            -1, -1, 11, 11, 11, 11, 11, 11,
            -1, -1, 11, 11, 11, 11, 11, 11,
            -1, -1, 11, 11, 11, 11, 11, 11,
            -1, -1, 11, 11, 11, 11, 11, 11,
            -1, -1, 11, 11, 11, 11, 11, 11,
        ], null],
        tree1_tr: [[
            -1, -1, -1, -1, -1, -1, -1, -1,
            11, 11, 11, 11, 11, -1, -1, -1,
            11, 11, 11, 11, 11, 11, -1, -1,
            11, 11, 11, 11, 11, 11, -1, -1,
            11, 11, 11, 11, 11, 11, -1, -1,
            11, 11, 11, 11, 11, 11, -1, -1,
            11, 11, 11, 11, 11, 11, -1, -1,
            11, 11, 11, 11, 11, 11, -1, -1,
        ], null],
        tree1_bl: [[
            -1, -1, 11, 11, 11, 11, 11, 11,
            -1, -1, -1, 11, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, 11, -1, 11,
            -1, -1, -1, -1, -1, -1, 11, 11,
            -1, -1, -1, -1, -1, -1, -1, 11,
            -1, -1, -1, -1, -1, -1, -1, 11,
            -1, -1, -1, -1, -1, -1, -1, 11,
            -1, -1, -1, -1, -1, -1, -1, -1,
        ], null],
        tree1_br: [[
            11, 11, 11, 11, 11, 11, -1, -1,
            -1, -1, -1, -1, 11, -1, -1, -1,
            11, -1, 11, -1, -1, -1, -1, -1,
            11, 11, -1, -1, -1, -1, -1, -1,
            11, -1, -1, -1, -1, -1, -1, -1,
            11, -1, -1, -1, -1, -1, -1, -1,
            11, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, -1,
        ], null],
        tree2_tl: [[
            -1, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, 11,
            -1, -1, -1, -1, -1, -1, 11, 11,
            -1, -1, -1, -1, -1, -1, 11, 11,
            -1, -1, -1, -1, -1, 11, 11, 11,
            -1, -1, -1, -1, -1, 11, 11, 11,
            -1, -1, -1, -1, 11, 11, 11, 11,
            -1, -1, -1, -1, 11, 11, 11, 11,
        ], null],
        tree2_tr: [[
            -1, -1, -1, -1, -1, -1, -1, -1,
            11, -1, -1, -1, -1, -1, -1, -1,
            11, 11, -1, -1, -1, -1, -1, -1,
            11, 11, -1, -1, -1, -1, -1, -1,
            11, 11, 11, -1, -1, -1, -1, -1,
            11, 11, 11, -1, -1, -1, -1, -1,
            11, 11, 11, 11, -1, -1, -1, -1,
            11, 11, 11, 11, -1, -1, -1, -1,
        ], null],
        tree2_bl: [[
            -1, -1, -1, -1, 11, 11, 11, 11,
            -1, -1, -1, -1, 11, 11, 11, 11,
            -1, -1, -1, -1, 11, 11, 11, 11,
            -1, -1, -1, -1, -1, 11, 11, 11,
            -1, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, 11,
            -1, -1, -1, -1, -1, -1, -1, 11,
            -1, -1, -1, -1, -1, -1, -1, -1,
        ], null],
        tree2_br: [[
            11, 11, 11, 11, -1, -1, -1, -1,
            11, 11, 11, 11, -1, -1, -1, -1,
            11, 11, 11, 11, -1, -1, -1, -1,
            11, 11, 11, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, -1,
            11, -1, -1, -1, -1, -1, -1, -1,
            11, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, -1,
        ], null],
        house_tl: [[
            -1, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1,  8,
            -1, -1, -1, -1, -1,  8,  8, -1,
            -1, -1, -1,  8,  8, -1, -1, -1,
            -1, -1,  8, -1, -1, -1, -1, -1,
            -1,  8, -1, -1, -1, -1, -1, -1,
            -1,  8, -1, -1, -1, -1, -1, -1,
            -1,  8, -1, -1, -1, -1, -1,  8,
        ], null],
        house_tr: [[
            -1, -1, -1, -1, -1, -1, -1, -1,
              8, -1, -1, -1, -1, -1, -1, -1,
            -1,  8, -1, -1, -1, -1, -1, -1,
            -1,  8,  8, -1, -1, -1, -1, -1,
            -1, -1,  8,  8, -1, -1, -1, -1,
            -1, -1,  8,  8,  8, -1, -1, -1,
            -1, -1, -1,  8,  8,  8, -1, -1,
            -1, -1, -1,  8,  8,  8,  8, -1,
        ], null],
        house_bl: [[
            -1,  8, -1, -1, -1,  8,  8, -1,
            -1,  8, -1,  8,  8, -1, -1,  8,
            -1,  8,  8, -1, -1,  8,  8,  8,
            -1,  8, -1,  8,  8,  8,  8,  8,
            -1, -1,  8,  8, -1,  8,  8, -1,
            -1, -1,  8,  8, -1,  8,  8, -1,
            -1, -1,  8,  8,  8,  8,  8, -1,
            -1, -1, -1, -1, -1, -1, -1, -1,
        ], null],
        house_br: [[
            -1,  8,  8,  8,  8,  8,  8, -1,
              8, -1, -1,  8,  8,  8,  8, -1,
              8,  8,  8, -1, -1,  8,  8, -1,
              8,  8,  8,  8,  8, -1,  8, -1,
            -1,  8,  8, -1,  8,  8, -1, -1,
            -1,  8,  8, -1,  8,  8, -1, -1,
            -1,  8,  8,  8,  8,  8, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, -1,
        ], null],
    };

    const maps = {
        forest: { w: 32, h: 24 },
        village: { w: 32, h: 24 },
    };
    let currentMap = 'forest';
    let camX = 0, camY = 0;
    const camSpeed = 2;

    function tile(tx, ty, name, layer = 0, mapId) {
        const cx = tx * 2, cy = ty * 2;
        mset(cx, cy, name + '_tl', layer, mapId);
        mset(cx + 1, cy, name + '_tr', layer, mapId);
        mset(cx, cy + 1, name + '_bl', layer, mapId);
        mset(cx + 1, cy + 1, name + '_br', layer, mapId);
    }

    function init() {
        for (let y = 0; y < 12; y++) {
            for (let x = 0; x < 16; x++) {
                tile(x, y, 'grass', 0, 'forest');
            }
        }
        for (let x = 0; x < 16; x++) { tile(x, 5, 'dirt', 0, 'forest'); }
        for (let y = 0; y < 12; y++) { tile(7, y, 'dirt', 0, 'forest'); }
        for (let x = 10; x < 13; x++) {
            for (let y = 9; y < 11; y++) {
                tile(x, y, 'water', 0, 'forest');
            }
        }
        tile(1, 1, 'tree1', 1, 'forest'); tile(2, 2, 'tree2', 1, 'forest');
        tile(5, 2, 'tree1', 1, 'forest'); tile(14, 1, 'tree2', 1, 'forest');
        tile(1, 8, 'tree1', 1, 'forest'); tile(4, 10, 'tree2', 1, 'forest');
        tile(12, 3, 'tree1', 1, 'forest'); tile(15, 7, 'tree2', 1, 'forest');
        tile(0, 4, 'tree2', 1, 'forest'); tile(6, 11, 'tree1', 1, 'forest');
        tile(13, 4, 'tree1', 1, 'forest'); tile(9, 2, 'tree2', 1, 'forest');

        for (let y = 0; y < 12; y++) {
            for (let x = 0; x < 16; x++) {
                tile(x, y, 'stone', 0, 'village');
            }
        }
        for (let x = 0; x < 16; x++) { tile(x, 6, 'dirt', 0, 'village'); }
        for (let y = 0; y < 12; y++) { tile(4, y, 'dirt', 0, 'village'); tile(12, y, 'dirt', 0, 'village'); }
        tile(1, 1, 'house', 1, 'village'); tile(6, 1, 'house', 1, 'village');
        tile(10, 2, 'house', 1, 'village'); tile(14, 4, 'house', 1, 'village');
        tile(2, 8, 'house', 1, 'village'); tile(7, 9, 'house', 1, 'village');
        tile(1, 4, 'tree1', 1, 'village'); tile(15, 10, 'tree1', 1, 'village');
        tile(9, 7, 'tree2', 1, 'village'); tile(13, 1, 'tree2', 1, 'village');
    }

    function update() {
        if (btnp('1')) { currentMap = 'forest'; camX = 0; camY = 0; }
        if (btnp('2')) { currentMap = 'village'; camX = 0; camY = 0; }

        if (btn('a') || btn('ArrowLeft')) camX -= camSpeed;
        if (btn('d') || btn('ArrowRight')) camX += camSpeed;
        if (btn('w') || btn('ArrowUp')) camY -= camSpeed;
        if (btn('s') || btn('ArrowDown')) camY += camSpeed;

        const info = maps[currentMap];
        camX = Math.max(0, Math.min(info.w * 8 - 128, camX));
        camY = Math.max(0, Math.min(info.h * 8 - 128, camY));
    }

    function draw() {
        cls(0);
        camera(camX, camY);
        const info = maps[currentMap];
        map(0, 0, 0, info.w, info.h, 0, 0, currentMap);
        map(1, 0, 0, info.w, info.h, 0, 0, currentMap);
        creset();
        text(currentMap, 1, 1, 7);
        text('1=forest 2=village', 1, 8, 6);
        text('WASD to scroll', 1, 15, 6);
    }

    start({ sprites, sounds: {}, init, update, draw, target });
});
Map System: Complete Example
Press 1/2 to switch maps and WASD to scroll — two scrollable multi-layer worlds

Going Further

A few directions you could take this:

  • Procedural maps — use rnd() to scatter tiles randomly for terrain generation
  • Map data from JSON — define tile grids as 2D arrays and loop through them to call mset()
  • Parallax scrolling — render background layers at a fraction of the camera speed for depth
  • Animated tiles — swap tile sprites on a timer (e.g., water that ripples)
  • Collision with tiles — combine mget() with movement code to block the player from walking through walls (see the platformer tutorial)