// Starfield: parallax scrolling stars that feel like 3D depth.
// @description Parallax scrolling stars at several speeds, so a flat two-dimensional field reads as real depth.
//
// Part of the BLIT386 demo series.
//
// Prerequisites:
//   Basics     https://demos.blit386.dev/basics
//   Primitives https://demos.blit386.dev/primitives
//   Colors     https://demos.blit386.dev/colors
//
// Guide: https://blit386.dev/docs/api/rendering#primitives
//
// WHAT YOU WILL SEE
// Three layers of stars scroll to the left at different speeds. Stars that are
// "far away" move slowly and look dim and tiny. Stars that are "close" move fast
// and look bright and a little bigger. Your brain reads that mix as depth, even
// though the screen is flat. Two everyday comparisons:
//   - Car window: nearby trees zip past, but faraway mountains barely move.
//   - Train window: the fence right beside the tracks blurs by, houses farther
//     back drift slowly, and distant hills almost look still.
//
// WHAT YOU WILL LEARN
//   - Arrays of simple objects (each star remembers x, y, speed, and a palette slot)
//   - Parallax: fake depth by changing speed, size, and brightness together
//   - Wrapping: when a star leaves the left side, jump it to the right (endless sky)
//   - A shooting star: a fast diagonal line drawn with BT.drawLine
//
// HOW COLORS WORK IN THIS DEMO
// Every star has a unique brightness (how bright its gray color is). Instead of
// making a new Color32 every frame, we register each star's gray color in the
// palette once at startup and store the palette slot number on the star.
// render() just reads that slot number - no Color32 objects needed per frame.
//
// The three explainer lines in the corner are drawn with the shared UI kit
// (src/shared/ui.js), so their colors and spacing match every other demo.
//
// The engine splits work the usual way: update() moves things; render() only draws.
// See the Basics demo for the full story: https://demos.blit386.dev/basics

import { 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;
    ... 106 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 uiui, import UI_ANCHORSUI_ANCHORS } from './shared/ui.js'; /** @typedef {import('blit386').IBTDemo} IBTDemo */ /** @typedef {import('blit386').HardwareSettings} HardwareSettings */ /** @typedef {import('blit386').Palette} Palette */ // Logical screen size in "game pixels". const const DISPLAY_W: 320DISPLAY_W = 320; const const DISPLAY_H: 240DISPLAY_H = 240; // How many stars live in each layer. Far = many tiny dots; near = fewer, brighter blobs. const const FAR_COUNT: 30FAR_COUNT = 30; const const MEDIUM_COUNT: 20MEDIUM_COUNT = 20; const const NEAR_COUNT: 10NEAR_COUNT = 10; // Near stars are drawn as a small solid square this many pixels wide and tall. const const NEAR_STAR_SIZE: 2NEAR_STAR_SIZE = 2; // Where in the palette we start registering individual star colors. // Each star gets its own slot so brightness variety is preserved exactly. // We have 30 + 20 + 10 = 60 stars, using slots 10..69. const const SLOT_START: 10SLOT_START = 10; // Static color slots. const const C_BG: 2C_BG = 2; // Deep space background (very dark blue-black). const const C_CHART_TEXT: 3C_CHART_TEXT = 3; // Light blue-white: overlay text and timing chart update bars. const const C_CHART_WARN: 4C_CHART_WARN = 4; // Dim blue-gray: overlay timing chart warning color. const const C_CHART_TAG: 5C_CHART_TAG = 5; // Even dimmer: overlay timing chart tag labels. const const C_STREAK: 7C_STREAK = 7; // Cool white for the shooting star streak. /** * Parallax starfield with three layers plus an occasional shooting star. * * @implements {IBTDemo} */ class class Demo
