// Coordinate Patterns: an endless world that remembers nothing at all.
// @description An endless world computed from hash1i, hash2i, and hash3i that stores no tiles, yet never changes.
//
// Part of the BLIT386 demo series.
//
// Prerequisites:
//   Basics        https://demos.blit386.dev/basics
//   Random Basics https://demos.blit386.dev/random-basics
//   Seeded Worlds https://demos.blit386.dev/seeded-worlds
//
// WHAT YOU WILL SEE
// A world you can scroll around forever with the arrow keys, the on-screen D-pad, or a
// swipe. Press "Jump far" to fling yourself thousands of tiles away, then "Home" to come
// straight back - and find every tile exactly where you left it.
//
// Nothing was saved. The demo stores zero tiles. Each square works out what it is from
// nothing but its own position.
//
// WHAT YOU WILL LEARN
//   - hash1i(x, seed) turns one number into a scrambled-but-repeatable number
//   - hash2i(x, y, seed) does the same for a pair, which is what a tile map needs
//   - hash3i(x, y, z, seed) adds a third number, so you get a whole new set of answers
//   - "Stateless" means the answer is worked out fresh every time, never looked up
//
// HOW THIS DIFFERS FROM SEEDED WORLDS
// The Seeded Worlds demo rolls its hills and trees once and keeps the list in memory:
// https://demos.blit386.dev/seeded-worlds
// This demo keeps nothing. Ask about tile (4000, -812) and it answers instantly, without
// ever having visited it. That is how games fit worlds far too big for memory.
//
// WHY IT LOOKS LIKE STATIC
// Every tile is scrambled on its own, so a tile knows nothing about its neighbors. That is
// why the world looks speckled rather than like real countryside, where a lake is one big
// lake instead of scattered puddles. Smoothing that speckle into rolling hills is what the
// Noise demo is for: https://demos.blit386.dev/noise
//
// WATCH THE LAYER SLIDER CLOSELY
// The ground comes from hash2i, which only knows x and y - so sliding the layer leaves the
// landscape untouched. The little rocks come from hash3i, which also knows the layer - so
// they change completely. Same place, different detail: that is the third number at work.
//
// The engine splits work the usual way: update() moves things; render() only draws.
// See the Basics demo for the full story: https://demos.blit386.dev/basics

