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.

Bitmap sprite HTML
<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>
Emerald Reach pine

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.

Overlapping sprites HTML
<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>
Priority moth Mirrored pine

Sprites in a perspective world

Launch and steer to see the pilot bank. Scenery and enemies scale with distance, and their depth controls overlap.

Sprites in a perspective world HTML
<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>
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 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
Course controls
Move horizontally
Move vertically
Sector
Sector cleared. Choose the next sector.

Run the course, steer with the radio controls, and select an approaching enemy to shoot. Clear the targets in each sector.

All sectors cleared.

Run the course and select an approaching target to shoot.

Keyboard / touch

API

Attributes
NameType or defaultBehavior
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.
Emitted events
NameType or defaultBehavior
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.

Markup hooks
NameType or defaultBehavior
data-native-target On the label wrapping a target checkbox and sprite image, enables pointer targeting without JavaScript.

View the Game Kit manifest