game-sprite
A sprite references a pattern bank or contains its own image. Outside a world, x/y/width/height/scale preserve the original logical-screen API. Within a world, x/y/z/size map to CSS 3D transforms. Sprite sheets animate with CSS keyframes and steps(). Player, enemy and scenery behavior is authored on the same graphics record. Native enemy labels can contain checkbox controls for script-free targeting.
Examples
Bitmap sprite
Scale the sprite from its top-left corner.
<game-screen width="128" height="64">
<game-sprite id="sprite-pine" x="24" y="12" width="16" height="24" scale="2">
<img src="/games/aether-wing/pine.png" alt="Emerald Reach pine">
</game-sprite>
</game-screen>
Overlapping sprites
The moth paints above the mirrored tree because its priority is higher, even though it appears first in the markup. Ties follow document order. Perspective worlds use depth instead.
<game-screen width="128" height="64" role="group" aria-label="Sprite drawing order">
<game-sprite id="priority-moth" x="42" y="24" width="28" height="28" priority="2" style="overflow:hidden">
<img src="/games/aether-wing/moth.png" alt="Priority moth" style="width:800%;max-width:none">
</game-sprite>
<game-sprite x="55" y="6" width="30" height="48" priority="1" flip-x>
<img src="/games/aether-wing/pine.png" alt="Mirrored pine">
</game-sprite>
</game-screen>
Sprites in a perspective world
Launch and steer to see the pilot bank. Scenery and enemies scale with distance, and their depth controls overlap.
<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>



API
| Name | Type or default | Behavior |
|---|---|---|
x |
number
Default 0 |
Left edge in logical pixels. |
y |
number
Default 0 |
Top edge in logical pixels. |
width |
number
Default 8 |
Unscaled bitmap width. Minimum 1. |
height |
number
Default 8 |
Unscaled bitmap height. Minimum 1. |
scale |
number
Default 1 |
Size multiplier from the top-left corner, clamped to 0.125–16. |
flip-x |
boolean
|
Presence mirrors the bitmap horizontally. |
frames |
number
Default 1 |
Number of consecutive sheet frames. CSS steps through them. A player selects a bank frame from input. |
fps |
number
Default 8 |
CSS sprite playback rate. |
start-x |
number
Default 0 |
Initial horizontal position, clamped to authored bounds. |
start-y |
number
Default 1.1 |
Initial height, clamped to authored bounds. |
min-x |
number
Default -2.7 |
Minimum horizontal position, bounded to -10–10. |
max-x |
number
Default 2.7 |
Maximum horizontal position, bounded to -10–10 and at least min-x. |
min-y |
number
Default 0.3 |
Minimum height, bounded to -2–10. |
max-y |
number
Default 2.4 |
Maximum height, bounded to -2–10 and at least min-y. Equal vertical bounds constrain an actor to the ground. |
speed |
number
Default 3.2 |
Movement world units per second, clamped to 0.1–12. |
max-health |
number
Default 5 |
Initial shield capacity, integer 1–99. |
radius |
number
Default 0.18 |
Collision radius in world units, clamped to 0.05–1. |
size |
number
Default 1.2 |
Billboard height in world units. |
fire-rate |
number
Default 0.16 |
Seconds between player shots, clamped to 0.06–2. |
shot-speed |
number
Default 70 |
Forward shot speed, clamped to 20–180. |
recovery |
number
Default 1.2 |
Invulnerability after a hit, seconds clamped to 0.1–5. |
health |
number
|
For a player, reflected remaining shield. For an enemy, the authored number of shots needed to destroy it (default 1). |
at |
number
Default 30 |
Forward distance at which the first enemy spawns. |
interval |
number
Default 35 |
Distance between spawns, clamped to 1–1000. |
count |
number
Default 1 |
Number of enemies, integer 0–50. |
behavior |
string
Default straight |
straight, sine, dive, or chase. |
z |
number
Default 60 |
World depth. CSS translate3d projects it through the screen perspective. |
spread |
number
Default 0 |
Alternating offset to either side of x, clamped to 0–5. |
sway |
number
Default 1 |
Horizontal amplitude for sine or dive motion. |
frequency |
number
Default 2 |
Sine angular frequency, clamped to 0–8. |
dive-rate |
number
Default 0.45 |
Height lost per second in dive movement, clamped to 0–2. |
tracking |
number
Default 0.8 |
Chase response coefficient, clamped to 0–3. |
score |
number
Default 100 |
Points awarded on destruction, rounded 0–10000. |
fire-delay |
number
Default 1.2 |
Delay before the first shot, seconds clamped to 0.1–20. |
spacing |
number
Default 16 |
World distance between repeating copies, clamped to 2–200. |
offset |
number
Default 0 |
Initial world distance, clamped to 0–200. |
actor |
string
|
Contextual behavior in a world: player, scenery, or enemy. Rendering uses the same sprite primitive. |
tileset |
string
|
ID of a pattern bank. Its image provides equal-sized, row-major sprite frames. |
aspect |
number
Default 1 |
Frame width divided by frame height for CSS world sizing. |
frame |
number
|
Reflected selected frame for a player sprite. |
position-x |
number
|
Reflected player center coordinate, separate from the image base x/y. |
position-y |
number
|
Reflected player center altitude. |
priority |
integer
Default 0 |
Stacking order in a flat screen. Higher values paint above lower values. Equal values paint in document order. Perspective worlds order sprites by depth. |
invulnerable |
boolean
|
Reflected during player recovery after a hit. The world stylesheet flashes the pilot sheet while this attribute is present. |
| Name | Type or default | Behavior |
|---|---|---|
game-shot |
CustomEvent<{ x: number, y: number }>
|
Bubbles and is composed. Emitted on each accepted shot. |
game-player-hit |
CustomEvent<{ health: number, source: "enemy" | "projectile" }>
|
Bubbles and is composed. Emitted on an accepted hit, after applying one point of damage and the recovery interval. |
game-enemy-hit |
CustomEvent<{ remaining: number, destroyed: boolean, score: number }>
|
Bubbles and is composed. Emitted from the wave for each accepted player shot. Score increases once when an enemy is destroyed. |
Listened-for events
None.
CSS custom properties
None.
| Name | Type or default | Behavior |
|---|---|---|
data-native-target |
On the label wrapping a target checkbox and sprite image, enables pointer targeting without JavaScript. |