Parallax starfield with three layers plus an occasional shooting star.
@implementsIBTDemo
Demo
{
// The palette holds all colors used in this demo. /** @type {Palette | null} */ Demo.palette: Palette | null
@type{Palette | null}
palette
= null;
// Slot map for the shared UI kit theme, filled in init() by applyTheme(). // The kit's label widgets draw with these colors automatically. Demo.theme: nulltheme = null; // Three separate arrays. Each entry is a plain object: // { x, y, speed, paletteIndex } // paletteIndex is the slot number registered during init(). Demo.farLayer: {}farLayer = []; Demo.mediumLayer: {}mediumLayer = []; Demo.nearLayer: {}nearLayer = []; // Shooting star: not an array - only one at a time, or none. // When active is false, we ignore the numbers until we spawn again. // prevHeadX/prevHeadY remember the head position at the START of the most recent // update() tick, before this tick's movement. render() blends between them and // headX/headY using BT.renderAlpha so the streak glides smoothly between physics // ticks instead of hopping in 14px jumps - see "Interpolating render state with // renderAlpha" in the engine's docs/api-game-loop.md.
Demo.streak: {
    active: boolean;
    headX: number;
    headY: number;
    prevHeadX: number;
    prevHeadY: number;
}
streak
= {
active: booleanactive: false, headX: numberheadX: 0, headY: numberheadY: 0, prevHeadX: numberprevHeadX: 0, prevHeadY: numberprevHeadY: 0, }; // Counts how many update() ticks passed since the last shooting star (for timing). Demo.ticksSinceShoot: numberticksSinceShoot = 0; // After a shooting star finishes, wait this many ticks before planning the next one. Demo.nextShootDelay: numbernextShootDelay = 200; /** * Shows the timing chart while many stars move each frame (useful for spotting render spikes). * * @returns {Partial<HardwareSettings>} */ Demo.configure(): Partial<HardwareSettings>
Shows the timing chart while many stars move each frame (useful for spotting render spikes).
@returns
configure
() {
return { isOverlayTimingChartEnabled: booleanisOverlayTimingChartEnabled: true,
overlayStyle: {
    barPaletteIndex: number;
    textPaletteIndex: number;
    gapPaletteIndex: number;
}
overlayStyle
: {
barPaletteIndex: numberbarPaletteIndex: const C_BG: 2C_BG, textPaletteIndex: numbertextPaletteIndex: const C_CHART_TEXT: 3C_CHART_TEXT, gapPaletteIndex: numbergapPaletteIndex: const C_BG: 2C_BG, },
overlayTimingChartStyle: {
    updateBarPaletteIndex: number;
    renderBarPaletteIndex: number;
    warningPaletteIndex: number;
    errorPaletteIndex: number;
    tagPaletteIndex: number;
}
overlayTimingChartStyle
: {
updateBarPaletteIndex: numberupdateBarPaletteIndex: const C_CHART_TEXT: 3C_CHART_TEXT, renderBarPaletteIndex: numberrenderBarPaletteIndex: const C_STREAK: 7C_STREAK, warningPaletteIndex: numberwarningPaletteIndex: const C_CHART_WARN: 4C_CHART_WARN, errorPaletteIndex: numbererrorPaletteIndex: const C_STREAK: 7C_STREAK, tagPaletteIndex: numbertagPaletteIndex: const C_CHART_TAG: 5C_CHART_TAG, }, }; } /** * Sets up the palette and creates star layers. * * IMPORTANT ORDER: * 1. Create palette and register static colors. * 2. Build the star layers (this decides each star's brightness). * 3. Register each star's gray color in the palette, store the slot on the star. * 4. BT.paletteSet() - tell the engine to use this palette. * * @returns {Promise<boolean>} */ async Demo.init(): Promise<boolean>
Sets up the palette and creates star layers. IMPORTANT ORDER: 1. Create palette and register static colors. 2. Build the star layers (this decides each star's brightness). 3. Register each star's gray color in the palette, store the slot on the star. 4. BT.paletteSet() - tell the engine to use this palette.
@returns
init
() {
console.log('[StarfieldDemo] Initializing...'); // Step 1: Create palette and static colors 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;
    ... 106 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
(4, 6, 18)); // Deep space: very dark blue-black.
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_CHART_TEXT: 3C_CHART_TEXT, 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
(200, 210, 230)); // Overlay text and chart update bars.
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_CHART_WARN: 4C_CHART_WARN, 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
(140, 150, 170)); // Dim blue-gray chart warnings.
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_CHART_TAG: 5C_CHART_TAG, 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
(110, 120, 140)); // Dimmer chart tag labels.
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_STREAK: 7C_STREAK, 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
(230, 240, 255)); // Cool white shooting streak.
// Step 2: Build the three star layers // Each helper picks random positions, speeds, and brightness values in the given ranges. // Far stars: slow (0.3..0.5 pixels/tick), dim (80..120 brightness). // Medium stars: medium (0.8..1.2), brighter (150..200). // Near stars: fast (1.5..2.5), bright (220..255). this.Demo.farLayer: {}farLayer = this.
Demo.createLayerData(count: number, speedMin: number, speedMax: number, brightMin: number, brightMax: number): Array<{
    x: number;
    y: number;
    prevX: number;
    prevY: number;
    speed: number;
    brightness: number;
    paletteIndex: number;
}>
Build one layer's raw data: count stars with random positions and brightness. Returns objects with { x, y, speed, brightness } - paletteIndex is added later in init() once the palette is ready. BT.random is the engine's shared random number generator. float() returns a decimal from the first value up to (but not including) the second.
@paramcount@paramspeedMin@paramspeedMax@parambrightMin@parambrightMax@returns
createLayerData
(const FAR_COUNT: 30FAR_COUNT, 0.3, 0.5, 80, 120);
this.Demo.mediumLayer: {}mediumLayer = this.
Demo.createLayerData(count: number, speedMin: number, speedMax: number, brightMin: number, brightMax: number): Array<{
    x: number;
    y: number;
    prevX: number;
    prevY: number;
    speed: number;
    brightness: number;
    paletteIndex: number;
}>
Build one layer's raw data: count stars with random positions and brightness. Returns objects with { x, y, speed, brightness } - paletteIndex is added later in init() once the palette is ready. BT.random is the engine's shared random number generator. float() returns a decimal from the first value up to (but not including) the second.
@paramcount@paramspeedMin@paramspeedMax@parambrightMin@parambrightMax@returns
createLayerData
(const MEDIUM_COUNT: 20MEDIUM_COUNT, 0.8, 1.2, 150, 200);
this.Demo.nearLayer: {}nearLayer = this.
Demo.createLayerData(count: number, speedMin: number, speedMax: number, brightMin: number, brightMax: number): Array<{
    x: number;
    y: number;
    prevX: number;
    prevY: number;
    speed: number;
    brightness: number;
    paletteIndex: number;
}>
Build one layer's raw data: count stars with random positions and brightness. Returns objects with { x, y, speed, brightness } - paletteIndex is added later in init() once the palette is ready. BT.random is the engine's shared random number generator. float() returns a decimal from the first value up to (but not including) the second.
@paramcount@paramspeedMin@paramspeedMax@parambrightMin@parambrightMax@returns
createLayerData
(const NEAR_COUNT: 10NEAR_COUNT, 1.5, 2.5, 220, 255);
// Step 3: Register each star's color in the palette // We walk all three layers in one pass, giving each star its own slot number. // new Color32(b, b, b) makes a neutral gray: equal red, green, and blue. let let slot: numberslot = const SLOT_START: 10SLOT_START; for (const const star: anystar of this.Demo.farLayer: {}farLayer) { const const b: anyb = const star: anystar.brightness; 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
(let slot: numberslot, 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
(const b: anyb, const b: anyb, const b: anyb));
const star: anystar.paletteIndex = let slot: numberslot; let slot: numberslot++; } for (const const star: anystar of this.Demo.mediumLayer: {}mediumLayer) { const const b: anyb = const star: anystar.brightness; 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
(let slot: numberslot, 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
(const b: anyb, const b: anyb, const b: anyb));
const star: anystar.paletteIndex = let slot: numberslot; let slot: numberslot++; } for (const const star: anystar of this.Demo.nearLayer: {}nearLayer) { const const b: anyb = const star: anystar.brightness; 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
(let slot: numberslot, 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
(const b: anyb, const b: anyb, const b: anyb));
const star: anystar.paletteIndex = let slot: numberslot; let slot: numberslot++; } // Install the shared UI kit theme. applyTheme() writes twelve UI colors into // high palette slots (240 and up), far above this demo's scene slots (1..69), // so the star colors and the UI colors never fight over the same slots. this.Demo.theme: nulltheme = import applyThemeapplyTheme(this.Demo.palette: Palette
@type{Palette | null}
palette
);
// Activate the 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;
    ... 106 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
);
// Start the shooting-star timer at a pleasant "about 200 ticks" delay. this.Demo.ticksSinceShoot: numberticksSinceShoot = 0; this.Demo.nextShootDelay: numbernextShootDelay =
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;
    ... 106 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. Mutating the instance (for example `BT.random.int(10)`) advances the shared stream.
