Custom map authoring

You design a Keyboard Warriors map the same way you'd build a web page: HTML elements, positioned with CSS, tagged with data-kw-* attributes. The baker turns each tagged element into arena geometry — floors you stand on, walls you hide behind, lava you die in, spawns, pickups. The page's own look becomes the map's texture.

This is how the hand-built maps on our site are made. It's entirely optional — ordinary websites are baked automatically without any of this.

How it works

A page becomes a map in one of two ways:

A single data-kw-map on your root switches the whole page into authored mode. From then on, only tagged elements become geometry — untagged decoration (backgrounds, labels, your title) is drawn into the texture but is not solid.

The coordinate system

Everything is in page pixels, the same numbers you write in CSS left/top/width/height.

CSS border-radius is retained.

Size and scale

The arena is your map's size: the scroll size of the element with data-kw-map (usually <body>). Give it an explicit width and height.

Width Height
Minimum 320 px 240 px
Maximum 4000 px 4000 px
Recommended 1280 px 512 px

A map larger than the maximum is cut off at 4000 px: the screenshot ends there and boxes that start beyond it are dropped. A smaller one is padded up to the minimum. 1280 px wide is the size of every arena made from a normal web page, so a map that wide plays at the familiar size; wider is a bigger arena.

The scale is fixed: 1 page pixel is always the same size in the game, on every map, hand-built or not. The numbers below hold whatever your map's size.

The player is 31 px tall and 17 px wide. The highest ledge each move reaches, measured from the surface the player stands on, in the units you write in data-kw-z:

Pillars at the height each move reaches: step 6 px, jump 47 px, High Jump 103 px, hammer launch 164 px, jump plus hammer 245 px, High Jump plus hammer 510 px

Move Reaches
Walk up a step 6 px
Jump 47 px
High Jump (pickup) 103 px
Impact Hammer launch (full charge, aimed down) 164 px
Jump, then hammer launch (or a rocket jump) 245 px
High Jump + hammer launch 510 px

The plain jump is measured in the game; the others are worked out from it, so leave some margin.

Heights are capped: data-kw-z from 0 to 600 and data-kw-extrusion from 1 to 400. For a tower taller than 400, stack two boxes with the same footprint: one from the floor up, one on top of it.

Counts a map may have at most:

Boxes Spawns Pickups Hazards
5000 256 512 128

Attribute reference

Put these on absolutely-positioned elements inside your authored page.

Attribute Values On What it does
data-kw-map — (presence) <body> or root wrapper Marks the page as a hand-built map (authored mode). Required for a custom map. It takes no value: players pick Team Deathmatch or Deathmatch when they join, and every map supports both.
data-kw-node solid | platform | island | bridge geometry element Makes the element solid arena geometry. See Node types.
data-kw-anchor ground | floating geometry element ground drops the box to the floor; floating leaves it at its z.
data-kw-material glass geometry element See-through rendering, with solid collision. Blocks players, shots, grenades and blast damage. Omit for the usual opaque material.
data-kw-z number, 0–600 (page-pixel scale) geometry / spawn / pickup / hazard Height of the box's top (or the point's height).
data-kw-extrusion number, 1–400 (page-pixel scale) geometry / hazard How thick the box is, downward from its top.
data-kw-team red | blue | neutral spawn marker Same as data-kw-spawn-team. On geometry it is recorded but has no effect in the game yet.
data-kw-spawn any id string marker element A spawn point at the element's centre.
data-kw-spawn-team red | blue | neutral spawn marker Which team spawns here (overrides data-kw-team).
data-kw-hazard lava | void element A kill zone covering the element's rectangle.
data-kw-pickup see Pickups marker element Places a pickup at the element's centre.
data-kw-ignore — (presence) any element Hides the element from the bake entirely (geometry and texture). Use it for editor-only labels or a "Play this map" link.

Notes:

Node types

data-kw-node decides how a box behaves underfoot:

Type Behaviour
solid A full block: solid sides, stand on top. Your walls, towers and floors.
platform A thin slab you stand on — a ledge or step. Pair with data-kw-anchor="floating" and a data-kw-z to hang it in the air.
island A standalone raised pad, surrounded by drop. Good for jump targets over lava.
bridge A long thin connector between two higher places.

data-kw-anchor:

Pickups

data-kw-pickup="<kind>" on a small marker div places one pickup at its centre.

