How to Use the Map System
This tutorial was written in February 2026, for v2 of the engine.
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 });
});
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 });
});
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);
rectfill(0, 0, 78, 15, 0);
text(currentMap, 1, 1, 7);
text('1=forest 2=village', 1, 8, 6);
}
start({ sprites, sounds: {}, init, update, draw, target });
});
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();
rectfill(0, 0, 68, 15, 0);
text('WASD to scroll', 1, 1, 7);
text(camX + ',' + camY, 1, 8, 6);
}
start({ sprites, sounds: {}, init, update, draw, target });
});
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();
rectfill(0, 0, 78, 22, 0);
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 });
});
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)