@since1.5.0@returnsThe shared {@link Random} instance.@exampleBT.randomSeed(42); BT.random.int(150, 420); BT.random.pick(['a', 'b', 'c']);
random
.Random.int(minOrMaxExclusive: number, maxExclusive?: number): number
Returns a pseudo-random integer in [0, maxExclusive) or [min, maxExclusive).
@paramminOrMaxExclusive - When alone, exclusive upper bound from 0; otherwise inclusive min.@parammaxExclusive - Exclusive upper bound when two arguments are passed.@returnsWhole number in the half-open range.@since1.5.0
int
(180, 220);
console.log('[StarfieldDemo] Ready.'); return true; } /** * Fixed-step game logic: slide every star left, wrap off-screen ones, * and maybe spawn or advance the shooting star. */ Demo.update(): void
Fixed-step game logic: slide every star left, wrap off-screen ones, and maybe spawn or advance the shooting star.
update
() {
// Move the three layers. wrapW is how wide the star is for "gone off the left?" checks. this.
Demo.moveLayer(layer: Array<{
    x: number;
    y: number;
    speed: number;
    brightness: number;
    paletteIndex: number;
}>, wrapW: number): void
Move every star in one layer to the left by its speed, then wrap if it exited left.
@paramlayer@paramwrapW how many pixels wide the drawable star occupies (for wrapping)
moveLayer
(this.Demo.farLayer: {}farLayer, 1);
this.
Demo.moveLayer(layer: Array<{
    x: number;
    y: number;
    speed: number;
    brightness: number;
    paletteIndex: number;
}>, wrapW: number): void
Move every star in one layer to the left by its speed, then wrap if it exited left.
@paramlayer@paramwrapW how many pixels wide the drawable star occupies (for wrapping)
moveLayer
(this.Demo.mediumLayer: {}mediumLayer, 1);
this.
Demo.moveLayer(layer: Array<{
    x: number;
    y: number;
    speed: number;
    brightness: number;
    paletteIndex: number;
}>, wrapW: number): void
Move every star in one layer to the left by its speed, then wrap if it exited left.
@paramlayer@paramwrapW how many pixels wide the drawable star occupies (for wrapping)
moveLayer
(this.Demo.nearLayer: {}nearLayer, const NEAR_STAR_SIZE: 2NEAR_STAR_SIZE);
this.Demo.updateStreak(): void
Maybe start a new streak, or move the current one until it leaves the screen.
updateStreak
();
} /** * Draw sky, stars (back to front), shooting streak, then text labels on top. * Notice: NO Color32 objects appear here. Every draw call uses a palette index number. */ Demo.render(): void
Draw sky, stars (back to front), shooting streak, then text labels on top. Notice: NO Color32 objects appear here. Every draw call uses a palette index number.
render
() {
// Deep space background.
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;
    ... 106 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);