Kind Effect
health Restores 50 health (can't be taken at full).
shield Soaks the next hit (including blast and hammer knockback).
haste Faster movement for a while.
highjump Higher jumps for a while.
rapid Faster fire for a while.
rockets The Rocket Launcher: a full launcher of 8 rockets, lost on death.
sniper The Sniper Rifle: 3 shots, a scope on right-click, lost on death.
grenades The Grenade Launcher: 6 bouncing grenades, a 2-second fuse or enemy-contact detonation, lost on death. Select with 4.

Placement tips:

Spawns and teams

Players pick the mode when they join, and every map plays both. Mark each spawn with data-kw-spawn="some-id" on a small div; the spawn is at its centre, at height data-kw-z. Ids just need to be unique (red-1, blue-3 read well).

Mode Spawns used How one is picked
Deathmatch All your data-kw-spawn markers (team tags ignored) The one farthest from any living opponent
Team Deathmatch, both teams tagged Only spawns with data-kw-spawn-team="red" / "blue", each team its own About 45 units from the nearest enemy (safe, but near the fight), pulled toward teammates. If enemies overrun a side, that team spawns on any tagged spawn instead, so spawn camping doesn't work
Team Deathmatch, no team tags Automatic: the map's long axis is split, red at one end, blue at the other, a neutral band in the middle; any standing spot counts Same rule

So tagging is optional. Without it, teams start at either end of the map. Tag spawns when you want to place each team's base yourself, and tag both sides: with only one side tagged, the automatic split is used.

Spread deathmatch spawns around the whole map; in a small cluster, players respawn on top of each other.

Where exactly a player appears:

Hazards

data-kw-hazard="lava" (or "void") covers the element's whole rectangle with a kill zone. Give it a data-kw-z for where its surface sits and a data-kw-extrusion for thickness. Touch it and you die, so hazards double as the gaps between your platforms — leave open lava under a run of floating islands and the jumps suddenly matter.

A complete example

A compact team-deathmatch arena: two ground towers, a floating bridge between them, lava underneath, a spawn and a pickup per side, and a contested pickup in the middle.

<body data-kw-map style="margin:0;position:relative;width:1280px;height:512px">
  <!-- The lava floor everything sits over -->
  <div data-kw-hazard="lava" data-kw-z="0" data-kw-extrusion="14"
       style="position:absolute;left:0;top:0;width:1280px;height:512px"></div>

  <!-- Red tower (left) and blue tower (right) -->
  <div data-kw-node="solid" data-kw-anchor="ground"
       style="position:absolute;left:57px;top:128px;width:256px;height:256px"></div>
  <div data-kw-node="solid" data-kw-anchor="ground"
       style="position:absolute;left:967px;top:128px;width:256px;height:256px"></div>

  <!-- A floating bridge joining them across the lava -->
  <div data-kw-node="bridge" data-kw-anchor="floating" data-kw-z="50" data-kw-extrusion="13"
       style="position:absolute;left:334px;top:228px;width:612px;height:64px"></div>

  <!-- One spawn per side -->
  <div data-kw-spawn="red-1"  data-kw-spawn-team="red"  data-kw-z="43"
       style="position:absolute;left:164px;top:213px;width:11px;height:11px"></div>
  <div data-kw-spawn="blue-1" data-kw-spawn-team="blue" data-kw-z="43"
       style="position:absolute;left:1095px;top:213px;width:11px;height:11px"></div>

  <!-- A health pack on each tower, mirrored; the Sniper Rifle fought over in the middle -->
  <div data-kw-pickup="health" style="position:absolute;left:185px;top:156px;width:6px;height:6px"></div>
  <div data-kw-pickup="health" style="position:absolute;left:1095px;top:156px;width:6px;height:6px"></div>
  <div data-kw-pickup="sniper" style="position:absolute;left:640px;top:213px;width:6px;height:6px"></div>

  <!-- An editor-only label the bake ignores -->
  <h1 data-kw-ignore>My Arena</h1>
</body>

Our own maps are fuller, styled examples worth reading: open them and view the page source. Facing Tabs builds two towers from real styled panels, with terraced steps, a bridge, spawn clusters and a full pickup spread; Frost Yard is a small symmetric team yard.

Tips

Publishing your map

A map is just a web page, so you publish it by putting it online. Anywhere public works: GitHub Pages, Netlify, your own site.

  1. Host your HTML page (with data-kw-map on its root) at a public https:// address.
  2. Open it in your browser, click the Keyboard Warriors extension, and press Create.
  3. The server bakes your page with your exact geometry. From then on, anyone who opens the extension on that address sees the arena and can join. Share the page's link and people play there.

Players choose Team Deathmatch or Deathmatch when they join; every map supports both (see Spawns and teams).

The bake loads your page the way a visitor would, within limits:

Limit Value
Page size 320 × 240 to 4000 × 4000 page pixels (see Size and scale)
Time to load about 20 seconds
Requests GET/HEAD only, at most 300 (images, fonts, stylesheets, scripts)
Rebuild when a new match starts on an arena built in an earlier hour, so edits to your page show up

Keep the page self-contained and light: inline styles, a few images, no logins or cookie walls in the way.

Testing your map

You test the same way you publish: with the extension. The quick loop while you're building:

  1. Host the page somewhere public (a free static host is fine) and open it.
  2. Press Create in the extension, then Start a match and walk your map.
  3. Edit the page, and give its address a new query string: …/my-map/?v=2, then ?v=3. An arena belongs to its exact address, so each new address is a fresh bake right away. (Once an address has an arena, the extension offers Join instead of Create, and the arena is rebuilt at most once an hour.) Create allows about 5 bakes per 10 minutes per person, so that's your test pace.
  4. When it plays right, share the plain address. That one gets your latest version the next time it's rebuilt.

What to check on each run: