/*
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.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 Color32Mutable 32-bit RGBA color value with 8-bit channels.Color32, class Rect2iInteger 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.Rect2i, class Vector2iInteger 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.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 | nullpalette = 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 | nullpalette = 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) => PaletteCreates a standalone palette instance.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: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_BG: 2C_BG, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(4, 6, 18));
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_CHART_TEXT: 3C_CHART_TEXT, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(200, 210, 230));
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_CHART_WARN: 4C_CHART_WARN, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(140, 150, 170));
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_CHART_TAG: 5C_CHART_TAG, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(110, 120, 140));
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_STREAK: 7C_STREAK, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.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: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(let slot: numberslot, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.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: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(let slot: numberslot, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.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: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(let slot: numberslot, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.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: Palettepalette);
// 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) => voidStores 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.paletteSet(this.Demo.palette: Palettepalette);
// 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: RandomDefault 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.random.Random.int(minOrMaxExclusive: number, maxExclusive?: number): numberReturns a pseudo-random integer in [0, maxExclusive) or [min, maxExclusive).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) => voidSets 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.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: RandomDefault 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.random.Random.float(min: number, max: number): numberReturns the next pseudo-random float in [min, max).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: RandomDefault 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.random.Random.float(min: number, max: number): numberReturns the next pseudo-random float in [min, max).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: RandomDefault 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.random.Random.float(min: number, max: number): numberReturns the next pseudo-random float in [min, max).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: RandomDefault 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.random.Random.intInclusive(min: number, max: number): numberReturns a pseudo-random integer in [min, max] (inclusive on both ends).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: RandomDefault 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.random.Random.float(min: number, max: number): numberReturns the next pseudo-random float in [min, max).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: RandomDefault 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.random.Random.float(min: number, max: number): numberReturns the next pseudo-random float in [min, max).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: RandomDefault 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.random.Random.int(minOrMaxExclusive: number, maxExclusive?: number): numberReturns a pseudo-random integer in [0, maxExclusive) or [min, maxExclusive).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: RandomDefault 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.random.Random.float(min: number, max: number): numberReturns the next pseudo-random float in [min, max).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: RandomDefault 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.random.Random.float(min: number, max: number): numberReturns the next pseudo-random float in [min, max).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: numberFractional progress between the last completed fixed update and the next.
Intended for interpolating render state between fixed-update steps.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) => voidDraws a pixel-perfect line between two points.
Uses rasterized line drawing without antialiasing.drawLine(new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(const tailX: anytailX, const tailY: numbertailY), new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.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: numberFractional progress between the last completed fixed update and the next.
Intended for interpolating render state between fixed-update steps.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) => voidDraws 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)`.drawPixel(new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.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) => voidDraws a filled rectangle.drawRectFill(new new Rect2i(x?: number, y?: number, width?: number, height?: number): Rect2iCreates an integer rectangle, truncating all inputs toward zero.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.bootstrap(class DemoDemo);