/*

Reduced Motion: a warp starfield that damps itself when the viewer asks for less motion.
@description A warp starfield whose speed, streaks, shake, and blinking all damp for reduced motion.

Part of the BLIT386 demo series.

Prerequisites:
  - Basics: https://demos.blit386.dev/basics
  - Starfield: https://demos.blit386.dev/starfield

Live version: https://demos.blit386.dev/reduced-motion

Guides:
  - Splash: https://blit386.dev/docs/guides/splash#reduced-motion
  - Game loop: https://blit386.dev/docs/guides/game-loop#smoothing-motion-between-updates

Reference:
  - Core: https://blit386.dev/docs/api/core#reduced-motion
  - Camera: https://blit386.dev/docs/api/camera
  - Easing: https://blit386.dev/docs/api/easing#applyeasing

---

WHAT YOU WILL SEE
-----------------

A 320x240 cockpit window looking straight down the flight path: a two-tone frame around
the screen and an instrument housing in the top-right corner with a 12x12 lamp labeled
"WARP", and under it the current phase and the jump count ("CRUISE  x0"). 140 stars
drift out from the center. The flight cycles through four phases: cruise (2.5 s), jump
(0.75 s), warp (2 s), and drop (0.75 s). At the jump the stars speed up and stretch into
long radial streaks, the hull kicks sideways by up to 5 pixels, and the lamp starts
blinking twice a second. The drop reverses it and the cycle repeats.

Three UI kit panels sit around the window: "Reduced motion" (top-left) shows the OS
preference, the local override, the effective state, and a counter of change-hook calls;
"Controls" under it holds the two controls; "When reduced" (bottom-right) lists the four
damped effects. Everything sits clear of the engine overlay's bars, so opening it with
the backquote key hides nothing.

When reduced motion is preferred, the demo keeps flying but damps every effect: stars at
20 percent speed, no streaks, no shake, and a lamp that stays lit instead of blinking. It
never freezes, because a frozen screen reads as a crash.

WHAT YOU WILL LEARN
-------------------

- BT.isReducedMotionPreferred resolves the ?reducedmotion / ?noreducedmotion URL flags
  over the browser's prefers-reduced-motion media query. The demo reads it once in init().
- onReducedMotionChange(prefersReduced) on the demo class is called by the engine when the
  preference changes while the page is open. It reports changes only, never the initial
  value.
- Perspective projection: dividing a star's offset by its depth turns a flat list of
  points into a 3D tunnel, and the outward acceleration falls out of that division.
- Deriving a streak from speed: the tail is the star's position STREAK_TICKS ticks ago, so
  one speed number controls both the motion and the streak length.
- BT.renderAlpha blends each star's previous and current depth so the field moves smoothly
  between fixed updates.
- Screen shake as a camera offset (BT.cameraSet), and why BT.cameraReset() must run before
  the UI kit draws.
- applyEasing(t, 'cubic-in-out') for the speed ramps at the jump and the drop.
- Degrading motion instead of deleting it: slower, shorter, steadier - not stopped.
- overlayStyle in configure() picks the engine overlay's colors from the palette.

HOW THIS DIFFERS FROM THE STARFIELD DEMO
----------------------------------------

The Starfield demo (https://demos.blit386.dev/starfield) fakes depth: three flat layers
scrolling sideways at three speeds. This demo computes depth instead. Each star has a real
distance, and the screen position is worked out by dividing by it, so a star speeds up on
its own as it gets closer. Nothing in the code adds that acceleration - it falls out of
the division.

TRY THIS
--------

- Press M (or tap "M Force reduced") to force reduced motion on without touching the OS.
  The checkbox can only add reduced motion on top of the system setting, never remove it.
- Press J (or tap "J Jump / drop") to jump to warp or drop back out right now. The
  button does nothing while a jump or drop is already in progress.
- Press the backquote key (`) or click the small icon in the bottom-left corner to open
  the engine overlay with the frame rate and timings.
- Turn the system's reduce-motion setting on or off while the page is open and watch the
  "Hook fired" counter and the "Last hook" line change. No reload is needed.
- Add ?reducedmotion or ?noreducedmotion to the URL to start in either state. A URL flag
  pins the answer, so the change hook stays quiet while it is present.

The engine's own startup splash follows the same preference by itself: it holds a still
frame instead of fading and dissolving.

*/

