/*

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

Live version: https://demos.blit386.dev/starfield

Guides:
  - Game loop: https://blit386.dev/docs/guides/game-loop#smoothing-motion-between-updates
  - Random: https://blit386.dev/docs/guides/random#deterministic-by-default
  - Overlay: https://blit386.dev/docs/guides/overlay#colors-and-hud-palette-slots

Reference:
  - Rendering: https://blit386.dev/docs/api/rendering#primitives
  - Game loop: https://blit386.dev/docs/api/game-loop#interpolating-render-state-with-renderalpha
  - Random: https://blit386.dev/docs/api/random#engine-default-btrandom

---

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

A very dark blue-black sky with 60 gray stars sliding to the left in three layers:

  - Far: 30 dim single pixels, slow (0.3 to 0.5 pixels per tick).
  - Medium: 20 brighter single pixels, faster (0.8 to 1.2 pixels per tick).
  - Near: 10 bright 2x2 squares, fastest (1.5 to 2.5 pixels per tick).

When a star leaves the left edge it reappears just past the right edge on a new row, so
the sky never runs out. Every few seconds a shooting star streaks across the upper half of
the screen: a short white line racing left and slightly down at 14 pixels per tick. Three
dim lines of text in the top-left corner name the layers.

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, brightness, 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 for an endless sky.
- BT.random.float(min, max) and BT.random.intInclusive(min, max) for random positions,
  speeds, and brightness.
- Smooth motion with BT.renderAlpha: update() moves the stars in fixed ticks, and render()
  blends each star between its previous and current position so nothing hops.
- A shooting star: a fast diagonal line drawn with BT.drawLine().
- Turning on the engine overlay's timing chart from configure() and picking its colors.

HOW COLORS WORK IN THIS DEMO
----------------------------

Every star has its own brightness, a gray level from 80 (dim) to 255 (white). Instead of
making a new Color32 every frame, init() registers each star's gray in the palette once
(slots 10 to 69, one per star) and stores the slot number on the star. render() just
reads that slot number - no Color32 objects are created per frame.

The three label lines are drawn with the shared UI kit (src/shared/ui.js), so their color
and spacing match every other demo. applyTheme() puts the kit's colors in slots 240 and
up, far above this demo's slots 2 to 69.

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

TRY THIS
--------

- Press the backquote key (`) to open the engine overlay. The timing chart band is turned
  on by configure(): the pale blue bars are update() time and the white bars are render()
  time, so you can watch what 60 moving stars cost.

*/

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;
    ... 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 uiui, import UI_ANCHORSUI_ANCHORS } from './shared/ui.js'; /** @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 to 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. class class DemoDemo { // 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, prevX, prevY, speed, // brightness, paletteIndex }. paletteIndex is the slot number registered in 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 ticks instead of hopping in 14-pixel jumps.
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 launching the next one. Demo.nextShootDelay: numbernextShootDelay = 200; // Shows the timing chart while many stars move each frame (useful for spotting render // spikes), and picks palette slots for the overlay bars and chart colors.
Demo.configure(): {
    isOverlayTimingChartEnabled: boolean;
    overlayStyle: {
        barPaletteIndex: number;
        textPaletteIndex: number;
        gapPaletteIndex: number;
    };
    overlayTimingChartStyle: {
        updateBarPaletteIndex: number;
        renderBarPaletteIndex: number;
        warningPaletteIndex: number;
        errorPaletteIndex: number;
        tagPaletteIndex: number;
    };
}
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 the star layers. The order matters: // 1. Create the palette and register the static colors. // 2. Build the star layers (this decides each star's brightness). // 3. Register each star's gray in the palette and store the slot on the star. // 4. BT.paletteSet() - tell the engine to use this palette. async Demo.init(): unknowninit() { console.log('[StarfieldDemo] Initializing...'); // Step 1: create the palette and the 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;
    ... 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);
// Deep space (very dark blue-black), then three grays for the overlay text and // timing chart (update bars, warnings, tag labels), then a cool white for the // streak. 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));
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));
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));
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));
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));
// Step 2: build the three star layers. Each call picks random positions, speeds, // and brightness values in the given ranges. // Far stars: slow (0.3 to 0.5 pixels per tick), dim (brightness 80 to 120). // Medium stars: medium speed (0.8 to 1.2), brighter (150 to 200). // Near stars: fast (1.5 to 2.5), bright (220 to 255). this.Demo.farLayer: {}farLayer = this.Demo.createLayerData(count: any, speedMin: any, speedMax: any, brightMin: any, brightMax: any): {}createLayerData(const FAR_COUNT: 30FAR_COUNT, 0.3, 0.5, 80, 120); this.Demo.mediumLayer: {}mediumLayer = this.Demo.createLayerData(count: any, speedMin: any, speedMax: any, brightMin: any, brightMax: any): {}createLayerData(const MEDIUM_COUNT: 20MEDIUM_COUNT, 0.8, 1.2, 150, 200); this.Demo.nearLayer: {}nearLayer = this.Demo.createLayerData(count: any, speedMin: any, speedMax: any, brightMin: any, brightMax: any): {}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 slots (2 to 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
);
// Step 4: 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;
    ... 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
);
// Start the shooting-star timer at a pleasant "about 200 ticks" delay. int() // picks a whole number from the first value up to, but not including, the second. 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;
    ... 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.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 the ones that left the screen, // and spawn or advance the shooting star. Demo.update(): voidupdate() { // Move the three layers. The second argument is how wide the star is, for the // "gone off the left?" check. this.Demo.moveLayer(layer: any, wrapW: any): voidmoveLayer(this.Demo.farLayer: {}farLayer, 1); this.Demo.moveLayer(layer: any, wrapW: any): voidmoveLayer(this.Demo.mediumLayer: {}mediumLayer, 1); this.Demo.moveLayer(layer: any, wrapW: any): voidmoveLayer(this.Demo.nearLayer: {}nearLayer, const NEAR_STAR_SIZE: 2NEAR_STAR_SIZE); this.Demo.updateStreak(): voidupdateStreak(); } // Draws the sky, the stars (back to front), the shooting streak, then the text labels // on top. Notice: no Color32 objects appear here. Every draw call uses a palette // index. Demo.render(): voidrender() { // 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;
    ... 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);
// Draw back to front so near stars cover far ones, like real depth. Far and // medium stars are single pixels (size 1); near stars are bigger blocks. this.Demo.drawLayer(layer: any, size: any): voiddrawLayer(this.Demo.farLayer: {}farLayer, 1); this.Demo.drawLayer(layer: any, size: any): voiddrawLayer(this.Demo.mediumLayer: {}mediumLayer, 1); this.Demo.drawLayer(layer: any, size: any): voiddrawLayer(this.Demo.nearLayer: {}nearLayer, const NEAR_STAR_SIZE: 2NEAR_STAR_SIZE); this.Demo.drawStreak(): voiddrawStreak(); this.Demo.drawLabels(): voiddrawLabels(); } // Builds one layer's raw data: `count` stars with random positions, speeds, and // brightness. Returns objects with { x, y, prevX, prevY, speed, brightness, // paletteIndex } - paletteIndex is filled in later by 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. Demo.createLayerData(count: any, speedMin: any, speedMax: any, brightMin: any, brightMax: any): {}createLayerData(count: anycount, speedMin: anyspeedMin, speedMax: anyspeedMax, brightMin: anybrightMin, brightMax: anybrightMax) { const const layer: {}layer = []; for (let let i: numberi = 0; let i: numberi < count: anycount; 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;
    ... 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
(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;
    ... 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
(0, const DISPLAY_H: 240DISPLAY_H);
// Speed: how many pixels left per update tick (a decimal 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;
    ... 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
(speedMin: anyspeedMin, speedMax: anyspeedMax);
// 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;
    ... 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.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: anybrightMin, brightMax: anybrightMax);
// paletteIndex starts at 0; init() fills 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; } // Moves every star in one layer to the left by its speed, then wraps it if it exited // on the left. `wrapW` is how many pixels wide the drawn star is. Demo.moveLayer(layer: any, wrapW: any): voidmoveLayer(layer: anylayer, wrapW: anywrapW) { for (let let i: numberi = 0; let i: numberi < layer: anylayer.length; let i: numberi++) { const const star: anystar = layer: anylayer[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: anystar.prevX = const star: anystar.x; const star: anystar.prevY = const star: anystar.y; // Left means subtract from x (the origin is at the top-left of the screen). const star: anystar.x -= const star: anystar.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 on a fresh row. if (const star: anystar.x < -wrapW: anywrapW) { const star: anystar.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;
    ... 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 DISPLAY_W: 320DISPLAY_W, const DISPLAY_W: 320DISPLAY_W + 40);
const star: anystar.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;
    ... 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
(0, const DISPLAY_H: 240DISPLAY_H);
// Snap prevX/prevY to 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: anystar.prevX = const star: anystar.x; const star: anystar.prevY = const star: anystar.y; } } } // Starts a new streak when the timer runs out, or moves the current one until it // leaves the screen. Demo.updateStreak(): voidupdateStreak() { 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 - 14 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;
    ... 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.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(): voidspawnStreak(); this.Demo.ticksSinceShoot: numberticksSinceShoot = 0; } } // Places a new streak just beyond the right edge so it flies across the sky. Demo.spawnStreak(): voidspawnStreak() { 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;
    ... 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 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;
    ... 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
(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;
} // Draws a short bright line: the tail trails behind the head along the direction of // motion. Uses the static C_STREAK palette slot registered in init(). Demo.drawStreak(): voiddrawStreak() { 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;
    ... 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
;
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);
// The tail sits to the right and a little up because we move left and down. 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;
    ... 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
(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. `size` is the star's width and // height in pixels: 1 draws a single pixel, anything bigger draws a filled square // (near stars use NEAR_STAR_SIZE). Each star's paletteIndex was set in init() to // point at its own gray shade. Demo.drawLayer(layer: any, size: any): voiddrawLayer(layer: anylayer, size: anysize) { // 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;
    ... 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
;
for (let let i: numberi = 0; let i: numberi < layer: anylayer.length; let i: numberi++) { const const star: anystar = layer: anylayer[let i: numberi]; const const px: anypx = Math.floor(const star: anystar.prevX + (const star: anystar.x - const star: anystar.prevX) * const alpha: numberalpha); const const py: anypy = Math.floor(const star: anystar.prevY + (const star: anystar.y - const star: anystar.prevY) * const alpha: numberalpha); if (size: anysize === 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;
    ... 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
(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: anystar.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;
    ... 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
(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: anysize, size: anysize), const star: anystar.paletteIndex);
} } } // Explains the three layers with the shared UI kit (drawn last so the 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(): voiddrawLabels() { 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 DemoDemo);