Game Kit
Build a one-sector version of Aether Wing from an empty viewport, then add a briefing and a launch menu. Each step introduces an element and shows the HTML alongside its result.
Import Game Kit
Vendor the Game Kit package beside your HTML file, then load its stylesheet and ES module in the page head. Game Kit has no kit dependencies. The examples use Aether Wing’s game art. Copy the images you need into your project or point the image paths at your own art. The documentation sliders and readouts let you change attributes and inspect events.
<link rel="stylesheet" href="./game-kit/styles.css">
<script type="module" src="./game-kit/index.js"></script>
Set the flight viewport
Set width="320" and height="200" to establish Aether Wing’s logical resolution. The screen scales to fit and clips its children. Set camera-x and camera-y to offset its direct layers.
<game-screen width="320" height="200" role="img"
aria-label="320 by 200 pixel viewport"></game-screen>
The viewport starts empty. Its background shows the area that the graphics will occupy.
Place the pilot
Place a bitmap with x and y. The running world sets the player’s z depth and selects a bank frame. The player emits game-shot and game-player-hit during flight.
<game-screen width="320" height="200">
<game-sprite x="144" y="142" width="32" height="32" style="overflow:hidden">
<img src="/games/aether-wing/pilot.png" alt="Aether Wing pilot"
style="width:500%;max-width:none;transform:translateX(-40%)">
</game-sprite>
</game-screen>
Crop a single frame from the five-frame pilot image. The later world step uses the same sheet for banking.
Build the horizon
Use factor to give distant mountains and closer trees different scroll speeds. Aether Wing’s stage layers add drift and use mode="perspective" for the ground plane.
<game-screen id="pilot-camera" width="320" height="200" camera-x="0">
<game-tileset id="pilot-camera-reach-mountains" tile-width="16" tile-height="16"><img src="/games/aether-wing/reach-mountains-tiles.png" alt=""></game-tileset>
<game-layer factor="0.25" drift="0.04" style="right:-50%">
<game-tilemap tileset="pilot-camera-reach-mountains" columns="12" rows="5" style="--sheet-columns:16;--sheet-rows:3" role="img" aria-label="Emerald Reach mountains">
<game-tile index="0" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="6" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="7" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="8" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="10" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="11" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="12" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile>
<game-tile index="0" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="6" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="14" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="15" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="16" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="17" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile>
<game-tile index="25" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="26" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="27" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="28" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="29" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="30" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile>
<game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="36" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="37" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="38" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile>
<game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile>
</game-tilemap>
</game-layer>
<game-tileset id="pilot-camera-reach-forest" tile-width="16" tile-height="16"><img src="/games/aether-wing/reach-forest-tiles.png" alt=""></game-tileset>
<game-layer factor="1" drift="0.1" style="right:-50%">
<game-tilemap tileset="pilot-camera-reach-forest" columns="12" rows="5" style="--sheet-columns:16;--sheet-rows:3" role="img" aria-label="Emerald Reach forest">
<game-tile index="0" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile>
<game-tile index="0" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile>
<game-tile index="6" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="7" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="8" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="10" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="11" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="12" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="13" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="14" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="15" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="16" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="17" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile>
<game-tile index="26" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="27" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="28" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="29" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="30" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="31" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="32" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="33" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="34" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="35" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="36" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="37" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile>
<game-tile index="41" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="41" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="41" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="41" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="41" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="41" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="41" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="41" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="41" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="41" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="41" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile><game-tile index="41" style="background-image:url('/games/aether-wing/reach-forest-tiles.png')"></game-tile>
</game-tilemap>
</game-layer>
<game-sprite x="144" y="142" width="32" height="32" style="overflow:hidden">
<img src="/games/aether-wing/pilot.png" alt="Aether Wing pilot"
style="width:500%;max-width:none;transform:translateX(-40%)">
</game-sprite>
</game-screen>
The layers scroll at different speeds as the camera moves. The pilot stays in front.