import { function applyEasing(t: number, easing: EasingFunction): number
Applies an easing curve to a normalized time value.
@since1.0.3@changed1.5.0 Added sine, cubic, quartic, quintic, expo, circ, back, elastic, and bounce families.@paramt - Normalized time in [0, 1]. Values outside this range are not clamped.@parameasing - Easing curve to apply.@returnsEased value. Guaranteed to return 0 for `t = 0` and 1 for `t = 1`.
applyEasing
, function bootstrap(DemoClass: DemoConstructor, options?: BootstrapOptions): Promise<boolean>
One-liner bootstrap function for BLIT386 demos. Handles canvas retrieval and engine initialization. Backend selection (WebGPU or software fallback) is managed internally by BTAPI. This function provides a streamlined way to start a demo with sensible defaults while allowing customization through options.
@since0.2.0@changed1.4.0 Calling `bootstrap()` again while already initialized now routes to a hot swap (via {@link registerHotReload}) when a Vite HMR context is registered, or logs a double-bootstrap guard and returns `false` otherwise - previously it silently started a second, unstoppable `GameLoop`.@changed1.7.0 Exposes `BT` on `window.BT` after bootstrap finishes, gated by {@link BootstrapOptions.exposeGlobal} (default: {@link BT.isDevMode}).@paramDemoClass - Demo class constructor implementing `IBTDemo` (optional `configure()` for hardware settings).@paramoptions - Optional configuration for IDs and callbacks.@returns`true` when the demo boots successfully; otherwise `false`.@example// Simplest usage - uses default IDs. bootstrap(MyDemo);@example// With custom options. bootstrap(MyDemo, { canvasID: 'custom-canvas', containerID: 'custom-container', onSuccess: () => console.log('Demo started!'), onError: (err) => analytics.trackError(err), });@example// Await the result. const success = await bootstrap(MyDemo); if (success) { console.log('Demo is running'); }
bootstrap
,
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
, class Color32
Mutable 32-bit RGBA color value with 8-bit channels.
@since0.1.0
Color32
, class Rect2i
Integer rectangle for pixel-perfect bounds and regions. Used throughout the engine for sprite regions, display-space bounds, and zero-allocation geometry helpers. Both convenience getters and allocation-free `*To()` helpers are provided so callers can choose between readability and hot-path efficiency.
@since0.1.0
Rect2i
, class Vector2i
Integer 2D vector for pixel-perfect positioning. Used for points, sizes, directions, and camera offsets throughout the engine. The API includes both allocation-free `*To()` / `*InPlace()` variants and convenience methods that return new vectors.
@since0.1.0
Vector2i
} from 'blit386';
import { import applyThemeapplyTheme, import THEME_OVERLAY_STYLETHEME_OVERLAY_STYLE, import uiui } from './shared/ui.js'; /** @typedef {import('blit386').Palette} Palette */ /** @typedef {{ x: number, y: number, z: number, prevZ: number, speedScale: number }} Star */ // Logical screen size in game pixels, and the vanishing point everything flies out of. const const DISPLAY_W: 320DISPLAY_W = 320; const const DISPLAY_H: 240DISPLAY_H = 240; const const CENTER_X: numberCENTER_X = const DISPLAY_W: 320DISPLAY_W / 2; const const CENTER_Y: numberCENTER_Y = const DISPLAY_H: 240DISPLAY_H / 2; // Palette slots for the scene. The shared UI kit owns slots 240-251 (see applyTheme in // init()), so scene colors live down at the bottom where they can never collide. const const C_BG: 2C_BG = 2; // deep space: almost black, faintly blue const const C_STAR_FAR: 3C_STAR_FAR = 3; // dim gray: stars still a long way off const const C_STAR_MID: 4C_STAR_MID = 4; // middle distance const const C_STAR_NEAR: 5C_STAR_NEAR = 5; // near white: about to fly past the window const const C_FRAME: 6C_FRAME = 6; // cockpit window frame, lit edge const const C_FRAME_DARK: 7C_FRAME_DARK = 7; // cockpit window frame, shadowed inner edge const const C_HULL: 8C_HULL = 8; // the instrument housing the lamp sits in const const C_LAMP_ON: 9C_LAMP_ON = 9; // lamp lit: amber, never a saturated red (see the flash note below) const const C_LAMP_DIM: 10C_LAMP_DIM = 10; // lamp unlit: a dark amber so the bulb is still visible const const C_LAMP_RIM: 11C_LAMP_RIM = 11; // lamp bezel and the small "WARP" label // How many stars fly at once. Enough to feel dense at warp, few enough that every one can // afford to draw a line instead of a dot. const const STAR_COUNT: 140STAR_COUNT = 140; // The depth range. Stars are born at Z_FAR and rush toward the cockpit; Z_NEAR is the // point where they have flown past and get recycled to the back of the queue. const const Z_NEAR: 0.08Z_NEAR = 0.08; const const Z_FAR: 1.6Z_FAR = 1.6; // How strongly depth spreads stars apart on screen. Bigger means a wider, faster tunnel. const const PROJECT_SCALE: 150PROJECT_SCALE = 150; // How far off-center a star is born, in world units. Never zero: a star sitting exactly // on the vanishing point would stay a single motionless pixel in the middle forever. const const SPAWN_RADIUS_MIN: 0.06SPAWN_RADIUS_MIN = 0.06; const const SPAWN_RADIUS_MAX: 0.9SPAWN_RADIUS_MAX = 0.9; // Per-star speed variety, as a multiplier on the ship's speed. const const SPEED_SCALE_MIN: 0.85SPEED_SCALE_MIN = 0.85; const const SPEED_SCALE_MAX: 1.15SPEED_SCALE_MAX = 1.15; // How much depth the ship eats per tick while cruising, and while at warp. The whole // "speed" of the demo is this one number; everything else is derived from it. const const SPEED_CRUISE: 0.004SPEED_CRUISE = 0.004; const const SPEED_WARP: 0.055SPEED_WARP = 0.055; // The four phases of the flight, and how many ticks (1/60 second each) each one lasts. const const PHASE_CRUISE: "cruise"PHASE_CRUISE = 'cruise'; const const PHASE_JUMP: "jump"PHASE_JUMP = 'jump'; const const PHASE_WARP: "warp"PHASE_WARP = 'warp'; const const PHASE_DROP: "drop"PHASE_DROP = 'drop'; const
const PHASE_TICKS: {
    cruise: number;
    jump: number;
    warp: number;
    drop: number;
}
PHASE_TICKS
= {
[const PHASE_CRUISE: "cruise"PHASE_CRUISE]: 150, [const PHASE_JUMP: "jump"PHASE_JUMP]: 45, [const PHASE_WARP: "warp"PHASE_WARP]: 120, [const PHASE_DROP: "drop"PHASE_DROP]: 45, }; // A streak is the same star drawn twice: once where it is, once where it was a moment // ago. This is how many ticks "a moment ago" means. Because the gap is measured in ticks // of the current speed, cruising gives a dot and warp gives a long line - one number, // both looks. const const STREAK_TICKS: 2STREAK_TICKS = 2; // Shorter than this on screen, the "line" is not worth drawing - use a single pixel. const const STREAK_MIN_PX: 1.5STREAK_MIN_PX = 1.5; // When reduced motion is on, the ship still flies, at a fifth of the speed. const const REDUCED_SPEED_SCALE: 0.2REDUCED_SPEED_SCALE = 0.2; // Screen shake: a kick this big, multiplied down each tick until it is small enough to // stop. const const SHAKE_START_PX: 5SHAKE_START_PX = 5; const const SHAKE_DECAY: 0.86SHAKE_DECAY = 0.86; const const SHAKE_STOP_PX: 0.35SHAKE_STOP_PX = 0.35; // The warp lamp blinks once every 30 ticks: on for 15, off for 15. At 60 ticks a second // that is 2 flashes a second. The accessibility limit is 3 flashes a second (WCAG 2.1, // "Three Flashes or Below Threshold"), because faster flashing can trigger seizures. Two // is comfortably under it, and the lamp is a 12x12 patch of a 320x240 screen. Never flash // the whole screen, and never flash saturated red: both have stricter limits still. const const LAMP_PERIOD_TICKS: 30LAMP_PERIOD_TICKS = 30; const const LAMP_ON_TICKS: 15LAMP_ON_TICKS = 15; // Screen layout. The engine overlay (toggled with the backquote key) covers the top 42 // rows and the bottom 14 when it is open, so everything the reader needs sits between // those bands, MARGIN pixels in from the cockpit frame's sides. The left column holds the // status panel with the controls under it; the right column holds the instrument housing // at the top and the legend at the bottom. The starfield shows through the middle. const const BAND_TOP: 46BAND_TOP = 46; const const BAND_BOTTOM: 223BAND_BOTTOM = 223; const const MARGIN: 8MARGIN = 8; const const PANEL_GAP: 6PANEL_GAP = 6; const const LEFT_PANEL_W: 150LEFT_PANEL_W = 150; const const STATUS_PANEL_H: 87STATUS_PANEL_H = 87; const const CONTROLS_Y: numberCONTROLS_Y = const BAND_TOP: 46BAND_TOP + const STATUS_PANEL_H: 87STATUS_PANEL_H + const PANEL_GAP: 6PANEL_GAP; const const LEGEND_W: 120LEGEND_W = 120; const const LEGEND_H: 73LEGEND_H = 73; const const LEGEND_X: numberLEGEND_X = const DISPLAY_W: 320DISPLAY_W - const MARGIN: 8MARGIN - const LEGEND_W: 120LEGEND_W; const const LEGEND_Y: numberLEGEND_Y = const BAND_BOTTOM: 223BAND_BOTTOM - const LEGEND_H: 73LEGEND_H; // The status panel's value column starts after this many characters, so the longest key, // "Hook fired", keeps a space after it. const const STATUS_KV_COLS: 11STATUS_KV_COLS = 11; // The instrument housing in the top-right corner: the warp lamp with its "WARP" label, // and under them the phase and the jump count, printed on the hull like cockpit readouts. const const HOUSING_RECT: Rect2iHOUSING_RECT = new new Rect2i(x?: number, y?: number, width?: number, height?: number): Rect2i
Creates an integer rectangle, truncating all inputs toward zero.
@paramx - Left-edge X coordinate (defaults to 0).@paramy - Top-edge Y coordinate (defaults to 0).@paramwidth - Width in pixels (defaults to 0).@paramheight - Height in pixels (defaults to 0).
Rect2i
(const DISPLAY_W: 320DISPLAY_W - const MARGIN: 8MARGIN - 74, const BAND_TOP: 46BAND_TOP, 74, 36);
const const LAMP_RECT: Rect2iLAMP_RECT = new new Rect2i(x?: number, y?: number, width?: number, height?: number): Rect2i
Creates an integer rectangle, truncating all inputs toward zero.
@paramx - Left-edge X coordinate (defaults to 0).@paramy - Top-edge Y coordinate (defaults to 0).@paramwidth - Width in pixels (defaults to 0).@paramheight - Height in pixels (defaults to 0).
Rect2i
(const HOUSING_RECT: Rect2iHOUSING_RECT.Rect2i.x: number
Left edge X coordinate (defaults to 0).
x
+ 6, const HOUSING_RECT: Rect2iHOUSING_RECT.Rect2i.y: number
Top edge Y coordinate (defaults to 0).
y
+ 5, 12, 12);
const const LAMP_LABEL_X: numberLAMP_LABEL_X = const LAMP_RECT: Rect2iLAMP_RECT.Rect2i.x: number
Left edge X coordinate (defaults to 0).
x
+ 18;
const const LAMP_LABEL_Y: numberLAMP_LABEL_Y = const LAMP_RECT: Rect2iLAMP_RECT.Rect2i.y: number
Top edge Y coordinate (defaults to 0).
y
+ 2;
const const READOUT_X: numberREADOUT_X = const LAMP_RECT: Rect2iLAMP_RECT.Rect2i.x: number
Left edge X coordinate (defaults to 0).
x
;
const const READOUT_Y: numberREADOUT_Y = const LAMP_RECT: Rect2iLAMP_RECT.Rect2i.y: number
Top edge Y coordinate (defaults to 0).
y
+ 18;
const const READOUT_RIGHT_X: numberREADOUT_RIGHT_X = const HOUSING_RECT: Rect2iHOUSING_RECT.Rect2i.x: number
Left edge X coordinate (defaults to 0).
x
+ const HOUSING_RECT: Rect2iHOUSING_RECT.Rect2i.width: number
Width in pixels (defaults to 0).
width
- 6;
// Every character of the system font is 6 pixels wide; its letters fill 5 of them. const const FONT_W: 6FONT_W = 6; // Depth thresholds picking which of the three star colors to draw with. const const BAND_FAR_Z: 1BAND_FAR_Z = 1.0; const const BAND_MID_Z: 0.4BAND_MID_Z = 0.4; // A star is recycled once it has flown this far past the edge of the window. const const CULL_MARGIN_PX: 12CULL_MARGIN_PX = 12; // A warp-speed cockpit whose motion damps itself when reduced motion is preferred. class class DemoDemo { /** @type {Palette | null} */ Demo.palette: Palette | null
@type{Palette | null}
palette
= null;
// Slot numbers of the shared UI kit colors, filled in init() by applyTheme(). Demo.theme: nulltheme = null; // One plain object per star: `x` and `y` are the sideways and up/down offsets in // world units, fixed until the star respawns; `z` is the distance from the cockpit, // shrinking every tick; `prevZ` is where z was at the start of this tick, for // interpolated drawing; `speedScale` is a small per-star multiplier so the field does // not move in lockstep. /** @type {Star[]} */ Demo.stars: {}
@type{Star[]}
stars
= [];
// Where the flight is in the cruise / jump / warp / drop cycle, and how long it has // been there. Demo.phase: stringphase = const PHASE_CRUISE: "cruise"PHASE_CRUISE; Demo.phaseTicks: numberphaseTicks = 0; // Depth eaten per tick right now. Recomputed every tick from the phase. Demo.warpSpeed: numberwarpSpeed = const SPEED_CRUISE: 0.004SPEED_CRUISE; // How many times the ship has jumped to warp, so the reader can see the button // worked. Demo.jumpCount: numberjumpCount = 0; // The operating system's answer, read ONCE in init() and kept current by the change // hook below. It is deliberately not read every frame: each read of // BT.isReducedMotionPreferred re-parses the URL flags and re-queries the media query, // which is wasted work sixty times a second. Demo.osPrefersReduced: booleanosPrefersReduced = false; // The M key / checkbox. It can only force reduced motion ON, never off: no demo // should offer a control that overrides someone's accessibility setting against them. Demo.forceReduced: booleanforceReduced = false; // osPrefersReduced OR forceReduced. Recomputed at the top of every update(). Demo.reducedNow: booleanreducedNow = false; // Proof that onReducedMotionChange() really fires. Starting at zero is correct: the // engine only reports changes, so nothing is delivered until the setting actually // moves. Demo.hookCallCount: numberhookCallCount = 0; /** @type {boolean | null} */ Demo.lastHookValue: boolean | null
@type{boolean | null}
lastHookValue
= null;
/** @type {number | null} */ Demo.lastHookTick: number | null
@type{number | null}
lastHookTick
= null;
// Shake state. shakeAmount is the fading strength; shakeX/shakeY is the whole-number // pixel offset actually handed to the camera this tick. Demo.shakeAmount: numbershakeAmount = 0; Demo.shakeX: numbershakeX = 0; Demo.shakeY: numbershakeY = 0; // Counts 0..LAMP_PERIOD_TICKS-1 forever; the lamp is lit for the first half. Demo.lampTick: numberlampTick = 0; // Reused objects. Creating a new Vector2i per star per frame would make hundreds of // throwaway objects a second, which is exactly what a retro engine cannot afford. Demo.cameraVec: Vector2icameraVec = new new Vector2i(x?: number, y?: number): Vector2i
Creates an integer 2D vector, truncating inputs toward zero.
@paramx - Horizontal component (defaults to 0).@paramy - Vertical component (defaults to 0).
Vector2i
(0, 0);
Demo.headVec: Vector2iheadVec = new new Vector2i(x?: number, y?: number): Vector2i
Creates an integer 2D vector, truncating inputs toward zero.
@paramx - Horizontal component (defaults to 0).@paramy - Vertical component (defaults to 0).
Vector2i
(0, 0);
Demo.tailVec: Vector2itailVec = new new Vector2i(x?: number, y?: number): Vector2i
Creates an integer 2D vector, truncating inputs toward zero.
@paramx - Horizontal component (defaults to 0).@paramy - Vertical component (defaults to 0).
Vector2i
(0, 0);
Demo.tempRect: Rect2itempRect = new new Rect2i(x?: number, y?: number, width?: number, height?: number): Rect2i
Creates an integer rectangle, truncating all inputs toward zero.
@paramx - Left-edge X coordinate (defaults to 0).@paramy - Top-edge Y coordinate (defaults to 0).@paramwidth - Width in pixels (defaults to 0).@paramheight - Height in pixels (defaults to 0).
Rect2i
(0, 0, 0, 0);
// Colors the engine overlay (toggled with the backquote key) from the shared UI // theme. Its default colors live in palette slots 1 and 2, which here are empty and // deep space - black bars with black text.
Demo.configure(): {
    overlayStyle: any;
}
configure
() {
return { overlayStyle: anyoverlayStyle: import THEME_OVERLAY_STYLETHEME_OVERLAY_STYLE, }; } // Builds the palette, fills the sky, and asks once whether this viewer wants less // motion. async Demo.init(): unknowninit() { this.Demo.palette: Palette | null
@type{Palette | null}
palette
=
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.paletteCreate: (size?: number) => Palette
Creates a standalone palette instance.
@since1.0.3@paramsize - Palette size. Defaults to 256 colors.@returnsNew mutable palette.
paletteCreate
(256);
this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const C_BG: 2C_BG, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32
Creates a clamped 8-bit RGBA color.
@paramr - Red channel (0-255, defaults to 255).@paramg - Green channel (0-255, defaults to 255).@paramb - Blue channel (0-255, defaults to 255).@parama - Alpha channel (0-255, defaults to 255 = opaque).
Color32
(3, 5, 14));
this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const C_STAR_FAR: 3C_STAR_FAR, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32
Creates a clamped 8-bit RGBA color.
@paramr - Red channel (0-255, defaults to 255).@paramg - Green channel (0-255, defaults to 255).@paramb - Blue channel (0-255, defaults to 255).@parama - Alpha channel (0-255, defaults to 255 = opaque).
Color32
(96, 104, 124));
this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const C_STAR_MID: 4C_STAR_MID, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32
Creates a clamped 8-bit RGBA color.
@paramr - Red channel (0-255, defaults to 255).@paramg - Green channel (0-255, defaults to 255).@paramb - Blue channel (0-255, defaults to 255).@parama - Alpha channel (0-255, defaults to 255 = opaque).
Color32
(168, 180, 200));
this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const C_STAR_NEAR: 5C_STAR_NEAR, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32
Creates a clamped 8-bit RGBA color.
@paramr - Red channel (0-255, defaults to 255).@paramg - Green channel (0-255, defaults to 255).@paramb - Blue channel (0-255, defaults to 255).@parama - Alpha channel (0-255, defaults to 255 = opaque).
Color32
(236, 242, 255));
this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const C_FRAME: 6C_FRAME, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32
Creates a clamped 8-bit RGBA color.
@paramr - Red channel (0-255, defaults to 255).@paramg - Green channel (0-255, defaults to 255).@paramb - Blue channel (0-255, defaults to 255).@parama - Alpha channel (0-255, defaults to 255 = opaque).
Color32
(92, 104, 132));
this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const C_FRAME_DARK: 7C_FRAME_DARK, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32
Creates a clamped 8-bit RGBA color.
@paramr - Red channel (0-255, defaults to 255).@paramg - Green channel (0-255, defaults to 255).@paramb - Blue channel (0-255, defaults to 255).@parama - Alpha channel (0-255, defaults to 255 = opaque).
Color32
(38, 46, 64));
this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const C_HULL: 8C_HULL, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32
Creates a clamped 8-bit RGBA color.
@paramr - Red channel (0-255, defaults to 255).@paramg - Green channel (0-255, defaults to 255).@paramb - Blue channel (0-255, defaults to 255).@parama - Alpha channel (0-255, defaults to 255 = opaque).
Color32
(52, 60, 80));
this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const C_LAMP_ON: 9C_LAMP_ON, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32
Creates a clamped 8-bit RGBA color.
@paramr - Red channel (0-255, defaults to 255).@paramg - Green channel (0-255, defaults to 255).@paramb - Blue channel (0-255, defaults to 255).@parama - Alpha channel (0-255, defaults to 255 = opaque).
Color32
(255, 176, 58));
this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const C_LAMP_DIM: 10C_LAMP_DIM, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32
Creates a clamped 8-bit RGBA color.
@paramr - Red channel (0-255, defaults to 255).@paramg - Green channel (0-255, defaults to 255).@paramb - Blue channel (0-255, defaults to 255).@parama - Alpha channel (0-255, defaults to 255 = opaque).
Color32
(74, 44, 16));
this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const C_LAMP_RIM: 11C_LAMP_RIM, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32
Creates a clamped 8-bit RGBA color.
@paramr - Red channel (0-255, defaults to 255).@paramg - Green channel (0-255, defaults to 255).@paramb - Blue channel (0-255, defaults to 255).@parama - Alpha channel (0-255, defaults to 255 = opaque).
Color32
(126, 102, 70));
// Fill the sky. Each star gets its own starting depth spread across the whole // range, so the very first frame already looks like a tunnel rather than a wall // of stars all arriving together. this.Demo.stars: {}
@type{Star[]}
stars
= [];
for (let let i: numberi = 0; let i: numberi < const STAR_COUNT: 140STAR_COUNT; let i: numberi++) { this.Demo.stars: {}
@type{Star[]}
stars
.push(this.
Demo.createStar(z: any): {
    x: number;
    y: number;
    z: any;
    prevZ: any;
    speedScale: number;
}
createStar
(
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.random: Random
Default engine PRNG (live reference - not a copy). Time-seeded when the engine singleton is created. Call {@link BT.randomSeed } for a reproducible run, or open the page with `?seed=N` - applied in `BT.init()` just before the demo's own `init()`, so a `randomSeed` call there still wins. Mutating the instance (for example `BT.random.int(10)`) advances the shared stream.
@since1.5.0@changed1.7.1 `?seed=N` URL parameter seeds the shared generator before the demo's `init()`.@returnsThe shared {@link Random} instance.@exampleBT.randomSeed(42); BT.random.int(150, 420); BT.random.pick(['a', 'b', 'c']);
random
.Random.float(min: number, max: number): number
Returns the next pseudo-random float in [min, max).
@parammin - Inclusive lower bound.@parammax - Exclusive upper bound.@returnsFloat in [min, max).@since1.5.0
float
(const Z_NEAR: 0.08Z_NEAR, const Z_FAR: 1.6Z_FAR)));
} // Install the shared UI kit colors in slots 240-251, then activate the palette. // applyTheme() must run BEFORE paletteSet(), or the kit would draw with whatever // happened to be in those slots. this.Demo.theme: nulltheme = import applyThemeapplyTheme(this.Demo.palette: Palette
@type{Palette | null}
palette
);
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.paletteSet: (palette: Palette) => void
Stores the active engine palette. Use this to swap the **entire palette** (e.g. switch between a day and night theme). After this call the renderer uploads the new palette uniform on the next frame. **Palette-value swap (change what a slot looks like):** mutate the live {@link BT.palette } in place with `palette.set(slot, newColor)`. The renderer uploads dirty slots on the next frame; no `paletteSet()` or {@link BT.spritesRefresh } needed. **Palette-layout swap (same colors, different slot positions):** build a new palette with the same colors at new indices, call `paletteSet()`, then call {@link BT.spritesRefresh } so every sprite sheet re-maps its original RGBA pixels against the new slot layout.
@since1.0.3@parampalette - Palette to make active.
paletteSet
(this.Demo.palette: Palette
@type{Palette | null}
palette
);
// Ask the browser once. From here on, onReducedMotionChange() keeps this current // - the engine installs its media-query listener right after this init() resolves // true. this.Demo.osPrefersReduced: booleanosPrefersReduced =
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.isReducedMotionPreferred: boolean
Whether reduced motion is currently preferred. Resolves `window.matchMedia('(prefers-reduced-motion: reduce)')`, or the `?reducedmotion` / `?noreducedmotion` URL flags when either is present. Pair with {@link IBTDemo.onReducedMotionChange } to react when the preference changes at runtime - it is not fixed for the session, the same way {@link BT.screenOrientation } is not.
@since1.7.0@returns`true` when reduced motion should be preferred.
isReducedMotionPreferred
;
this.Demo.reducedNow: booleanreducedNow = this.Demo.osPrefersReduced: booleanosPrefersReduced; this.Demo.enterPhase(phase: any): voidenterPhase(const PHASE_CRUISE: "cruise"PHASE_CRUISE); return true; } // Fixed-step logic: advance the flight, move every star, fade the shake, blink the // lamp. Demo.update(): voidupdate() { // First line, always: this is what lets the kit see key presses and taps. import uiui.tick(); // The OS setting and the local override are combined here, once, and every piece // of motion below reads the result. One switch, one place to change it. this.Demo.reducedNow: booleanreducedNow = this.Demo.osPrefersReduced: booleanosPrefersReduced || this.Demo.forceReduced: booleanforceReduced; this.Demo.advancePhase(): voidadvancePhase(); this.Demo.moveStars(): voidmoveStars(); this.Demo.updateShake(): voidupdateShake(); // % wraps the counter back to 0 when it reaches the period, like a clock hand. this.Demo.lampTick: numberlampTick = (this.Demo.lampTick: numberlampTick + 1) % const LAMP_PERIOD_TICKS: 30LAMP_PERIOD_TICKS; } // Draws the sky and hull under the shake, then the interface at rest on top. Demo.render(): voidrender() { // clear() paints the whole display and ignores the camera, so shaking the scene // can never expose an unpainted strip along an edge.
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.clear: (paletteIndex: number) => void
Sets the frame clear color using a palette index. The renderer uses this color when clearing the full display at the start of the next frame.
@since0.1.0@parampaletteIndex - Palette index for the full-screen clear pass.
clear
(const C_BG: 2C_BG);
// Screen shake done properly: nudge the camera, not every single draw call. A // camera offset of -3 moves everything drawn after it 3 pixels the other way, so // nothing else in this method has to know the shake exists.
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.cameraSet: (offset: Vector2i) => void
Sets the global camera offset applied to subsequent draw calls.
@since0.1.0@paramoffset - Camera translation in display pixels.
cameraSet
(this.Demo.cameraVec: Vector2icameraVec.Vector2i.set(x: number, y: number): Vector2i
Sets both components of this vector. Modifies this vector directly for maximum performance. WARNING: Mutates this vector. Don't use on frozen/cached singletons.
@paramx - New x component.@paramy - New y component.@returnsThis vector for chaining.
set
(-this.Demo.shakeX: numbershakeX, -this.Demo.shakeY: numbershakeY));
this.Demo.drawStars(): voiddrawStars(); this.Demo.drawHull(): voiddrawHull(); this.Demo.drawLamp(): voiddrawLamp(); // Put the camera back BEFORE any ui.* call. The kit draws its panels through the // same camera-aware calls, but it decides what a finger hit using raw screen // positions - so a shaking camera would slide the buttons away from their own tap // targets.
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.cameraReset: () => void
Resets the global camera offset to `(0, 0)`.
@since0.1.0@changed1.7.1 Now persists once nothing sets the camera again: previously, the offset the engine re-applies at frame start stayed at its pre-reset value forever, so a later frame that never calls `cameraSet` again still saw the old offset leak back in.
cameraReset
();
this.Demo.drawStatusPanel(): voiddrawStatusPanel(); this.Demo.drawControlPanel(): voiddrawControlPanel(); this.Demo.drawLegendPanel(): voiddrawLegendPanel(); } // The engine calls this when the system's reduce-motion setting changes while the // page is open. It fires only on a change - never once at startup with the current // value - which is why init() reads BT.isReducedMotionPreferred for the starting // answer. `prefersReduced` is true when the viewer now wants less motion. Demo.onReducedMotionChange(prefersReduced: any): voidonReducedMotionChange(prefersReduced: anyprefersReduced) { this.Demo.osPrefersReduced: booleanosPrefersReduced = prefersReduced: anyprefersReduced; // Everything below is only here so the demo can show that the hook really fired. // A real game would just keep the line above. this.Demo.hookCallCount: numberhookCallCount += 1; this.Demo.lastHookValue: boolean | null
@type{boolean | null}
lastHookValue
= prefersReduced: anyprefersReduced;
this.Demo.lastHookTick: number | null
@type{number | null}
lastHookTick
=
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.ticks: number
Current fixed-update tick counter. Increments once per engine update. Reset via {@link BT.ticksReset } .
@since1.0.4@returnsCurrent tick count since initialization or last reset.
ticks
;
} // Makes one star at depth `z`: a random direction out from the middle, a random // distance along it, and a slightly random speed.
Demo.createStar(z: any): {
    x: number;
    y: number;
    z: any;
    prevZ: any;
    speedScale: number;
}
createStar
(z: anyz) {
// BT.random.angle() picks a direction anywhere around a full turn. Cosine and // sine turn that direction plus a distance into a sideways and an up/down offset // - the standard way to scatter points evenly around a circle instead of in a // square. const const angle: numberangle =
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.random: Random
Default engine PRNG (live reference - not a copy). Time-seeded when the engine singleton is created. Call {@link BT.randomSeed } for a reproducible run, or open the page with `?seed=N` - applied in `BT.init()` just before the demo's own `init()`, so a `randomSeed` call there still wins. Mutating the instance (for example `BT.random.int(10)`) advances the shared stream.
@since1.5.0@changed1.7.1 `?seed=N` URL parameter seeds the shared generator before the demo's `init()`.@returnsThe shared {@link Random} instance.@exampleBT.randomSeed(42); BT.random.int(150, 420); BT.random.pick(['a', 'b', 'c']);
random
.Random.angle(): number
Returns a uniform angle in [0, 2π) radians.
@returnsAngle in radians.@since1.5.0
angle
();
const const radius: numberradius =
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.random: Random
Default engine PRNG (live reference - not a copy). Time-seeded when the engine singleton is created. Call {@link BT.randomSeed } for a reproducible run, or open the page with `?seed=N` - applied in `BT.init()` just before the demo's own `init()`, so a `randomSeed` call there still wins. Mutating the instance (for example `BT.random.int(10)`) advances the shared stream.
@since1.5.0@changed1.7.1 `?seed=N` URL parameter seeds the shared generator before the demo's `init()`.@returnsThe shared {@link Random} instance.@exampleBT.randomSeed(42); BT.random.int(150, 420); BT.random.pick(['a', 'b', 'c']);
random
.Random.float(min: number, max: number): number
Returns the next pseudo-random float in [min, max).
@parammin - Inclusive lower bound.@parammax - Exclusive upper bound.@returnsFloat in [min, max).@since1.5.0
float
(const SPAWN_RADIUS_MIN: 0.06SPAWN_RADIUS_MIN, const SPAWN_RADIUS_MAX: 0.9SPAWN_RADIUS_MAX);
return { x: numberx: Math.cos(const angle: numberangle) * const radius: numberradius, y: numbery: Math.sin(const angle: numberangle) * const radius: numberradius, z: anyz, prevZ: anyprevZ: z: anyz, speedScale: numberspeedScale:
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.random: Random
Default engine PRNG (live reference - not a copy). Time-seeded when the engine singleton is created. Call {@link BT.randomSeed } for a reproducible run, or open the page with `?seed=N` - applied in `BT.init()` just before the demo's own `init()`, so a `randomSeed` call there still wins. Mutating the instance (for example `BT.random.int(10)`) advances the shared stream.
@since1.5.0@changed1.7.1 `?seed=N` URL parameter seeds the shared generator before the demo's `init()`.@returnsThe shared {@link Random} instance.@exampleBT.randomSeed(42); BT.random.int(150, 420); BT.random.pick(['a', 'b', 'c']);
random
.Random.float(min: number, max: number): number
Returns the next pseudo-random float in [min, max).
@parammin - Inclusive lower bound.@parammax - Exclusive upper bound.@returnsFloat in [min, max).@since1.5.0
float
(const SPEED_SCALE_MIN: 0.85SPEED_SCALE_MIN, const SPEED_SCALE_MAX: 1.15SPEED_SCALE_MAX),
}; } // Sends one star back to the far end of the tunnel with a fresh direction. Demo.respawnStar(star: any): voidrespawnStar(star: anystar) { const const angle: numberangle =
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.random: Random
Default engine PRNG (live reference - not a copy). Time-seeded when the engine singleton is created. Call {@link BT.randomSeed } for a reproducible run, or open the page with `?seed=N` - applied in `BT.init()` just before the demo's own `init()`, so a `randomSeed` call there still wins. Mutating the instance (for example `BT.random.int(10)`) advances the shared stream.
@since1.5.0@changed1.7.1 `?seed=N` URL parameter seeds the shared generator before the demo's `init()`.@returnsThe shared {@link Random} instance.@exampleBT.randomSeed(42); BT.random.int(150, 420); BT.random.pick(['a', 'b', 'c']);
random
.Random.angle(): number
Returns a uniform angle in [0, 2π) radians.
@returnsAngle in radians.@since1.5.0
angle
();
const const radius: numberradius =
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.random: Random
Default engine PRNG (live reference - not a copy). Time-seeded when the engine singleton is created. Call {@link BT.randomSeed } for a reproducible run, or open the page with `?seed=N` - applied in `BT.init()` just before the demo's own `init()`, so a `randomSeed` call there still wins. Mutating the instance (for example `BT.random.int(10)`) advances the shared stream.
@since1.5.0@changed1.7.1 `?seed=N` URL parameter seeds the shared generator before the demo's `init()`.@returnsThe shared {@link Random} instance.@exampleBT.randomSeed(42); BT.random.int(150, 420); BT.random.pick(['a', 'b', 'c']);
random
.Random.float(min: number, max: number): number
Returns the next pseudo-random float in [min, max).
@parammin - Inclusive lower bound.@parammax - Exclusive upper bound.@returnsFloat in [min, max).@since1.5.0
float
(const SPAWN_RADIUS_MIN: 0.06SPAWN_RADIUS_MIN, const SPAWN_RADIUS_MAX: 0.9SPAWN_RADIUS_MAX);
star: anystar.x = Math.cos(const angle: numberangle) * const radius: numberradius; star: anystar.y = Math.sin(const angle: numberangle) * const radius: numberradius; star: anystar.z = const Z_FAR: 1.6Z_FAR; // Snap the remembered depth to the new one as well. Without this, the // interpolated drawing in drawStars() would blend from the star's old position // right next to the cockpit all the way back to the far distance, painting a // streak across the entire window on the frame it respawns. star: anystar.prevZ = const Z_FAR: 1.6Z_FAR; } // Walks the flight through cruise -> jump -> warp -> drop and back, and sets the // speed. Demo.advancePhase(): voidadvancePhase() { this.Demo.phaseTicks: numberphaseTicks += 1; if (this.Demo.phaseTicks: numberphaseTicks >=
const PHASE_TICKS: {
    cruise: number;
    jump: number;
    warp: number;
    drop: number;
}
PHASE_TICKS
[this.Demo.phase: stringphase]) {
if (this.Demo.phase: stringphase === const PHASE_CRUISE: "cruise"PHASE_CRUISE) { this.Demo.enterPhase(phase: any): voidenterPhase(const PHASE_JUMP: "jump"PHASE_JUMP); } else if (this.Demo.phase: stringphase === const PHASE_JUMP: "jump"PHASE_JUMP) { this.Demo.enterPhase(phase: any): voidenterPhase(const PHASE_WARP: "warp"PHASE_WARP); } else if (this.Demo.phase: stringphase === const PHASE_WARP: "warp"PHASE_WARP) { this.Demo.enterPhase(phase: any): voidenterPhase(const PHASE_DROP: "drop"PHASE_DROP); } else { this.Demo.enterPhase(phase: any): voidenterPhase(const PHASE_CRUISE: "cruise"PHASE_CRUISE); } } // Base speed for this phase. The two transitions slide between the two speeds // instead of snapping: t counts 0 to 1 across the phase, and applyEasing() bends // that straight count into a slow start, fast middle, slow finish. let let speed: numberspeed = this.Demo.phase: stringphase === const PHASE_WARP: "warp"PHASE_WARP ? const SPEED_WARP: 0.055SPEED_WARP : const SPEED_CRUISE: 0.004SPEED_CRUISE; if (this.Demo.phase: stringphase === const PHASE_JUMP: "jump"PHASE_JUMP || this.Demo.phase: stringphase === const PHASE_DROP: "drop"PHASE_DROP) { const const t: numbert = this.Demo.phaseTicks: numberphaseTicks /
const PHASE_TICKS: {
    cruise: number;
    jump: number;
    warp: number;
    drop: number;
}
PHASE_TICKS
[this.Demo.phase: "jump" | "drop"phase];
const const eased: numbereased = function applyEasing(t: number, easing: EasingFunction): number
Applies an easing curve to a normalized time value.
@since1.0.3@changed1.5.0 Added sine, cubic, quartic, quintic, expo, circ, back, elastic, and bounce families.@paramt - Normalized time in [0, 1]. Values outside this range are not clamped.@parameasing - Easing curve to apply.@returnsEased value. Guaranteed to return 0 for `t = 0` and 1 for `t = 1`.
applyEasing
(const t: numbert, 'cubic-in-out');
let speed: numberspeed = this.Demo.phase: "jump" | "drop"phase === const PHASE_JUMP: "jump"PHASE_JUMP ? const SPEED_CRUISE: 0.004SPEED_CRUISE + (const SPEED_WARP: 0.055SPEED_WARP - const SPEED_CRUISE: 0.004SPEED_CRUISE) * const eased: numbereased : const SPEED_WARP: 0.055SPEED_WARP + (const SPEED_CRUISE: 0.004SPEED_CRUISE - const SPEED_WARP: 0.055SPEED_WARP) * const eased: numbereased; } // The first degradation: reduced motion keeps the whole flight, at a fifth of the // speed. Slowed, not stopped - a frozen starfield looks like the demo has // crashed. this.Demo.warpSpeed: numberwarpSpeed = this.Demo.reducedNow: booleanreducedNow ? let speed: numberspeed * const REDUCED_SPEED_SCALE: 0.2REDUCED_SPEED_SCALE : let speed: numberspeed; } // Switches to `phase` (one of PHASE_CRUISE, PHASE_JUMP, PHASE_WARP, PHASE_DROP), and // kicks the hull on the two moments where that makes sense. Demo.enterPhase(phase: any): voidenterPhase(phase: anyphase) { this.Demo.phase: stringphase = phase: anyphase; this.Demo.phaseTicks: numberphaseTicks = 0; // Crossing into or out of warp is a jolt; cruising along and holding at warp are // not. if (phase: anyphase === const PHASE_JUMP: "jump"PHASE_JUMP || phase: anyphase === const PHASE_DROP: "drop"PHASE_DROP) { this.Demo.shakeAmount: numbershakeAmount = const SHAKE_START_PX: 5SHAKE_START_PX; } if (phase: anyphase === const PHASE_JUMP: "jump"PHASE_JUMP) { this.Demo.jumpCount: numberjumpCount += 1; } } // Pulls every star closer by this tick's speed, recycling the ones that have flown // past. Demo.moveStars(): voidmoveStars() { for (let let i: numberi = 0; let i: numberi < this.Demo.stars: {}
@type{Star[]}
stars
.length; let i: numberi++) {
const const star: anystar = this.Demo.stars: {}
@type{Star[]}
stars
[let i: numberi];
// Remember where the star was, so render() can draw the in-between position. const star: anystar.prevZ = const star: anystar.z; // Smaller z means closer. This is the only movement in the demo: while a star // is alive nothing changes its x or y. The sideways rush you see is entirely // the division by a shrinking depth over in drawStars(). const star: anystar.z -= this.Demo.warpSpeed: numberwarpSpeed * const star: anystar.speedScale; if (const star: anystar.z <= const Z_NEAR: 0.08Z_NEAR || this.Demo.isStarOffScreen(star: any): booleanisStarOffScreen(const star: anystar)) { this.Demo.respawnStar(star: any): voidrespawnStar(const star: anystar); } } } // Returns true when the star has flown out of the window. Checked so no star is ever // drawn - or worse, drawn as a very long line - far outside the screen. Demo.isStarOffScreen(star: any): booleanisStarOffScreen(star: anystar) { const const sx: numbersx = const CENTER_X: numberCENTER_X + (star: anystar.x / star: anystar.z) * const PROJECT_SCALE: 150PROJECT_SCALE; const const sy: numbersy = const CENTER_Y: numberCENTER_Y + (star: anystar.y / star: anystar.z) * const PROJECT_SCALE: 150PROJECT_SCALE; return ( const sx: numbersx < -const CULL_MARGIN_PX: 12CULL_MARGIN_PX || const sx: numbersx > const DISPLAY_W: 320DISPLAY_W + const CULL_MARGIN_PX: 12CULL_MARGIN_PX || const sy: numbersy < -const CULL_MARGIN_PX: 12CULL_MARGIN_PX || const sy: numbersy > const DISPLAY_H: 240DISPLAY_H + const CULL_MARGIN_PX: 12CULL_MARGIN_PX ); } // Fades the shake toward nothing, then turns it into whole pixels - or zero, if // damped. Demo.updateShake(): voidupdateShake() { // Multiplying by a number just under 1 every tick makes the kick die away quickly // at first and gently later, which is how a real jolt settles. Below about a // third of a pixel it is invisible, so snap it to nothing and stop the wobble // cleanly. this.Demo.shakeAmount: numbershakeAmount *= const SHAKE_DECAY: 0.86SHAKE_DECAY; if (this.Demo.shakeAmount: numbershakeAmount < const SHAKE_STOP_PX: 0.35SHAKE_STOP_PX) { this.Demo.shakeAmount: numbershakeAmount = 0; } // The second degradation: no shake at all. The jump still happens, and the panel // still counts it - only the camera stops moving. if (this.Demo.reducedNow: booleanreducedNow) { this.Demo.shakeX: numbershakeX = 0; this.Demo.shakeY: numbershakeY = 0; return; } this.Demo.shakeX: numbershakeX = Math.round(
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.random: Random
Default engine PRNG (live reference - not a copy). Time-seeded when the engine singleton is created. Call {@link BT.randomSeed } for a reproducible run, or open the page with `?seed=N` - applied in `BT.init()` just before the demo's own `init()`, so a `randomSeed` call there still wins. Mutating the instance (for example `BT.random.int(10)`) advances the shared stream.
@since1.5.0@changed1.7.1 `?seed=N` URL parameter seeds the shared generator before the demo's `init()`.@returnsThe shared {@link Random} instance.@exampleBT.randomSeed(42); BT.random.int(150, 420); BT.random.pick(['a', 'b', 'c']);
random
.Random.float(min: number, max: number): number
Returns the next pseudo-random float in [min, max).
@parammin - Inclusive lower bound.@parammax - Exclusive upper bound.@returnsFloat in [min, max).@since1.5.0
float
(-this.Demo.shakeAmount: numbershakeAmount, this.Demo.shakeAmount: numbershakeAmount));
this.Demo.shakeY: numbershakeY = Math.round(
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.random: Random
Default engine PRNG (live reference - not a copy). Time-seeded when the engine singleton is created. Call {@link BT.randomSeed } for a reproducible run, or open the page with `?seed=N` - applied in `BT.init()` just before the demo's own `init()`, so a `randomSeed` call there still wins. Mutating the instance (for example `BT.random.int(10)`) advances the shared stream.
@since1.5.0@changed1.7.1 `?seed=N` URL parameter seeds the shared generator before the demo's `init()`.@returnsThe shared {@link Random} instance.@exampleBT.randomSeed(42); BT.random.int(150, 420); BT.random.pick(['a', 'b', 'c']);
random
.Random.float(min: number, max: number): number
Returns the next pseudo-random float in [min, max).
@parammin - Inclusive lower bound.@parammax - Exclusive upper bound.@returnsFloat in [min, max).@since1.5.0
float
(-this.Demo.shakeAmount: numbershakeAmount, this.Demo.shakeAmount: numbershakeAmount));
} // Draws every star as a streak from where it is back toward where it just was. // // THE ONE IDEA IN THIS DEMO: dividing by depth. A star two units away is drawn half // as far from the middle as the same star one unit away. So as depth shrinks, the // drawn position races outward faster and faster all by itself. Think of standing // between railway tracks: sleepers far ahead barely seem to move, the one at your // feet whips past. Demo.drawStars(): voiddrawStars() { // BT.renderAlpha says how far this frame is between the last tick and the next // one, from 0 to just under 1. Blending the old and new depth by it keeps the // flight smooth even though the logic above only runs at a fixed 60 steps a // second. const const alpha: numberalpha =
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.renderAlpha: number
Fractional progress between the last completed fixed update and the next. Intended for interpolating render state between fixed-update steps.
@since1.3.0@returnsInterpolation alpha in `[0, 1)`.
renderAlpha
;
// The third degradation: reduced motion drops the trail length to zero, so every // star stays a dot at every speed. The long radial smear is the part that hurts. const const streakTicks: 2 | 0streakTicks = this.Demo.reducedNow: booleanreducedNow ? 0 : const STREAK_TICKS: 2STREAK_TICKS; for (let let i: numberi = 0; let i: numberi < this.Demo.stars: {}
@type{Star[]}
stars
.length; let i: numberi++) {
const const star: anystar = this.Demo.stars: {}
@type{Star[]}
stars
[let i: numberi];
const const z: anyz = const star: anystar.prevZ + (const star: anystar.z - const star: anystar.prevZ) * const alpha: numberalpha; // Guard against a depth of zero: dividing by it would give infinity. if (const z: anyz <= 0) { continue; } const const scale: numberscale = const PROJECT_SCALE: 150PROJECT_SCALE / const z: anyz; const const hx: numberhx = const CENTER_X: numberCENTER_X + const star: anystar.x * const scale: numberscale; const const hy: numberhy = const CENTER_Y: numberCENTER_Y + const star: anystar.y * const scale: numberscale; // Where the star was streakTicks ticks ago: the same star, further away. // Because the gap is this tick's speed times a fixed number of ticks, the // streak is short when the ship crawls and long when it is at warp. One // number, two looks. const const zTail: anyzTail = const z: anyz + this.Demo.warpSpeed: numberwarpSpeed * const star: anystar.speedScale * const streakTicks: 2 | 0streakTicks; const const tailScale: numbertailScale = const PROJECT_SCALE: 150PROJECT_SCALE / const zTail: anyzTail; const const tx: numbertx = const CENTER_X: numberCENTER_X + const star: anystar.x * const tailScale: numbertailScale; const const ty: numberty = const CENTER_Y: numberCENTER_Y + const star: anystar.y * const tailScale: numbertailScale; const const slot: 3 | 4 | 5slot = this.Demo.starSlot(z: any): 3 | 4 | 5starSlot(const z: anyz); // A "line" shorter than a pixel and a half is just a dot with extra work. if (Math.abs(const hx: numberhx - const tx: numbertx) < const STREAK_MIN_PX: 1.5STREAK_MIN_PX && Math.abs(const hy: numberhy - const ty: numberty) < const STREAK_MIN_PX: 1.5STREAK_MIN_PX) {
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.drawPixel: (posOrX: Vector2i | number, yOrColor: number, maybeColor?: number) => void
Draws a single pixel. Accepts either: - `(posOrX: Vector2i, yOrColor: number)` where `yOrColor` is the palette index. - `(posOrX: number, yOrColor: number, maybeColor: number)` for `(x, y, paletteIndex)`.
@since0.1.0@paramposOrX - Pixel position as `Vector2i`, or x coordinate when using numeric overload.@paramyOrColor - Palette index for vector overload, or y coordinate for numeric overload.@parammaybeColor - Palette index when using numeric overload.
drawPixel
(this.Demo.headVec: Vector2iheadVec.Vector2i.set(x: number, y: number): Vector2i
Sets both components of this vector. Modifies this vector directly for maximum performance. WARNING: Mutates this vector. Don't use on frozen/cached singletons.
@paramx - New x component.@paramy - New y component.@returnsThis vector for chaining.
set
(Math.round(const hx: numberhx), Math.round(const hy: numberhy)), const slot: 3 | 4 | 5slot);
} else {
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.drawLine: (p0: Vector2i, p1: Vector2i, paletteIndex: number) => void
Draws a pixel-perfect line between two points. Uses rasterized line drawing without antialiasing.
@since0.1.0@paramp0 - Start position in display coordinates.@paramp1 - End position in display coordinates.@parampaletteIndex - Palette color index.
drawLine
(
this.Demo.tailVec: Vector2itailVec.Vector2i.set(x: number, y: number): Vector2i
Sets both components of this vector. Modifies this vector directly for maximum performance. WARNING: Mutates this vector. Don't use on frozen/cached singletons.
@paramx - New x component.@paramy - New y component.@returnsThis vector for chaining.
set
(Math.round(const tx: numbertx), Math.round(const ty: numberty)),
this.Demo.headVec: Vector2iheadVec.Vector2i.set(x: number, y: number): Vector2i
Sets both components of this vector. Modifies this vector directly for maximum performance. WARNING: Mutates this vector. Don't use on frozen/cached singletons.
@paramx - New x component.@paramy - New y component.@returnsThis vector for chaining.
set
(Math.round(const hx: numberhx), Math.round(const hy: numberhy)),
const slot: 3 | 4 | 5slot, ); } } } // Picks a palette slot for a star from its blended depth `z`: distant stars are dim, // close ones glare. Demo.starSlot(z: any): 3 | 4 | 5starSlot(z: anyz) { if (z: anyz > const BAND_FAR_Z: 1BAND_FAR_Z) { return const C_STAR_FAR: 3C_STAR_FAR; } if (z: anyz > const BAND_MID_Z: 0.4BAND_MID_Z) { return const C_STAR_MID: 4C_STAR_MID; } return const C_STAR_NEAR: 5C_STAR_NEAR; } // The cockpit window frame and the instrument housing. This is not decoration: a // shake against a plain starfield is nearly invisible, because there is no straight // edge to measure it against. Give the eye a rigid frame and the same 5 pixels read // as a jolt. Demo.drawHull(): voiddrawHull() {
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.drawRect: (rect: Rect2i, paletteIndex: number) => void
Draws an unfilled rectangle outline.
@since0.1.0@paramrect - Rectangle bounds in display coordinates.@parampaletteIndex - Palette color index.
drawRect
(this.Demo.tempRect: Rect2itempRect.Rect2i.set(x: number, y: number, width: number, height: number): Rect2i
Sets all components of this rectangle. Modifies this rectangle directly for maximum performance. WARNING: Mutates this rectangle. Don't use on frozen/cached singletons.
@paramx - New x position.@paramy - New y position.@paramwidth - New width.@paramheight - New height.@returnsThis rectangle for chaining.
set
(2, 2, const DISPLAY_W: 320DISPLAY_W - 4, const DISPLAY_H: 240DISPLAY_H - 4), const C_FRAME: 6C_FRAME);
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.drawRect: (rect: Rect2i, paletteIndex: number) => void
Draws an unfilled rectangle outline.
@since0.1.0@paramrect - Rectangle bounds in display coordinates.@parampaletteIndex - Palette color index.
drawRect
(this.Demo.tempRect: Rect2itempRect.Rect2i.set(x: number, y: number, width: number, height: number): Rect2i
Sets all components of this rectangle. Modifies this rectangle directly for maximum performance. WARNING: Mutates this rectangle. Don't use on frozen/cached singletons.
@paramx - New x position.@paramy - New y position.@paramwidth - New width.@paramheight - New height.@returnsThis rectangle for chaining.
set
(4, 4, const DISPLAY_W: 320DISPLAY_W - 8, const DISPLAY_H: 240DISPLAY_H - 8), const C_FRAME_DARK: 7C_FRAME_DARK);
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.drawRectFill: (rect: Rect2i, paletteIndex: number) => void
Draws a filled rectangle.
@since0.1.0@paramrect - Rectangle bounds in display coordinates.@parampaletteIndex - Palette color index.
drawRectFill
(const HOUSING_RECT: Rect2iHOUSING_RECT, const C_HULL: 8C_HULL);
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.drawRect: (rect: Rect2i, paletteIndex: number) => void
Draws an unfilled rectangle outline.
@since0.1.0@paramrect - Rectangle bounds in display coordinates.@parampaletteIndex - Palette color index.
drawRect
(const HOUSING_RECT: Rect2iHOUSING_RECT, const C_FRAME: 6C_FRAME);
} // The warp lamp. Lit while the ship is jumping or at warp, dark while cruising. // // The fourth degradation: normally the lamp blinks twice a second, but under reduced // motion it simply stays lit for the same phases. Same color, same meaning, no // flicker - which is also why the lamp is never the ONLY thing telling you the state. // Blinking alone is a poor way to say anything: the phase readout under the lamp says // it in words. Demo.drawLamp(): voiddrawLamp() { const const isWarping: booleanisWarping = this.Demo.phase: stringphase === const PHASE_JUMP: "jump"PHASE_JUMP || this.Demo.phase: stringphase === const PHASE_WARP: "warp"PHASE_WARP; const const isLit: booleanisLit = const isWarping: booleanisWarping && (this.Demo.reducedNow: booleanreducedNow || this.Demo.lampTick: numberlampTick < const LAMP_ON_TICKS: 15LAMP_ON_TICKS);
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.drawRectFill: (rect: Rect2i, paletteIndex: number) => void
Draws a filled rectangle.
@since0.1.0@paramrect - Rectangle bounds in display coordinates.@parampaletteIndex - Palette color index.
drawRectFill
(const LAMP_RECT: Rect2iLAMP_RECT, const isLit: booleanisLit ? const C_LAMP_ON: 9C_LAMP_ON : const C_LAMP_DIM: 10C_LAMP_DIM);
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.drawRect: (rect: Rect2i, paletteIndex: number) => void
Draws an unfilled rectangle outline.
@since0.1.0@paramrect - Rectangle bounds in display coordinates.@parampaletteIndex - Palette color index.
drawRect
(const LAMP_RECT: Rect2iLAMP_RECT, const C_LAMP_RIM: 11C_LAMP_RIM);
// Drawn inside the camera offset with the rest of the hull, so the labels shake // along with the housing they are printed on instead of floating free of it.
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.systemPrint: (pos: Vector2i, paletteIndex: number, text: string) => void
Draws text using the built-in 6x14 system font. The system font covers printable ASCII (characters 32-126). For custom bitmap fonts with proportional glyphs, use {@link BT.printFont } instead.
@since1.0.3@parampos - Text origin in display coordinates.@parampaletteIndex - Palette color index for the text.@paramtext - String to render.
systemPrint
(this.Demo.headVec: Vector2iheadVec.Vector2i.set(x: number, y: number): Vector2i
Sets both components of this vector. Modifies this vector directly for maximum performance. WARNING: Mutates this vector. Don't use on frozen/cached singletons.
@paramx - New x component.@paramy - New y component.@returnsThis vector for chaining.
set
(const LAMP_LABEL_X: numberLAMP_LABEL_X, const LAMP_LABEL_Y: numberLAMP_LABEL_Y), const C_LAMP_RIM: 11C_LAMP_RIM, 'WARP');
// The phase in capitals on the left, and the jump count right-aligned under the // label: its letters end on READOUT_RIGHT_X. const const jumps: stringjumps = `x${this.Demo.jumpCount: numberjumpCount}`;
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.systemPrint: (pos: Vector2i, paletteIndex: number, text: string) => void
Draws text using the built-in 6x14 system font. The system font covers printable ASCII (characters 32-126). For custom bitmap fonts with proportional glyphs, use {@link BT.printFont } instead.
@since1.0.3@parampos - Text origin in display coordinates.@parampaletteIndex - Palette color index for the text.@paramtext - String to render.
systemPrint
(this.Demo.headVec: Vector2iheadVec.Vector2i.set(x: number, y: number): Vector2i
Sets both components of this vector. Modifies this vector directly for maximum performance. WARNING: Mutates this vector. Don't use on frozen/cached singletons.
@paramx - New x component.@paramy - New y component.@returnsThis vector for chaining.
set
(const READOUT_X: numberREADOUT_X, const READOUT_Y: numberREADOUT_Y), const C_LAMP_RIM: 11C_LAMP_RIM, this.Demo.phase: stringphase.toUpperCase());
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.systemPrint: (pos: Vector2i, paletteIndex: number, text: string) => void
Draws text using the built-in 6x14 system font. The system font covers printable ASCII (characters 32-126). For custom bitmap fonts with proportional glyphs, use {@link BT.printFont } instead.
@since1.0.3@parampos - Text origin in display coordinates.@parampaletteIndex - Palette color index for the text.@paramtext - String to render.
systemPrint
(
this.Demo.headVec: Vector2iheadVec.Vector2i.set(x: number, y: number): Vector2i
Sets both components of this vector. Modifies this vector directly for maximum performance. WARNING: Mutates this vector. Don't use on frozen/cached singletons.
@paramx - New x component.@paramy - New y component.@returnsThis vector for chaining.
set
(const READOUT_RIGHT_X: numberREADOUT_RIGHT_X - (const jumps: stringjumps.length * const FONT_W: 6FONT_W - 1) + 1, const READOUT_Y: numberREADOUT_Y),
const C_LAMP_RIM: 11C_LAMP_RIM, const jumps: stringjumps, ); } // Top-left panel: what the system said, what the override says, and proof the hook // fires. The phase and the jump count are on the instrument housing (see drawLamp()). Demo.drawStatusPanel(): voiddrawStatusPanel() { import uiui.begin('topLeft', { x: numberx: const MARGIN: 8MARGIN, y: numbery: const BAND_TOP: 46BAND_TOP, width: numberwidth: const LEFT_PANEL_W: 150LEFT_PANEL_W, kvCols: numberkvCols: const STATUS_KV_COLS: 11STATUS_KV_COLS }); import uiui.panel('Reduced motion'); import uiui.kv('OS pref', this.Demo.osPrefersReduced: booleanosPrefersReduced ? 'reduce' : 'no-pref'); import uiui.kv('Override', this.Demo.forceReduced: booleanforceReduced ? 'on' : 'off'); import uiui.kv('Effective', this.Demo.reducedNow: booleanreducedNow ? 'REDUCED' : 'FULL'); import uiui.spacer(4); // "0" here is the right answer at startup, not a bug: the hook reports changes // only. import uiui.kv('Hook fired', this.Demo.hookCallCount: numberhookCallCount); import uiui.kv('Last hook', this.Demo.formatLastHook(): stringformatLastHook()); import uiui.end(); } // Puts the most recent hook call into one short line. Demo.formatLastHook(): stringformatLastHook() { if (this.Demo.lastHookValue: boolean | null
@type{boolean | null}
lastHookValue
=== null) {
return 'never'; } return `${this.Demo.lastHookValue: boolean
@type{boolean | null}
lastHookValue
? 'reduce' : 'no-pref'} @${this.Demo.lastHookTick: number | null
@type{number | null}
lastHookTick
}`;
} // Left panel under the status: the two controls, both usable by key and by touch. Demo.drawControlPanel(): voiddrawControlPanel() { import uiui.begin('topLeft', { x: numberx: const MARGIN: 8MARGIN, y: numbery: const CONTROLS_Y: numberCONTROLS_Y, width: numberwidth: const LEFT_PANEL_W: 150LEFT_PANEL_W }); import uiui.panel('Controls'); // A checkbox, not a button, because this is a lasting on/off state. The { key } // option gives it a keyboard shortcut and a tap target at once, so the demo works // on a phone with no keyboard attached. // // Note the direction: this can only ADD reduced motion on top of the system // setting. Nothing here can take it away from someone whose OS asked for it. this.Demo.forceReduced: booleanforceReduced = import uiui.checkbox('M Force reduced', this.Demo.forceReduced: booleanforceReduced, { key: stringkey: 'KeyM' }); // A little breathing room: buttons are taller than text rows, and without a gap // the button's top edge clips the descenders of the checkbox label above it. import uiui.spacer(4); if (import uiui.button('J Jump / drop', { key: stringkey: 'KeyJ', width: numberwidth: const LEFT_PANEL_W: 150LEFT_PANEL_W - 20 })) { this.Demo.toggleWarp(): voidtoggleWarp(); } import uiui.label('Or use the OS setting.', { color: stringcolor: 'dim' }); import uiui.end(); } // Bottom-right panel: exactly what changes when reduced motion is on. Demo.drawLegendPanel(): voiddrawLegendPanel() { import uiui.begin('topLeft', { x: numberx: const LEGEND_X: numberLEGEND_X, y: numbery: const LEGEND_Y: numberLEGEND_Y, width: numberwidth: const LEGEND_W: 120LEGEND_W }); import uiui.panel('When reduced'); import uiui.label('Stars 20% speed', { color: stringcolor: 'dim' }); import uiui.label('Streaks dots only', { color: stringcolor: 'dim' }); import uiui.label('Shake none', { color: stringcolor: 'dim' }); import uiui.label('Lamp steady', { color: stringcolor: 'dim' }); import uiui.end(); } // Jumps to warp now, or drops out now, so nobody has to wait for the cycle to come // round. Mid-transition (jump or drop) the button does nothing: the ship is already // busy changing speed, and interrupting it would snap the starfield. Demo.toggleWarp(): voidtoggleWarp() { if (this.Demo.phase: stringphase === const PHASE_CRUISE: "cruise"PHASE_CRUISE) { this.Demo.enterPhase(phase: any): voidenterPhase(const PHASE_JUMP: "jump"PHASE_JUMP); } else if (this.Demo.phase: stringphase === const PHASE_WARP: "warp"PHASE_WARP) { this.Demo.enterPhase(phase: any): voidenterPhase(const PHASE_DROP: "drop"PHASE_DROP); } } } // bootstrap finds the canvas, builds the Demo, and runs the loop. function bootstrap(DemoClass: DemoConstructor, options?: BootstrapOptions): Promise<boolean>
One-liner bootstrap function for BLIT386 demos. Handles canvas retrieval and engine initialization. Backend selection (WebGPU or software fallback) is managed internally by BTAPI. This function provides a streamlined way to start a demo with sensible defaults while allowing customization through options.
@since0.2.0@changed1.4.0 Calling `bootstrap()` again while already initialized now routes to a hot swap (via {@link registerHotReload}) when a Vite HMR context is registered, or logs a double-bootstrap guard and returns `false` otherwise - previously it silently started a second, unstoppable `GameLoop`.@changed1.7.0 Exposes `BT` on `window.BT` after bootstrap finishes, gated by {@link BootstrapOptions.exposeGlobal} (default: {@link BT.isDevMode}).@paramDemoClass - Demo class constructor implementing `IBTDemo` (optional `configure()` for hardware settings).@paramoptions - Optional configuration for IDs and callbacks.@returns`true` when the demo boots successfully; otherwise `false`.@example// Simplest usage - uses default IDs. bootstrap(MyDemo);@example// With custom options. bootstrap(MyDemo, { canvasID: 'custom-canvas', containerID: 'custom-container', onSuccess: () => console.log('Demo started!'), onError: (err) => analytics.trackError(err), });@example// Await the result. const success = await bootstrap(MyDemo); if (success) { console.log('Demo is running'); }
bootstrap
(class DemoDemo);