// Draw back to front so near stars visually cover far ones, like real depth. // Far and medium stars are single pixels (size 1); near stars are bigger blocks. this.
Demo.drawLayer(layer: Array<{
    x: number;
    y: number;
    prevX: number;
    prevY: number;
    paletteIndex: number;
}>, size: number): void
Draws one layer of stars. All three layers share the same movement math; only the drawn size differs, so one method handles them all. Each star's paletteIndex was set in init() to point at its unique gray shade.
@paramlayer@paramsize - Star width and height in pixels: 1 draws a single pixel, anything bigger draws a filled square (near stars use NEAR_STAR_SIZE).
drawLayer
(this.Demo.farLayer: {}farLayer, 1);
this.
Demo.drawLayer(layer: Array<{
    x: number;
    y: number;
    prevX: number;
    prevY: number;
    paletteIndex: number;
}>, size: number): void
Draws one layer of stars. All three layers share the same movement math; only the drawn size differs, so one method handles them all. Each star's paletteIndex was set in init() to point at its unique gray shade.
@paramlayer@paramsize - Star width and height in pixels: 1 draws a single pixel, anything bigger draws a filled square (near stars use NEAR_STAR_SIZE).
drawLayer
(this.Demo.mediumLayer: {}mediumLayer, 1);
this.
Demo.drawLayer(layer: Array<{
    x: number;
    y: number;
    prevX: number;
    prevY: number;
    paletteIndex: number;
}>, size: number): void
Draws one layer of stars. All three layers share the same movement math; only the drawn size differs, so one method handles them all. Each star's paletteIndex was set in init() to point at its unique gray shade.
@paramlayer@paramsize - Star width and height in pixels: 1 draws a single pixel, anything bigger draws a filled square (near stars use NEAR_STAR_SIZE).
drawLayer
(this.Demo.nearLayer: {}nearLayer, const NEAR_STAR_SIZE: 2NEAR_STAR_SIZE);
this.Demo.drawStreak(): void
Draw a short bright line: tail behind the head along the motion direction. Uses the static C_STREAK palette slot registered in init().
drawStreak
();
this.Demo.drawLabels(): void
Explain the three layers with the shared UI kit (drawn last so text stays readable). We skip ui.panel() on purpose: without it the group is just floating text, which keeps the sky visible behind the caption instead of covering it with a box.
drawLabels
();
} /** * Build one layer's raw data: count stars with random positions and brightness. * Returns objects with { x, y, speed, brightness } - paletteIndex is added later * in init() once the palette is ready. * * BT.random is the engine's shared random number generator. * float() returns a decimal from the first value up to (but not including) the second. * * @param {number} count * @param {number} speedMin * @param {number} speedMax * @param {number} brightMin * @param {number} brightMax * @returns {Array<{x: number, y: number, prevX: number, prevY: number, speed: number, brightness: number, paletteIndex: number}>} */
Demo.createLayerData(count: number, speedMin: number, speedMax: number, brightMin: number, brightMax: number): Array<{
    x: number;
    y: number;
    prevX: number;
    prevY: number;
    speed: number;
    brightness: number;
    paletteIndex: number;
}>
Build one layer's raw data: count stars with random positions and brightness. Returns objects with { x, y, speed, brightness } - paletteIndex is added later in init() once the palette is ready. BT.random is the engine's shared random number generator. float() returns a decimal from the first value up to (but not including) the second.
@paramcount@paramspeedMin@paramspeedMax@parambrightMin@parambrightMax@returns
createLayerData
(count: number
@paramcount
count
, speedMin: number
@paramspeedMin
speedMin
, speedMax: number
@paramspeedMax
speedMax
, brightMin: number
@parambrightMin
brightMin
, brightMax: number
@parambrightMax
brightMax
) {
const const layer: {}layer = []; for (let let i: numberi = 0; let i: numberi < count: number
@paramcount
count
; let i: numberi++) {
// Spread stars across the whole sky at the start so the screen looks full. const const x: numberx =
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;
    ... 106 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. Mutating the instance (for example `BT.random.int(10)`) advances the shared stream.
@since1.5.0@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
(0, const DISPLAY_W: 320DISPLAY_W);
const const y: numbery =
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;
    ... 106 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. Mutating the instance (for example `BT.random.int(10)`) advances the shared stream.
@since1.5.0@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
(0, const DISPLAY_H: 240DISPLAY_H);
// Speed: how many pixels left per update tick (float keeps motion smooth). const const speed: numberspeed =
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;
    ... 106 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. Mutating the instance (for example `BT.random.int(10)`) advances the shared stream.
@since1.5.0@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
(speedMin: number
@paramspeedMin
speedMin
, speedMax: number
@paramspeedMax
speedMax
);
// Brightness: 0 = black, 255 = white. Used to make a gray Color32. // intInclusive() gives a whole number and, unlike int(), the top value is a possible answer too // - so the brightest stars really can hit brightMax. const const brightness: numberbrightness =
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;
    ... 106 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. Mutating the instance (for example `BT.random.int(10)`) advances the shared stream.
@since1.5.0@returnsThe shared {@link Random} instance.@exampleBT.randomSeed(42); BT.random.int(150, 420); BT.random.pick(['a', 'b', 'c']);
random
.Random.intInclusive(min: number, max: number): number
Returns a pseudo-random integer in [min, max] (inclusive on both ends).
@parammin - Inclusive lower bound.@parammax - Inclusive upper bound.@returnsWhole number in the closed range.@since1.5.0
intInclusive
(brightMin: number
@parambrightMin
brightMin
, brightMax: number
@parambrightMax
brightMax
);
// paletteIndex starts at 0; init() will fill it in after palette setup. // prevX/prevY start equal to x/y - see moveLayer() for how they update. const layer: {}layer.push({ x: numberx, y: numbery, prevX: numberprevX: const x: numberx, prevY: numberprevY: const y: numbery, speed: numberspeed, brightness: numberbrightness, paletteIndex: numberpaletteIndex: 0 }); } return const layer: {}layer; } /** * Move every star in one layer to the left by its speed, then wrap if it exited left. * * @param {Array<{x: number, y: number, speed: number, brightness: number, paletteIndex: number}>} layer * @param {number} wrapW how many pixels wide the drawable star occupies (for wrapping) */
Demo.moveLayer(layer: Array<{
    x: number;
    y: number;
    speed: number;
    brightness: number;
    paletteIndex: number;
}>, wrapW: number): void
Move every star in one layer to the left by its speed, then wrap if it exited left.
@paramlayer@paramwrapW how many pixels wide the drawable star occupies (for wrapping)
moveLayer
(
layer: Array<{
    x: number;
    y: number;
    speed: number;
    brightness: number;
    paletteIndex: number;
}>
@paramlayer
layer
, wrapW: number
how many pixels wide the drawable star occupies (for wrapping)
@paramwrapW how many pixels wide the drawable star occupies (for wrapping)
wrapW
) {
for (let let i: numberi = 0; let i: numberi <
layer: Array<{
    x: number;
    y: number;
    speed: number;
    brightness: number;
    paletteIndex: number;
}>
@paramlayer
layer
.length; let i: numberi++) {
const
const star: Array<{
    x: number;
    y: number;
    speed: number;
    brightness: number;
    paletteIndex: number;
}>
star
=
layer: Array<{
    x: number;
    y: number;
    speed: number;
    brightness: number;
    paletteIndex: number;
}>
@paramlayer
layer
[let i: numberi];
// Remember where the star was before this tick moves it, so render() can // draw a smooth in-between position instead of a pop.
const star: Array<{
    x: number;
    y: number;
    speed: number;
    brightness: number;
    paletteIndex: number;
}>
star
.prevX =
const star: Array<{
    x: number;
    y: number;
    speed: number;
    brightness: number;
    paletteIndex: number;
}>
star
.x;
const star: Array<{
    x: number;
    y: number;
    speed: number;
    brightness: number;
    paletteIndex: number;
}>
star
.prevY =
const star: Array<{
    x: number;
    y: number;
    speed: number;
    brightness: number;
    paletteIndex: number;
}>
star
.y;
// Left means subtract from x (the origin is at the top-left of the screen).
const star: Array<{
    x: number;
    y: number;
    speed: number;
    brightness: number;
    paletteIndex: number;
}>
star
.x -=
const star: Array<{
    x: number;
    y: number;
    speed: number;
    brightness: number;
    paletteIndex: number;
}>
star
.speed;
// If the whole star is past the left edge, teleport it to the right. // Think of a conveyor belt: exit left, re-enter right with a fresh row position. if (
const star: Array<{
    x: number;
    y: number;
    speed: number;
    brightness: number;
    paletteIndex: number;
}>
star
.x < -wrapW: number
how many pixels wide the drawable star occupies (for wrapping)
@paramwrapW how many pixels wide the drawable star occupies (for wrapping)
wrapW
) {
const star: Array<{
    x: number;
    y: number;
    speed: number;
    brightness: number;
    paletteIndex: number;
}>
star
.x =
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;
    ... 106 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. Mutating the instance (for example `BT.random.int(10)`) advances the shared stream.
@since1.5.0@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 DISPLAY_W: 320DISPLAY_W, const DISPLAY_W: 320DISPLAY_W + 40);
const star: Array<{
    x: number;
    y: number;
    speed: number;
    brightness: number;
    paletteIndex: number;
}>
star
.y =
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;
    ... 106 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. Mutating the instance (for example `BT.random.int(10)`) advances the shared stream.
@since1.5.0@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
(0, const DISPLAY_H: 240DISPLAY_H);
// Snap prevX/prevY to match the teleported spot too. Without this, // render() would blend from the old off-screen-left position all the // way across to the new one, drawing a streak clear across the sky.
const star: Array<{
    x: number;
    y: number;
    speed: number;
    brightness: number;
    paletteIndex: number;
}>
star
.prevX =
const star: Array<{
    x: number;
    y: number;
    speed: number;
    brightness: number;
    paletteIndex: number;
}>
star
.x;
const star: Array<{
    x: number;
    y: number;
    speed: number;
    brightness: number;
    paletteIndex: number;
}>
star
.prevY =
const star: Array<{
    x: number;
    y: number;
    speed: number;
    brightness: number;
    paletteIndex: number;
}>
star
.y;
} } } /** * Maybe start a new streak, or move the current one until it leaves the screen. */ Demo.updateStreak(): void
Maybe start a new streak, or move the current one until it leaves the screen.
updateStreak
() {
if (this.
Demo.streak: {
    active: boolean;
    headX: number;
    headY: number;
    prevHeadX: number;
    prevHeadY: number;
}
streak
.active: booleanactive) {
// Remember the head's position before this tick moves it. this.
Demo.streak: {
    active: boolean;
    headX: number;
    headY: number;
    prevHeadX: number;
    prevHeadY: number;
}
streak
.prevHeadX: numberprevHeadX = this.
Demo.streak: {
    active: boolean;
    headX: number;
    headY: number;
    prevHeadX: number;
    prevHeadY: number;
}
streak
.headX: numberheadX;
this.
Demo.streak: {
    active: boolean;
    headX: number;
    headY: number;
    prevHeadX: number;
    prevHeadY: number;
}
streak
.prevHeadY: numberprevHeadY = this.
Demo.streak: {
    active: boolean;
    headX: number;
    headY: number;
    prevHeadX: number;
    prevHeadY: number;
}
streak
.headY: numberheadY;
// Very fast compared to normal stars - several pixels per tick. this.
Demo.streak: {
    active: boolean;
    headX: number;
    headY: number;
    prevHeadX: number;
    prevHeadY: number;
}
streak
.headX: numberheadX -= 14;
// A gentle downward drift sells the "falling" look. this.
Demo.streak: {
    active: boolean;
    headX: number;
    headY: number;
    prevHeadX: number;
    prevHeadY: number;
}
streak
.headY: numberheadY += 0.7;
// Once the head is well past the left edge, turn it off and reset the timer. if (this.
Demo.streak: {
    active: boolean;
    headX: number;
    headY: number;
    prevHeadX: number;
    prevHeadY: number;
}
streak
.headX: numberheadX < -24) {
this.
Demo.streak: {
    active: boolean;
    headX: number;
    headY: number;
    prevHeadX: number;
    prevHeadY: number;
}
streak
.active: booleanactive = false;
this.Demo.ticksSinceShoot: numberticksSinceShoot = 0; this.Demo.nextShootDelay: numbernextShootDelay =
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;
    ... 106 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. Mutating the instance (for example `BT.random.int(10)`) advances the shared stream.
@since1.5.0@returnsThe shared {@link Random} instance.@exampleBT.randomSeed(42); BT.random.int(150, 420); BT.random.pick(['a', 'b', 'c']);
random
.Random.int(minOrMaxExclusive: number, maxExclusive?: number): number
Returns a pseudo-random integer in [0, maxExclusive) or [min, maxExclusive).
@paramminOrMaxExclusive - When alone, exclusive upper bound from 0; otherwise inclusive min.@parammaxExclusive - Exclusive upper bound when two arguments are passed.@returnsWhole number in the half-open range.@since1.5.0
int
(180, 240);
} return; } // No active streak: count ticks until the next launch window. this.Demo.ticksSinceShoot: numberticksSinceShoot += 1; if (this.Demo.ticksSinceShoot: numberticksSinceShoot >= this.Demo.nextShootDelay: numbernextShootDelay) { this.Demo.spawnStreak(): void
Place a new streak just beyond the right edge so it flies across the sky.
spawnStreak
();
this.Demo.ticksSinceShoot: numberticksSinceShoot = 0; } } /** * Place a new streak just beyond the right edge so it flies across the sky. */ Demo.spawnStreak(): void
Place a new streak just beyond the right edge so it flies across the sky.
spawnStreak
() {
this.
Demo.streak: {
    active: boolean;
    headX: number;
    headY: number;
    prevHeadX: number;
    prevHeadY: number;
}
streak
.active: booleanactive = true;
// Start slightly off-screen to the right so it enters smoothly. this.
Demo.streak: {
    active: boolean;
    headX: number;
    headY: number;
    prevHeadX: number;
    prevHeadY: number;
}
streak
.headX: numberheadX =
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;
    ... 106 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. Mutating the instance (for example `BT.random.int(10)`) advances the shared stream.
@since1.5.0@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 DISPLAY_W: 320DISPLAY_W + 10, const DISPLAY_W: 320DISPLAY_W + 70);
// Keep it in the upper half so it reads as "sky" above the labels. this.
Demo.streak: {
    active: boolean;
    headX: number;
    headY: number;
    prevHeadX: number;
    prevHeadY: number;
}
streak
.headY: numberheadY =
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;
    ... 106 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. Mutating the instance (for example `BT.random.int(10)`) advances the shared stream.