Lay out the terrain
Set columns and rows to arrange the ground in a grid. Aether Wing repeats a two-by-two ground map. Its scenery layers use larger maps that reference bitmap patterns.
<game-screen width="80" height="48">
<game-tilemap columns="2" rows="2" role="img" aria-label="Two-by-two terrain">
<span style="background:#265c67"></span><span style="background:#398f85"></span>
<span style="background:#398f85"></span><span style="background:#265c67"></span>
</game-tilemap>
</game-screen>
Start with the checker pattern used by the ground.
Name each terrain cell
A tile’s index identifies its pattern. Repeat indices 0 and 1 to build the ground. Inside a pattern bank, a tile can instead contain whitespace-separated pixel indices.
<game-screen width="80" height="48">
<game-tilemap columns="2" rows="2" role="img" aria-label="Indexed terrain tiles">
<game-tile index="0" style="background:#265c67"></game-tile>
<game-tile index="1" style="background:#398f85"></game-tile>
<game-tile index="1" style="background:#398f85"></game-tile>
<game-tile index="0" style="background:#265c67"></game-tile>
</game-tilemap>
</game-screen>
An index can occur more than once. Here each index shares an authored CSS color.
Declare a pattern bank
Give the bank an id that sprites and maps can reference via tileset. Set tile-width and tile-height to describe the image grid.
<game-tileset id="pilot-bank" tile-width="32" tile-height="32" style="display:block">
<img src="/games/aether-wing/pilot.png" width="160" height="32" alt="Pilot frame sheet">
</game-tileset>
<game-screen width="96" height="32">
<game-tilemap tileset="pilot-bank" columns="3" rows="1" style="--sheet-columns:5"
role="img" aria-label="Left, center, and right pilot frames">
<game-tile index="0" style="background-image:url('/games/aether-wing/pilot.png')"></game-tile>
<game-tile index="2" style="background-image:url('/games/aether-wing/pilot.png')"></game-tile>
<game-tile index="4" style="background-image:url('/games/aether-wing/pilot.png')"></game-tile>
</game-tilemap>
</game-screen>
The bank describes a single row of five 32 × 32 pilot frames. The map reads frames 0, 2, and 4 from the same image using CSS background positions. The visible image inside the bank is useful when inspecting an atlas.
Choose the ground colors
Use colors for the indexed color list. Set cycle-start and cycle-rate to describe the cycle. The CSS ground effect uses the corresponding --palette-0 through --palette-3 and --cycle-rate values.
<game-palette id="reach-colors" colors="#265c67 #398f85 #64c2a1 #a7debd"
cycle-start="2" cycle-rate="5"></game-palette>
<game-screen width="128" height="24">
<game-tilemap palette="reach-colors" columns="4" rows="1" role="img" aria-label="Emerald Reach palette swatches">
<game-tile index="0" style="background:#265c67"></game-tile>
<game-tile index="1" style="background:#398f85"></game-tile>
<game-tile index="2" style="background:#64c2a1"></game-tile>
<game-tile index="3" style="background:#a7debd"></game-tile>
</game-tilemap>
</game-screen>
These are the four Emerald Reach colors. The first two fill alternating ground tiles and the last two alternate on the grid lines. The palette element records the indices and cycle rate. Aether Wing also assigns those colors to CSS properties on the ground layer.
Assemble a sector
Group the terrain and scenery under a stage with a unique id. Set name, length, and speed. Add the horizon and terrain layers from the previous steps. When a running world activates the stage, it emits game-stage-change from that stage.
<game-palette id="reach-stage-colors" colors="#265c67 #398f85 #64c2a1 #a7debd"></game-palette>
<game-screen width="128" height="64">
<game-stage id="reach-preview" name="Emerald Reach" length="480" speed="18">
<game-tileset id="reach-preview-reach-mountains" tile-width="16" tile-height="16"><img src="/games/aether-wing/reach-mountains-tiles.png" alt=""></game-tileset>
<game-layer factor="0.18" drift="0.04">
<game-tilemap tileset="reach-preview-reach-mountains" columns="12" rows="5" style="--sheet-columns:16;--sheet-rows:3" role="img" aria-label="Mountains over Emerald Reach">
<game-tile index="0" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="6" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="7" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="8" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="10" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="11" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="12" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile>
<game-tile index="0" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="6" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="14" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="15" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="16" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="17" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile>
<game-tile index="25" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="26" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="27" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="28" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="29" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="30" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile>
<game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="36" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="37" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="38" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile>
<game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile>
</game-tilemap>
</game-layer>
<game-layer style="top:52%">
<game-tilemap columns="2" rows="2" palette="reach-stage-colors">
<game-tile index="0" style="background:#265c67"></game-tile>
<game-tile index="1" style="background:#398f85"></game-tile>
<game-tile index="1" style="background:#398f85"></game-tile>
<game-tile index="0" style="background:#265c67"></game-tile>
</game-tilemap>
</game-layer>
<game-sprite x="22" y="24" width="22" height="36">
<img src="/games/aether-wing/pine.png" alt="Trackside pine">
</game-sprite>
</game-stage>
</game-screen>
This flat preview shows the stage’s authored scenery before the world projects it into depth.