import { function bootstrap(DemoClass: DemoConstructor, options?: BootstrapOptions): Promise<boolean>
One-liner bootstrap function for BLIT386 demos. Handles canvas retrieval and engine initialization. Backend selection (WebGPU or software fallback) is managed internally by BTAPI. This function provides a streamlined way to start a demo with sensible defaults while allowing customization through options.
@since0.2.0@changed1.4.0 Calling `bootstrap()` again while already initialized now routes to a hot swap (via {@link registerHotReload}) when a Vite HMR context is registered, or logs a double-bootstrap guard and returns `false` otherwise - previously it silently started a second, unstoppable `GameLoop`.@changed1.7.0 Exposes `BT` on `window.BT` after bootstrap finishes, gated by {@link BootstrapOptions.exposeGlobal} (default: {@link BT.isDevMode}).@paramDemoClass - Demo class constructor implementing `IBTDemo` (optional `configure()` for hardware settings).@paramoptions - Optional configuration for IDs and callbacks.@returns`true` when the demo boots successfully; otherwise `false`.@example// Simplest usage - uses default IDs. bootstrap(MyDemo);@example// With custom options. bootstrap(MyDemo, { canvasID: 'custom-canvas', containerID: 'custom-container', onSuccess: () => console.log('Demo started!'), onError: (err) => analytics.trackError(err), });@example// Await the result. const success = await bootstrap(MyDemo); if (success) { console.log('Demo is running'); }
bootstrap
,
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
, class Color32
Mutable 32-bit RGBA color value with 8-bit channels.
@since0.1.0
Color32
, function hash1i(x: number, seed?: number): number
Deterministic uint32 hash of a 1D integer coordinate.
@paramx - Coordinate (truncated toward zero with `| 0`).@paramseed - World / stream seed (default `0`; lower 32 bits used).@returnsUnsigned 32-bit value in `[0, 2^32)`.@since1.5.0
hash1i
, function hash2i(x: number, y: number, seed?: number): number
Deterministic uint32 hash of a 2D integer coordinate.
@paramx - X coordinate (truncated toward zero with `| 0`).@paramy - Y coordinate (truncated toward zero with `| 0`).@paramseed - World / stream seed (default `0`; lower 32 bits used).@returnsUnsigned 32-bit value in `[0, 2^32)`.@since1.5.0
hash2i
, function hash3i(x: number, y: number, z: number, seed?: number): number
Deterministic uint32 hash of a 3D integer coordinate.
@paramx - X coordinate (truncated toward zero with `| 0`).@paramy - Y coordinate (truncated toward zero with `| 0`).@paramz - Z coordinate (truncated toward zero with `| 0`).@paramseed - World / stream seed (default `0`; lower 32 bits used).@returnsUnsigned 32-bit value in `[0, 2^32)`.@since1.5.0
hash3i
, class Rect2i
Integer rectangle for pixel-perfect bounds and regions. Used throughout the engine for sprite regions, display-space bounds, and zero-allocation geometry helpers. Both convenience getters and allocation-free `*To()` helpers are provided so callers can choose between readability and hot-path efficiency.
@since0.1.0
Rect2i
, class Vector2i
Integer 2D vector for pixel-perfect positioning. Used for points, sizes, directions, and camera offsets throughout the engine. The API includes both allocation-free `*To()` / `*InPlace()` variants and convenience methods that return new vectors.
@since0.1.0
Vector2i
} from 'blit386';
import { import applyThemeapplyTheme, import uiui, import UI_ANCHORSUI_ANCHORS } from './shared/ui.js'; /** @typedef {import('blit386').IBTDemo} IBTDemo */ /** @typedef {import('blit386').HardwareSettings} HardwareSettings */ /** @typedef {import('blit386').Palette} Palette */ // This demo runs at a doubled screen size of 640x480 "game pixels" (the engine default is // 320x240), so every layout constant below is twice the size it would be at the default. const const DISPLAY_W: 640DISPLAY_W = 640; const const DISPLAY_H: 480DISPLAY_H = 480; // The seed for this whole world. Change this one number and every tile everywhere becomes // something else. const const WORLD_SEED: 20260730WORLD_SEED = 20260730; // The 1D strip along the top: one bar per screen column. const const STRIP_Y: 28STRIP_Y = 28; const const STRIP_H: 44STRIP_H = 44; // The tile grid below it. const const TILE: 32TILE = 32; const const GRID_Y: 90GRID_Y = 90; const const GRID_H: 400GRID_H = 400; // How fast holding a direction scrolls, in pixels per update tick. const const SCROLL_SPEED: 6SCROLL_SPEED = 6; // A swipe throws you this many pixels, and "Jump far" this many. const const SWIPE_DISTANCE: 320SWIPE_DISTANCE = 320; const const JUMP_DISTANCE: 128000JUMP_DISTANCE = 128000; // How many layers the slider can reach. const const LAYER_MAX: 8LAYER_MAX = 8; // Color slots. The shared UI kit owns slots 240-251, so scene colors stay well below that. const const C_BG: 1C_BG = 1; // Background behind everything. const const C_STRIP: 2C_STRIP = 2; // The 1D bars along the top. const const C_INK: 3C_INK = 3; // Frames and bright marks. const const C_DIM: 4C_DIM = 4; // Faded lines. const const C_ROCK_DOT: 5C_ROCK_DOT = 5; // The little decorations that hash3i places. // The four kinds of ground, in slots 10-13. The order matters: the number a tile hashes to // is compared against the thresholds below in this same order. const const C_TERRAIN_BASE: 10C_TERRAIN_BASE = 10; const const TERRAIN_WATER: 0TERRAIN_WATER = 0; const const TERRAIN_ROCK: 1TERRAIN_ROCK = 1; const const TERRAIN_TREE: 2TERRAIN_TREE = 2; const const TERRAIN_GRASS: 3TERRAIN_GRASS = 3; /** * Works out what kind of ground sits at a tile, using nothing but the tile's position. * * There is no list of tiles anywhere in this demo. This function is the world: hand it a * position and it hands back the ground, the same answer every time, forever. * * @param {number} tx - Tile column. Can be any whole number, including huge and negative ones. * @param {number} ty - Tile row. * @returns {number} One of the TERRAIN_* values. */ function function terrainAt(tx: number, ty: number): number
Works out what kind of ground sits at a tile, using nothing but the tile's position. There is no list of tiles anywhere in this demo. This function is the world: hand it a position and it hands back the ground, the same answer every time, forever.
@paramtx - Tile column. Can be any whole number, including huge and negative ones.@paramty - Tile row.@returnsOne of the TERRAIN_* values.
terrainAt
(tx: number
- Tile column. Can be any whole number, including huge and negative ones.
@paramtx - Tile column. Can be any whole number, including huge and negative ones.
tx
, ty: number
- Tile row.
@paramty - Tile row.
ty
) {
// hash2i scrambles the two coordinates together into a big number. "Scrambled" is the // point: neighboring tiles get wildly different answers, so the world looks random even // though nothing random ever happened. // // The number can be up to about 4 billion, which is far too big to be useful. Taking the // remainder after dividing by 100 (that is what % does) squashes it down to 0-99, which // is easy to split into slices. const const value: numbervalue = function hash2i(x: number, y: number, seed?: number): number
Deterministic uint32 hash of a 2D integer coordinate.
@paramx - X coordinate (truncated toward zero with `| 0`).@paramy - Y coordinate (truncated toward zero with `| 0`).@paramseed - World / stream seed (default `0`; lower 32 bits used).@returnsUnsigned 32-bit value in `[0, 2^32)`.@since1.5.0
hash2i
(tx: number
- Tile column. Can be any whole number, including huge and negative ones.
@paramtx - Tile column. Can be any whole number, including huge and negative ones.
tx
, ty: number
- Tile row.
@paramty - Tile row.
ty
, const WORLD_SEED: 20260730WORLD_SEED) % 100;
if (const value: numbervalue < 12) { return const TERRAIN_WATER: 0TERRAIN_WATER; // 12 tiles in every 100. } if (const value: numbervalue < 26) { return const TERRAIN_ROCK: 1TERRAIN_ROCK; // 14 in every 100. } if (const value: numbervalue < 42) { return const TERRAIN_TREE: 2TERRAIN_TREE; // 16 in every 100. } return const TERRAIN_GRASS: 3TERRAIN_GRASS; // The remaining 58. } /** * An endless scrollable world computed from coordinates, storing nothing. * * @implements {IBTDemo} */ class class Demo
An endless scrollable world computed from coordinates, storing nothing.
@implementsIBTDemo
Demo
{
/** @type {Palette | null} */ Demo.palette: Palette | null
@type{Palette | null}
palette
= null;
// Slot map for the shared UI kit theme, filled in by applyTheme() during init(). Demo.theme: nulltheme = null; // Where we are looking, measured in world pixels. These two numbers are the only thing // the demo remembers about the world - and they are a position, not a map. Demo.camX: numbercamX = 0; Demo.camY: numbercamY = 0; // Which layer the third hash coordinate is reading. Demo.layer: numberlayer = 0; /** * Runs the demo at 640x480. Arrow keys normally scroll the page, but this demo maps * them to scroll the world instead, so opt in so the browser does not scroll the demo * page while you play. * * @returns {Partial<HardwareSettings>} */ Demo.configure(): Partial<HardwareSettings>
Runs the demo at 640x480. Arrow keys normally scroll the page, but this demo maps them to scroll the world instead, so opt in so the browser does not scroll the demo page while you play.
@returns
configure
() {
return { displaySize: Vector2idisplaySize: 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 DISPLAY_W: 640DISPLAY_W, const DISPLAY_H: 480DISPLAY_H),
isCapturingKeyboardScroll: booleanisCapturingKeyboardScroll: true, }; } /** * Builds the palette. There is no world to build - that is the whole idea. * * @returns {Promise<boolean>} */ async Demo.init(): Promise<boolean>
Builds the palette. There is no world to build - that is the whole idea.
@returns
init
() {
this.Demo.palette: Palette | null
@type{Palette | null}
palette
=
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.paletteCreate: (size?: number) => Palette
Creates a standalone palette instance.
@since1.0.3@paramsize - Palette size. Defaults to 256 colors.@returnsNew mutable palette.
paletteCreate
(256);
this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const C_BG: 1C_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
(12, 14, 22)); // Background.
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_STRIP: 2C_STRIP, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32
Creates a clamped 8-bit RGBA color.
@paramr - Red channel (0-255, defaults to 255).@paramg - Green channel (0-255, defaults to 255).@paramb - Blue channel (0-255, defaults to 255).@parama - Alpha channel (0-255, defaults to 255 = opaque).
Color32
(126, 195, 255)); // 1D bars.
this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const C_INK: 3C_INK, 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
(226, 232, 244)); // Frames and marks.
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_DIM: 4C_DIM, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32
Creates a clamped 8-bit RGBA color.
@paramr - Red channel (0-255, defaults to 255).@paramg - Green channel (0-255, defaults to 255).@paramb - Blue channel (0-255, defaults to 255).@parama - Alpha channel (0-255, defaults to 255 = opaque).
Color32
(64, 72, 92)); // Faded lines.
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_ROCK_DOT: 5C_ROCK_DOT, 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
(232, 214, 160)); // hash3i decorations.
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_TERRAIN_BASE: 10C_TERRAIN_BASE + const TERRAIN_WATER: 0TERRAIN_WATER, 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
(44, 82, 140)); // Water.
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_TERRAIN_BASE: 10C_TERRAIN_BASE + const TERRAIN_ROCK: 1TERRAIN_ROCK, 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
(104, 104, 112)); // Rock.
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_TERRAIN_BASE: 10C_TERRAIN_BASE + const TERRAIN_TREE: 2TERRAIN_TREE, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32
Creates a clamped 8-bit RGBA color.
@paramr - Red channel (0-255, defaults to 255).@paramg - Green channel (0-255, defaults to 255).@paramb - Blue channel (0-255, defaults to 255).@parama - Alpha channel (0-255, defaults to 255 = opaque).
Color32
(52, 118, 68)); // Forest.
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_TERRAIN_BASE: 10C_TERRAIN_BASE + const TERRAIN_GRASS: 3TERRAIN_GRASS, 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
(86, 150, 84)); // Grass.
// Install the shared UI colors, then hand the finished palette to the engine. this.Demo.theme: nulltheme = import applyThemeapplyTheme(this.Demo.palette: Palette
@type{Palette | null}
palette
);
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.paletteSet: (palette: Palette) => void
Stores the active engine palette. Use this to swap the **entire palette** (e.g. switch between a day and night theme). After this call the renderer uploads the new palette uniform on the next frame. **Palette-value swap (change what a slot looks like):** mutate the live {@link BT.palette } in place with `palette.set(slot, newColor)`. The renderer uploads dirty slots on the next frame; no `paletteSet()` or {@link BT.spritesRefresh } needed. **Palette-layout swap (same colors, different slot positions):** build a new palette with the same colors at new indices, call `paletteSet()`, then call {@link BT.spritesRefresh } so every sprite sheet re-maps its original RGBA pixels against the new slot layout.
@since1.0.3@parampalette - Palette to make active.
paletteSet
(this.Demo.palette: Palette
@type{Palette | null}
palette
);
return true; } /** * Moves the view. Nothing else changes, because there is nothing else. */ Demo.update(): void
Moves the view. Nothing else changes, because there is nothing else.
update
() {
// Always first: this latches key shortcuts, the D-pad, and the swipe recognizer. import uiui.tick(); // Held keys and the on-screen D-pad both scroll. isKeyDown is "held right now", which // is safe to read from either update() or render() - unlike a key press, which is a // one-off event and must be read here. if (
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.isKeyDown: (key: string) => boolean
Checks whether a keyboard key is currently held. Uses `KeyboardEvent.code` (for example `"KeyW"`, `"Space"`, `"ArrowUp"`).
@since1.1.1@paramkey - DOM keyboard code string.@returns`true` while the key remains pressed.
isKeyDown
('ArrowLeft') || import uiui.dpad.isDown('left')) {
this.Demo.camX: numbercamX -= const SCROLL_SPEED: 6SCROLL_SPEED; } if (
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.isKeyDown: (key: string) => boolean
Checks whether a keyboard key is currently held. Uses `KeyboardEvent.code` (for example `"KeyW"`, `"Space"`, `"ArrowUp"`).
@since1.1.1@paramkey - DOM keyboard code string.@returns`true` while the key remains pressed.
isKeyDown
('ArrowRight') || import uiui.dpad.isDown('right')) {
this.Demo.camX: numbercamX += const SCROLL_SPEED: 6SCROLL_SPEED; } if (
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.isKeyDown: (key: string) => boolean
Checks whether a keyboard key is currently held. Uses `KeyboardEvent.code` (for example `"KeyW"`, `"Space"`, `"ArrowUp"`).
@since1.1.1@paramkey - DOM keyboard code string.@returns`true` while the key remains pressed.
isKeyDown
('ArrowUp') || import uiui.dpad.isDown('up')) {
this.Demo.camY: numbercamY -= const SCROLL_SPEED: 6SCROLL_SPEED; } if (
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.isKeyDown: (key: string) => boolean
Checks whether a keyboard key is currently held. Uses `KeyboardEvent.code` (for example `"KeyW"`, `"Space"`, `"ArrowUp"`).
@since1.1.1@paramkey - DOM keyboard code string.@returns`true` while the key remains pressed.
isKeyDown
('ArrowDown') || import uiui.dpad.isDown('down')) {
this.Demo.camY: numbercamY += const SCROLL_SPEED: 6SCROLL_SPEED; } // A swipe throws the view a screen's worth in one go, so a phone can cover ground // without holding anything down. const const swipe: anyswipe = import uiui.swipe(); if (const swipe: anyswipe === 'left') { this.Demo.camX: numbercamX += const SWIPE_DISTANCE: 320SWIPE_DISTANCE; } else if (const swipe: anyswipe === 'right') { this.Demo.camX: numbercamX -= const SWIPE_DISTANCE: 320SWIPE_DISTANCE; } else if (const swipe: anyswipe === 'up') { this.Demo.camY: numbercamY += const SWIPE_DISTANCE: 320SWIPE_DISTANCE; } else if (const swipe: anyswipe === 'down') { this.Demo.camY: numbercamY -= const SWIPE_DISTANCE: 320SWIPE_DISTANCE; } } /** * Draws the 1D strip, the tile grid, and the panels. */ Demo.render(): void
Draws the 1D strip, the tile grid, and the panels.
render
() {
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.clear: (paletteIndex: number) => void
Sets the frame clear color using a palette index. The renderer uses this color when clearing the full display at the start of the next frame.
@since0.1.0@parampaletteIndex - Palette index for the full-screen clear pass.
clear
(const C_BG: 1C_BG);
this.Demo.renderStrip(): void
Draws one bar per screen column, using the 1D hash.
renderStrip
();
this.Demo.renderGrid(): void
Draws every tile currently on screen, working each one out from its position.
renderGrid
();
import uiui.caption(8, 8, 'hash1i: one number in', { color: stringcolor: 'dim' }); import uiui.caption(8, const STRIP_Y: 28STRIP_Y + const STRIP_H: 44STRIP_H + 8, 'hash2i and hash3i: a whole world', { color: stringcolor: 'dim' }); this.Demo.renderControlPanel(): void
The travel buttons.
renderControlPanel
();
this.Demo.renderReadoutPanel(): void
Where we are, what layer we are on, and the number that matters most.
renderReadoutPanel
();
// The D-pad draws itself and appears once the demo has seen a touch. import uiui.dpadWidget(); } /** * Draws one bar per screen column, using the 1D hash. */ Demo.renderStrip(): void
Draws one bar per screen column, using the 1D hash.
renderStrip
() {
for (let let sx: numbersx = 0; let sx: numbersx < const DISPLAY_W: 640DISPLAY_W; let sx: numbersx++) { // The bar belongs to a world column, not a screen column, so it scrolls with the // view instead of sitting still. Math.floor keeps it a whole number even when the // camera is at a negative position. const const worldColumn: anyworldColumn = Math.floor(this.Demo.camX: numbercamX) + let sx: numbersx; // Same trick as the tiles: scramble the coordinate, then squash the huge result // down to a height that fits the strip. const const height: numberheight = function hash1i(x: number, seed?: number): number
Deterministic uint32 hash of a 1D integer coordinate.
@paramx - Coordinate (truncated toward zero with `| 0`).@paramseed - World / stream seed (default `0`; lower 32 bits used).@returnsUnsigned 32-bit value in `[0, 2^32)`.@since1.5.0
hash1i
(const worldColumn: anyworldColumn, const WORLD_SEED: 20260730WORLD_SEED) % const STRIP_H: 44STRIP_H;
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.drawLine: (p0: Vector2i, p1: Vector2i, paletteIndex: number) => void
Draws a pixel-perfect line between two points. Uses rasterized line drawing without antialiasing.
@since0.1.0@paramp0 - Start position in display coordinates.@paramp1 - End position in display coordinates.@parampaletteIndex - Palette color index.
drawLine
(new new Vector2i(x?: number, y?: number): Vector2i
Creates an integer 2D vector, truncating inputs toward zero.
@paramx - Horizontal component (defaults to 0).@paramy - Vertical component (defaults to 0).
Vector2i
(let sx: numbersx, const STRIP_Y: 28STRIP_Y + const STRIP_H: 44STRIP_H), 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
(let sx: numbersx, const STRIP_Y: 28STRIP_Y + const STRIP_H: 44STRIP_H - const height: numberheight), const C_STRIP: 2C_STRIP);
}
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.drawLine: (p0: Vector2i, p1: Vector2i, paletteIndex: number) => void
Draws a pixel-perfect line between two points. Uses rasterized line drawing without antialiasing.
@since0.1.0@paramp0 - Start position in display coordinates.@paramp1 - End position in display coordinates.@parampaletteIndex - Palette color index.
drawLine
(new new Vector2i(x?: number, y?: number): Vector2i
Creates an integer 2D vector, truncating inputs toward zero.
@paramx - Horizontal component (defaults to 0).@paramy - Vertical component (defaults to 0).
Vector2i
(0, const STRIP_Y: 28STRIP_Y + const STRIP_H: 44STRIP_H + 1), 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 DISPLAY_W: 640DISPLAY_W - 1, const STRIP_Y: 28STRIP_Y + const STRIP_H: 44STRIP_H + 1), const C_DIM: 4C_DIM);
} /** * Draws every tile currently on screen, working each one out from its position. */ Demo.renderGrid(): void
Draws every tile currently on screen, working each one out from its position.
renderGrid
() {
// Which tile is under the top-left corner of the view, and how far into that tile we // are. The leftover is what makes scrolling look smooth instead of jumping a whole // tile at a time. const const firstTileX: anyfirstTileX = Math.floor(this.Demo.camX: numbercamX / const TILE: 32TILE); const const firstTileY: anyfirstTileY = Math.floor(this.Demo.camY: numbercamY / const TILE: 32TILE); const const offsetX: numberoffsetX = this.Demo.camX: numbercamX - const firstTileX: anyfirstTileX * const TILE: 32TILE; const const offsetY: numberoffsetY = this.Demo.camY: numbercamY - const firstTileY: anyfirstTileY * const TILE: 32TILE; // One extra column and row so the partly-visible tiles at the edges still get drawn. const const cols: anycols = Math.ceil(const DISPLAY_W: 640DISPLAY_W / const TILE: 32TILE) + 1; const const rows: anyrows = Math.ceil(const GRID_H: 400GRID_H / const TILE: 32TILE) + 1; // The grid is a window cut into the middle of the screen, not the whole screen, so // the rows at its top and bottom have to be trimmed to fit. Skipping this lets tiles // spill over the frame and cover the caption above and the panels below. const const gridBottom: numbergridBottom = const GRID_Y: 90GRID_Y + const GRID_H: 400GRID_H; for (let let row: numberrow = 0; let row: numberrow < const rows: anyrows; let row: numberrow++) { const const tileTop: numbertileTop = const GRID_Y: 90GRID_Y + let row: numberrow * const TILE: 32TILE - const offsetY: numberoffsetY; // Keep only the slice of this row that lands inside the window. const const visibleTop: anyvisibleTop = Math.max(const tileTop: numbertileTop, const GRID_Y: 90GRID_Y); const const visibleH: numbervisibleH = Math.min(const tileTop: numbertileTop + const TILE: 32TILE, const gridBottom: numbergridBottom) - const visibleTop: anyvisibleTop; // A row that ended up entirely outside the window has nothing to draw. if (const visibleH: numbervisibleH <= 0) { continue; } for (let let col: numbercol = 0; let col: numbercol < const cols: anycols; let col: numbercol++) { const const tx: anytx = const firstTileX: anyfirstTileX + let col: numbercol; const const ty: anyty = const firstTileY: anyfirstTileY + let row: numberrow; const const screenX: numberscreenX = let col: numbercol * const TILE: 32TILE - const offsetX: numberoffsetX; // Left and right need no such care: those edges are the screen itself, and the // engine already ignores whatever hangs off it.
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.drawRectFill: (rect: Rect2i, paletteIndex: number) => void
Draws a filled rectangle.
@since0.1.0@paramrect - Rectangle bounds in display coordinates.@parampaletteIndex - Palette color index.
drawRectFill
(new new Rect2i(x?: number, y?: number, width?: number, height?: number): Rect2i
Creates an integer rectangle, truncating all inputs toward zero.
@paramx - Left-edge X coordinate (defaults to 0).@paramy - Top-edge Y coordinate (defaults to 0).@paramwidth - Width in pixels (defaults to 0).@paramheight - Height in pixels (defaults to 0).
Rect2i
(const screenX: numberscreenX, const visibleTop: anyvisibleTop, const TILE: 32TILE, const visibleH: numbervisibleH), const C_TERRAIN_BASE: 10C_TERRAIN_BASE + function terrainAt(tx: number, ty: number): number
Works out what kind of ground sits at a tile, using nothing but the tile's position. There is no list of tiles anywhere in this demo. This function is the world: hand it a position and it hands back the ground, the same answer every time, forever.
@paramtx - Tile column. Can be any whole number, including huge and negative ones.@paramty - Tile row.@returnsOne of the TERRAIN_* values.
terrainAt
(const tx: anytx, const ty: anyty));
// The decoration is the only thing that knows about the layer. hash3i takes a // third coordinate, so changing the layer gives a completely different answer // for the very same tile - while the ground underneath, which came from // hash2i, does not budge. // // A dot is too small to be worth trimming, so one that would poke outside the // window is simply left out. const const dotY: numberdotY = const tileTop: numbertileTop + 12; if (const dotY: numberdotY >= const GRID_Y: 90GRID_Y && const dotY: numberdotY + 6 <= const gridBottom: numbergridBottom && function hash3i(x: number, y: number, z: number, seed?: number): number
Deterministic uint32 hash of a 3D integer coordinate.
@paramx - X coordinate (truncated toward zero with `| 0`).@paramy - Y coordinate (truncated toward zero with `| 0`).@paramz - Z coordinate (truncated toward zero with `| 0`).@paramseed - World / stream seed (default `0`; lower 32 bits used).@returnsUnsigned 32-bit value in `[0, 2^32)`.@since1.5.0
hash3i
(const tx: anytx, const ty: anyty, this.Demo.layer: numberlayer, const WORLD_SEED: 20260730WORLD_SEED) % 100 < 20) {
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.drawRectFill: (rect: Rect2i, paletteIndex: number) => void
Draws a filled rectangle.
@since0.1.0@paramrect - Rectangle bounds in display coordinates.@parampaletteIndex - Palette color index.
drawRectFill
(new new Rect2i(x?: number, y?: number, width?: number, height?: number): Rect2i
Creates an integer rectangle, truncating all inputs toward zero.
@paramx - Left-edge X coordinate (defaults to 0).@paramy - Top-edge Y coordinate (defaults to 0).@paramwidth - Width in pixels (defaults to 0).@paramheight - Height in pixels (defaults to 0).
Rect2i
(const screenX: numberscreenX + 12, const dotY: numberdotY, 6, 6), const C_ROCK_DOT: 5C_ROCK_DOT);
} } } // A frame around the grid, drawn last so it sits on top of the tiles.
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.drawRect: (rect: Rect2i, paletteIndex: number) => void
Draws an unfilled rectangle outline.
@since0.1.0@paramrect - Rectangle bounds in display coordinates.@parampaletteIndex - Palette color index.
drawRect
(new new Rect2i(x?: number, y?: number, width?: number, height?: number): Rect2i
Creates an integer rectangle, truncating all inputs toward zero.
@paramx - Left-edge X coordinate (defaults to 0).@paramy - Top-edge Y coordinate (defaults to 0).@paramwidth - Width in pixels (defaults to 0).@paramheight - Height in pixels (defaults to 0).
Rect2i
(0, const GRID_Y: 90GRID_Y, const DISPLAY_W: 640DISPLAY_W, const GRID_H: 400GRID_H), const C_DIM: 4C_DIM);
} /** * The travel buttons. */ Demo.renderControlPanel(): void
The travel buttons.
renderControlPanel
() {
import uiui.begin(import UI_ANCHORSUI_ANCHORS.BOTTOM_LEFT); import uiui.panel('Travel'); if (import uiui.button('Jump far', { key: stringkey: 'KeyJ' })) { // Thousands of tiles in one step. A world held in memory could not do this - there // would be nothing out there yet. Here there is nothing to prepare. this.Demo.camX: numbercamX += const JUMP_DISTANCE: 128000JUMP_DISTANCE; this.Demo.camY: numbercamY += const JUMP_DISTANCE: 128000JUMP_DISTANCE; } if (import uiui.button('Home', { key: stringkey: 'KeyH' })) { // Coming home proves the point: the tiles are exactly as they were, because they // were never stored and never had a chance to drift. this.Demo.camX: numbercamX = 0; this.Demo.camY: numbercamY = 0; } import uiui.label('Arrows, D-pad, or swipe', { color: stringcolor: 'dim' }); import uiui.end(); } /** * Where we are, what layer we are on, and the number that matters most. */ Demo.renderReadoutPanel(): void
Where we are, what layer we are on, and the number that matters most.
renderReadoutPanel
() {
import uiui.begin(import UI_ANCHORSUI_ANCHORS.BOTTOM_RIGHT); import uiui.panel('This world'); import uiui.kv('tile x', Math.floor(this.Demo.camX: numbercamX / const TILE: 32TILE)); import uiui.kv('tile y', Math.floor(this.Demo.camY: numbercamY / const TILE: 32TILE)); // The headline. However far you travel, this never moves off zero. import uiui.kv('stored', '0 tiles'); this.Demo.layer: numberlayer = Math.round(import uiui.slider('Layer', this.Demo.layer: numberlayer, { min: numbermin: 0, max: numbermax: const LAYER_MAX: 8LAYER_MAX })); import uiui.separator(); import uiui.label('Ground ignores the layer.', { color: stringcolor: 'dim' }); import uiui.label('Rocks do not.', { color: stringcolor: 'dim' }); import uiui.end(); } } function bootstrap(DemoClass: DemoConstructor, options?: BootstrapOptions): Promise<boolean>
One-liner bootstrap function for BLIT386 demos. Handles canvas retrieval and engine initialization. Backend selection (WebGPU or software fallback) is managed internally by BTAPI. This function provides a streamlined way to start a demo with sensible defaults while allowing customization through options.
@since0.2.0@changed1.4.0 Calling `bootstrap()` again while already initialized now routes to a hot swap (via {@link registerHotReload}) when a Vite HMR context is registered, or logs a double-bootstrap guard and returns `false` otherwise - previously it silently started a second, unstoppable `GameLoop`.@changed1.7.0 Exposes `BT` on `window.BT` after bootstrap finishes, gated by {@link BootstrapOptions.exposeGlobal} (default: {@link BT.isDevMode}).@paramDemoClass - Demo class constructor implementing `IBTDemo` (optional `configure()` for hardware settings).@paramoptions - Optional configuration for IDs and callbacks.@returns`true` when the demo boots successfully; otherwise `false`.@example// Simplest usage - uses default IDs. bootstrap(MyDemo);@example// With custom options. bootstrap(MyDemo, { canvasID: 'custom-canvas', containerID: 'custom-container', onSuccess: () => console.log('Demo started!'), onError: (err) => analytics.trackError(err), });@example// Await the result. const success = await bootstrap(MyDemo); if (success) { console.log('Demo is running'); }
bootstrap
(class Demo
An endless scrollable world computed from coordinates, storing nothing.
@implementsIBTDemo
Demo
);