@since1.5.0@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
(16, 16 + const DISPLAY_H: 240DISPLAY_H * 0.45);
// Snap prevHeadX/prevHeadY to the spawn point too, so the very first render // after spawning does not blend in from wherever the last streak died. this.
Demo.streak: {
    active: boolean;
    headX: number;
    headY: number;
    prevHeadX: number;
    prevHeadY: number;
}
streak
.prevHeadX: numberprevHeadX = this.
Demo.streak: {
    active: boolean;
    headX: number;
    headY: number;
    prevHeadX: number;
    prevHeadY: number;
}
streak
.headX: numberheadX;
this.
Demo.streak: {
    active: boolean;
    headX: number;
    headY: number;
    prevHeadX: number;
    prevHeadY: number;
}
streak
.prevHeadY: numberprevHeadY = this.
Demo.streak: {
    active: boolean;
    headX: number;
    headY: number;
    prevHeadX: number;
    prevHeadY: number;
}
streak
.headY: numberheadY;
} /** * Draw a short bright line: tail behind the head along the motion direction. * Uses the static C_STREAK palette slot registered in init(). */ Demo.drawStreak(): void
Draw a short bright line: tail behind the head along the motion direction. Uses the static C_STREAK palette slot registered in init().
drawStreak
() {
if (!this.
Demo.streak: {
    active: boolean;
    headX: number;
    headY: number;
    prevHeadX: number;
    prevHeadY: number;
}
streak
.active: booleanactive) {
return; } // Blend the head's previous and current tick position by BT.renderAlpha - a // fraction from 0 (a tick just finished) to just under 1 (the next tick is // about to happen) - so the streak's drawn position matches this exact render // moment instead of only its last-tick position. 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;
    ... 106 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
;
const const hx: anyhx = Math.floor(this.
Demo.streak: {
    active: boolean;
    headX: number;
    headY: number;
    prevHeadX: number;
    prevHeadY: number;
}
streak
.prevHeadX: numberprevHeadX + (this.
Demo.streak: {
    active: boolean;
    headX: number;
    headY: number;
    prevHeadX: number;
    prevHeadY: number;
}
streak
.headX: numberheadX - this.
Demo.streak: {
    active: boolean;
    headX: number;
    headY: number;
    prevHeadX: number;
    prevHeadY: number;
}
streak
.prevHeadX: numberprevHeadX) * const alpha: numberalpha);
const const hy: anyhy = Math.floor(this.
Demo.streak: {
    active: boolean;
    headX: number;
    headY: number;
    prevHeadX: number;
    prevHeadY: number;
}
streak
.prevHeadY: numberprevHeadY + (this.
Demo.streak: {
    active: boolean;
    headX: number;
    headY: number;
    prevHeadX: number;
    prevHeadY: number;
}
streak
.headY: numberheadY - this.
Demo.streak: {
    active: boolean;
    headX: number;
    headY: number;
    prevHeadX: number;
    prevHeadY: number;
}
streak
.prevHeadY: numberprevHeadY) * const alpha: numberalpha);
// Tail sits to the right and a little up because we move left and down each tick. const const tailX: anytailX = const hx: anyhx + 14; const const tailY: numbertailY = const hy: anyhy - 4; // C_STREAK is the cool white color registered in init().
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;
    ... 106 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
(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
(const tailX: anytailX, const tailY: numbertailY), 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
(const hx: anyhx, const hy: anyhy), const C_STREAK: 7C_STREAK);
} /** * Draws one layer of stars. All three layers share the same movement math; * only the drawn size differs, so one method handles them all. * Each star's paletteIndex was set in init() to point at its unique gray shade. * * @param {Array<{x: number, y: number, prevX: number, prevY: number, paletteIndex: number}>} layer * @param {number} size - Star width and height in pixels: 1 draws a single pixel, * anything bigger draws a filled square (near stars use NEAR_STAR_SIZE). */
Demo.drawLayer(layer: Array<{
    x: number;
    y: number;
    prevX: number;
    prevY: number;
    paletteIndex: number;
}>, size: number): void
Draws one layer of stars. All three layers share the same movement math; only the drawn size differs, so one method handles them all. Each star's paletteIndex was set in init() to point at its unique gray shade.
@paramlayer@paramsize - Star width and height in pixels: 1 draws a single pixel, anything bigger draws a filled square (near stars use NEAR_STAR_SIZE).
drawLayer
(
layer: Array<{
    x: number;
    y: number;
    prevX: number;
    prevY: number;
    paletteIndex: number;
}>
@paramlayer
layer
, size: number
- Star width and height in pixels: 1 draws a single pixel, anything bigger draws a filled square (near stars use NEAR_STAR_SIZE).
@paramsize - Star width and height in pixels: 1 draws a single pixel, anything bigger draws a filled square (near stars use NEAR_STAR_SIZE).
size
) {
// Blend each star's previous and current tick position by BT.renderAlpha - a // fraction from 0 (a tick just finished) to just under 1 (the next tick is // about to happen) - so the star's drawn position matches this exact render // moment instead of only its last-tick position. 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;
    ... 106 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
;
for (let let i: numberi = 0; let i: numberi <
layer: Array<{
    x: number;
    y: number;
    prevX: number;
    prevY: number;
    paletteIndex: number;
}>
@paramlayer
layer
.length; let i: numberi++) {
const
const star: Array<{
    x: number;
    y: number;
    prevX: number;
    prevY: number;
    paletteIndex: number;
}>
star
=
layer: Array<{
    x: number;
    y: number;
    prevX: number;
    prevY: number;
    paletteIndex: number;
}>
@paramlayer
layer
[let i: numberi];
const const px: anypx = Math.floor(
const star: Array<{
    x: number;
    y: number;
    prevX: number;
    prevY: number;
    paletteIndex: number;
}>
star
.prevX + (
const star: Array<{
    x: number;
    y: number;
    prevX: number;
    prevY: number;
    paletteIndex: number;
}>
star
.x -
const star: Array<{
    x: number;
    y: number;
    prevX: number;
    prevY: number;
    paletteIndex: number;
}>
star
.prevX) * const alpha: numberalpha);
const const py: anypy = Math.floor(
const star: Array<{
    x: number;
    y: number;
    prevX: number;
    prevY: number;
    paletteIndex: number;
}>
star
.prevY + (
const star: Array<{
    x: number;
    y: number;
    prevX: number;
    prevY: number;
    paletteIndex: number;
}>
star
.y -
const star: Array<{
    x: number;
    y: number;
    prevX: number;
    prevY: number;
    paletteIndex: number;
}>
star
.prevY) * const alpha: numberalpha);
if (size: number
- Star width and height in pixels: 1 draws a single pixel, anything bigger draws a filled square (near stars use NEAR_STAR_SIZE).
@paramsize - Star width and height in pixels: 1 draws a single pixel, anything bigger draws a filled square (near stars use NEAR_STAR_SIZE).
size
=== 1) {
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;
    ... 106 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
(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
(const px: anypx, const py: anypy),
const star: Array<{
    x: number;
    y: number;
    prevX: number;
    prevY: number;
    paletteIndex: number;
}>
star
.paletteIndex);
} 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;
    ... 106 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
(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 px: anypx, const py: anypy, size: number
- Star width and height in pixels: 1 draws a single pixel, anything bigger draws a filled square (near stars use NEAR_STAR_SIZE).
@paramsize - Star width and height in pixels: 1 draws a single pixel, anything bigger draws a filled square (near stars use NEAR_STAR_SIZE).
size
, size: number
- Star width and height in pixels: 1 draws a single pixel, anything bigger draws a filled square (near stars use NEAR_STAR_SIZE).
@paramsize - Star width and height in pixels: 1 draws a single pixel, anything bigger draws a filled square (near stars use NEAR_STAR_SIZE).
size
),
const star: Array<{
    x: number;
    y: number;
    prevX: number;
    prevY: number;
    paletteIndex: number;
}>
star
.paletteIndex);
} } } /** * Explain the three layers with the shared UI kit (drawn last so text stays readable). * We skip ui.panel() on purpose: without it the group is just floating text, which * keeps the sky visible behind the caption instead of covering it with a box. */ Demo.drawLabels(): void
Explain the three layers with the shared UI kit (drawn last so text stays readable). We skip ui.panel() on purpose: without it the group is just floating text, which keeps the sky visible behind the caption instead of covering it with a box.
drawLabels
() {
import uiui.begin(import UI_ANCHORSUI_ANCHORS.TOP_LEFT); import uiui.label('FAR: slow, dim, 1 pixel', { color: stringcolor: 'dim' }); import uiui.label('MED: faster, brighter pixel', { color: stringcolor: 'dim' }); import uiui.label('NEAR: fastest, bright 2x2 block', { color: stringcolor: 'dim' }); import uiui.end(); } } // bootstrap finds your canvas, constructs Demo, and runs the game loop for you. 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 Demo
Parallax starfield with three layers plus an occasional shooting star.
@implementsIBTDemo
Demo
);