Wire up flight controls
Buttons use data-action to name commands. Set deadzone and fire-button for controller input. Listen for game-input to receive direction, fire state, and commands, and game-controller-change to detect a controller change.
<game-input deadzone="0.18" fire-button="0">
<div role="group" aria-label="Flight input example">
<button type="button" data-action="left">Left</button>
<button type="button" data-action="right">Right</button>
<button type="button" data-action="fire">Fire</button>
</div>
</game-input>
Hold a direction or press an arrow key with a control focused. Press Fire to send a command. A connected controller uses the same events.
Fly the assembled world
Wrap the screen, stage, pattern banks, player, and input in game-world. Observe state, score, and stage. The world emits game-world-change when its state changes and game-world-error if it cannot initialize.
<game-world aria-label="Flight demonstration">
<game-tileset id="training-pilot" tile-width="32" tile-height="32"><img src="/games/aether-wing/pilot.png" alt=""></game-tileset>
<game-tileset id="training-moth" tile-width="32" tile-height="32"><img src="/games/aether-wing/moth.png" alt=""></game-tileset>
<game-tileset id="training-pine" tile-width="32" tile-height="48"><img src="/games/aether-wing/pine.png" alt=""></game-tileset>
<game-tileset id="training-ground" tile-width="8" tile-height="8">
<game-tile index="0">3 3 3 3 3 3 3 3
3 0 0 0 0 0 0 0
3 0 0 0 0 0 0 0
3 0 0 0 0 0 0 0
3 0 0 0 0 0 0 0
3 0 0 0 0 0 0 0
3 0 0 0 0 0 0 0
3 0 0 0 0 0 0 0</game-tile>
<game-tile index="1">3 3 3 3 3 3 3 3
3 1 1 1 1 1 1 1
3 1 1 1 1 1 1 1
3 1 1 1 1 1 1 1
3 1 1 1 1 1 1 1
3 1 1 1 1 1 1 1
3 1 1 1 1 1 1 1
3 1 1 1 1 1 1 1</game-tile>
</game-tileset>
<game-input deadzone="0.18" fire-button="0">
<form>
<fieldset data-native-controls><legend>Course controls</legend>
<label><input type="checkbox" data-native-run>Run course</label>
<label><input type="checkbox" data-native-pause>Pause motion</label>
</fieldset>
<fieldset data-native-controls><legend>Move horizontally</legend>
<label><input type="radio" name="training-x" data-axis="x" value="-1">Left</label>
<label><input type="radio" name="training-x" data-axis="x" value="0" checked>Center</label>
<label><input type="radio" name="training-x" data-axis="x" value="1">Right</label>
</fieldset>
<fieldset data-native-controls><legend>Move vertically</legend>
<label><input type="radio" name="training-y" data-axis="y" value="1">Up</label>
<label><input type="radio" name="training-y" data-axis="y" value="0" checked>Middle</label>
<label><input type="radio" name="training-y" data-axis="y" value="-1">Down</label>
</fieldset>
<fieldset data-native-controls><legend>Sector</legend>
<label><input type="radio" name="training-stage" data-stage-choice="0" checked>Training range</label>
</fieldset>
<game-screen data-world-viewport width="320" height="200" tabindex="0" role="group" aria-label="Training world viewport">
<game-stage id="training-sector" data-stage-index="0" name="Training range" length="120" speed="12" sky="#213959" haze="#b0d5ce">
<game-tileset id="training-reach-mountains" tile-width="16" tile-height="16"><img src="/games/aether-wing/reach-mountains-tiles.png" alt=""></game-tileset>
<game-layer factor="0.18" drift="0.04">
<game-tilemap tileset="training-reach-mountains" columns="12" rows="5" style="--sheet-columns:16;--sheet-rows:3">
<game-tile index="0" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="6" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="7" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="8" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="10" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="11" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="12" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile>
<game-tile index="0" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="0" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="6" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="14" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="15" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="16" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="17" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile>
<game-tile index="25" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="26" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="27" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="28" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="29" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="30" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile>
<game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="36" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="37" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="38" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile>
<game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile><game-tile index="9" style="background-image:url('/games/aether-wing/reach-mountains-tiles.png')"></game-tile>
</game-tilemap>
</game-layer>
<game-palette id="training-palette-1" colors="#296b72 #48a99a #81cfb3 #b8e8c5" cycle-start="2" cycle-rate="5"></game-palette>
<game-layer mode="perspective" cell-size="4" style="--palette-0:#296b72;--palette-1:#48a99a;--palette-2:#81cfb3;--palette-3:#b8e8c5;--cycle-rate:5">
<game-tilemap palette="training-palette-1" tileset="training-ground" columns="2" rows="2">
<game-tile index="0"></game-tile><game-tile index="1"></game-tile><game-tile index="1"></game-tile><game-tile index="0"></game-tile>
</game-tilemap>
</game-layer>
<game-sprite actor="scenery" aspect="0.666667" tileset="training-pine" x="-4" size="4" spacing="16"><img src="/games/aether-wing/pine.png" alt=""></game-sprite>
<game-sprite actor="scenery" aspect="0.666667" tileset="training-pine" x="4" size="4" spacing="16" offset="8"><img src="/games/aether-wing/pine.png" alt=""></game-sprite>
<game-sprite actor="enemy" frames="8" fps="10" tileset="training-moth" at="8" interval="30" count="4" x="0" y="1.1" z="32" behavior="straight"
health="1" score="100" fire-rate="1.1" fire-delay="0.2" shot-speed="24"><label data-native-target><input type="checkbox" aria-label="Shoot moth"><span><img src="/games/aether-wing/moth.png" alt=""></span></label></game-sprite>
<div data-native-cleared>Sector cleared. Choose the next sector.</div></game-stage>
<game-sprite actor="player" tileset="training-pilot" frames="5" fps="0" max-health="3" speed="3.2" radius="0.18"
size="0.8" fire-rate="0.14" shot-speed="75" recovery="1.2" start-x="0" start-y="1.1"><img src="/games/aether-wing/pilot.png" alt=""></game-sprite>
</game-screen>
<p data-native-controls>Run the course, steer with the radio controls, and select an approaching enemy to shoot. Clear the targets in each sector.</p>
<button type="reset" data-native-controls>Reset course</button>
<p data-native-victory role="status">All sectors cleared.</p>
<div data-world-hud aria-label="Flight instruments">
<label>Shield <output data-world-shield aria-label="Shield" aria-live="off">3</output></label>
<label>Score <output data-world-score aria-label="Score" aria-live="off">000000</output></label>
<label>Sector <output data-world-sector aria-label="Sector" aria-live="off">Training range</output></label>
<label>Route <output data-world-progress aria-label="Route progress" aria-live="off">0%</output></label>
</div>
<p role="status" aria-label="Flight status" data-world-status>Run the course and select an approaching target to shoot.</p>
<div data-world-controls role="group" aria-label="Game controls">
<button type="button" data-action="start" disabled>Launch</button>
<button type="button" data-action="pause" disabled>Pause</button>
<button type="button" data-action="restart" disabled>Restart flight</button>
</div>
<div data-world-controls role="group" aria-label="Flight controls">
<button type="button" data-action="left" aria-label="Fly left" disabled>←</button>
<button type="button" data-action="up" aria-label="Fly up" disabled>↑</button>
<button type="button" data-action="down" aria-label="Fly down" disabled>↓</button>
<button type="button" data-action="right" aria-label="Fly right" disabled>→</button>
<button type="button" data-action="fire" disabled>Fire</button>
</div>
<p data-world-controller>Keyboard / touch</p>
</form>
</game-input>
</game-world>
Launch, steer, and fire. With scripting disabled, use Run course, the native direction controls, and the target checkbox. The training sector uses the same projection and collision rules as Aether Wing. Pause and resume to inspect the scene, or restart to reset score and health.



Add a preflight briefing
A scene holds an ordinary HTML screen. Use a unique id as its fragment destination. A flow uses data-start for its native default and reflects the current selection with active.
<game-scene id="observation">
<h3>Observation deck</h3>
<div>Fly through Emerald Reach, Amber Expanse, and Violet Citadel.</div>
</game-scene>
A scene can hold a briefing, a map, or the game itself.
Observation deck
Explain the controls
Wrap a native dialog in a modal element. Open it with a button command, or set the boolean open attribute after upgrade. The wrapper emits game-modal-change after the native dialog toggles.
<button type="button" command="show-modal" commandfor="flight-controls-dialog">Show flight controls</button>
<game-modal>
<dialog id="flight-controls-dialog" aria-label="Flight controls briefing">
<p>Steer with the arrow keys or WASD. Hold Space to fire.</p>
<form method="dialog"><button autofocus>Close flight controls</button></form>
</dialog>
</game-modal>
Open the controls briefing and close it with the button or Escape.
Link the briefing to launch
Put scenes inside a flow. Set start to the briefing ID and mark the same scene with data-start. Links select scenes through the URL fragment. The flow reflects selection in scene and emits game-scene-change.
<game-flow start="aether-briefing">
<game-scene id="aether-briefing" data-start tabindex="-1">
<h3>Mission briefing</h3>
<game-menu><a href="#aether-launch" data-key="1">Prepare to launch</a></game-menu>
</game-scene>
<game-scene id="aether-launch" tabindex="-1">
<h3>Ready to fly</h3>
<a href="/games/aether-wing/">Launch the full game</a>
<a href="#aether-briefing">Return to briefing</a>
</game-scene>
</game-flow>
Follow Prepare to launch to switch screens. The next link opens the full course.
Mission briefing
Ready to fly
Launch the full game Return to briefingAdapt the data for a console
Pattern banks, tile indices, palettes, and sprite records can feed a tool targeting a retro console. It would convert the images and scene data to the target’s tile formats and map projection and input to hardware routines. The browser examples use CSS transforms and raster effects.