/*

Sprites: draw images from a sprite sheet and transform them on the way to the screen.
@description Draw sprites from a sheet: pick cells, recolor, flip, rotate, scale, pivot, and stretch to any size.

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/sprites

Guides:
  - Palette (index sprites): https://blit386.dev/docs/guides/palette#index-sprites-preferred-and-manual-flows
  - Palette (per-draw offsets): https://blit386.dev/docs/guides/palette#per-draw-palette-offsets-zero-cost-color-variants

Reference:
  - Rendering (sprites): https://blit386.dev/docs/api/rendering#flipping-rotating-and-scaling-sprites
  - Rendering (tiles): https://blit386.dev/docs/api/rendering#drawing-tiles
  - Assets: https://blit386.dev/docs/api/assets#loading-assets
  - Palette: https://blit386.dev/docs/api/palette#fill-a-block-of-slots

---

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

A 640x400 screen on the UI kit's navy background, split into captioned sections:

- Top-left: six shapes, each loaded from its own 16x16 PNG - Square, Circle, Tri, Star,
  Heart, Gem.
- Below them: the same star four times, recolored by palette offset - Original, Fire, Ice,
  Void.
- Below that: the triangle facing Up, Right, Down, and Left using flip and rotate flags,
  then a fifth triangle with a Turn button beside it.
- Bottom-left: the Pivot section. Four triangles planted by their center on marked dots
  (small white squares), then two pairs comparing pivot (0, 0) with no pivot.
- Top-right: the Scale section. Stars at 1x, 2x, 3x, and 4x standing on one baseline, then
  uneven scales - Vector2i(3, 1), Vector2i(1, 3), and a quarter-turned triangle at (3, 1).
- Lower right: the Stretch section. A heart squeezed into a box whose width swings between
  20 and 200 pixels, next to a fixed 30x50 heart.
- Along the bottom: a full-width code panel showing how a real PNG would be loaded.

The engine overlay is turned off in this demo (isOverlayEnabled: false).

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

- A "sprite" is a 2D image used in a game - a character, a coin, an enemy. A "sprite
  sheet" is one big image holding many small sprites in a grid. You draw one by telling
  the engine which part of the sheet to copy.
- BT.drawSprite(sheet, srcRect, dest, params) copies the Rect2i source rectangle to dest.
  BT.drawTile(sheet, cell, pos, paletteOffset) does the same by cell number once the sheet
  knows its cell size (sheet.tileSize); sheet.tileRect(cell) gives the matching Rect2i.
- Loading PNGs with SpriteSheet.load(), registering their colors in the palette with
  SpriteSheet.loadColorsIntoPalette(), and calling sheet.indexize(palette) so every pixel
  becomes a palette slot number.
- palette.fillBlock(start, source, transform) writes a recolored copy of a color block;
  a palette offset then picks that block at draw time.
- The params object: flags (BT.FLIP_H, BT.FLIP_V, BT.ROT_90_CW, BT.ROT_180_CW,
  BT.ROT_270_CW, combined with |), scale (a whole number or a Vector2i), pivot (a Vector2i
  in source pixels), and paletteOffset. One object is created once and reused.
- A Rect2i destination stretches the sprite to any size; pivot must be undefined and scale
  must be 1 there.
- UI kit pieces: ui.caption() for labels, ui.button() with a { key } binding, and
  ui.tick() as the first line of update() so the key press is never missed.

HOW PALETTE OFFSETS WORK FOR SPRITES
------------------------------------

After sheet.indexize(palette), each pixel in the sprite is stored as a palette slot
number. When you draw the sprite:

  BT.drawTile(sheet, cell, pos, 0)           - uses the original colors
  BT.drawTile(sheet, cell, pos, colorCount)  - shifts every pixel's slot up by colorCount

BT.drawSprite() takes the same number as its 4th argument, or as params.paletteOffset
when you pass a params object. If the original colors sit in slots 10-14, an offset of 5
makes every pixel use slots 15-19 instead - a completely different color theme from the
same pixels. This is how retro games did "team colors" and environmental lighting.

This demo loads six tiny PNGs, one per shape, and shares one palette between them. The
one-call version for a single PNG looks like this:

  await SpriteSheet.loadIndexed('/sprites/hero.png', palette, startSlot)

The Palette Presets demo explores the palette system in depth:
https://demos.blit386.dev/palette-presets

TRY THIS
--------

- Click or tap the Turn button, or press Space, to flip the fifth triangle between facing
  right and facing left.
- Watch the stretched heart: its box width changes every frame, yet the edges stay crisp,
  because each screen pixel copies exactly one sprite pixel - no blur.
- Compare the two Pivot pairs: with no pivot the sprite starts at the dot; with pivot
  (0, 0) the flag has moved the source's top-left corner, so the sprite lands on the other
  side of the dot.

*/

import { function bootstrap(DemoClass: DemoConstructor, options?: BootstrapOptions): Promise<boolean>
One-liner bootstrap function for BLIT386 demos. Handles canvas retrieval and engine initialization. Backend selection (WebGPU or software fallback) is managed internally by BTAPI. This function provides a streamlined way to start a demo with sensible defaults while allowing customization through options.
@since0.2.0@changed1.4.0 Calling `bootstrap()` again while already initialized now routes to a hot swap (via {@link registerHotReload}) when a Vite HMR context is registered, or logs a double-bootstrap guard and returns `false` otherwise - previously it silently started a second, unstoppable `GameLoop`.@changed1.7.0 Exposes `BT` on `window.BT` after bootstrap finishes, gated by {@link BootstrapOptions.exposeGlobal} (default: {@link BT.isDevMode}).@paramDemoClass - Demo class constructor implementing `IBTDemo` (optional `configure()` for hardware settings).@paramoptions - Optional configuration for IDs and callbacks.@returns`true` when the demo boots successfully; otherwise `false`.@example// Simplest usage - uses default IDs. bootstrap(MyDemo);@example// With custom options. bootstrap(MyDemo, { canvasID: 'custom-canvas', containerID: 'custom-container', onSuccess: () => console.log('Demo started!'), onError: (err) => analytics.trackError(err), });@example// Await the result. const success = await bootstrap(MyDemo); if (success) { console.log('Demo is running'); }
bootstrap
,
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
, class Color32
Mutable 32-bit RGBA color value with 8-bit channels.
@since0.1.0
Color32
, class Rect2i
Integer rectangle for pixel-perfect bounds and regions. Used throughout the engine for sprite regions, display-space bounds, and zero-allocation geometry helpers. Both convenience getters and allocation-free `*To()` helpers are provided so callers can choose between readability and hot-path efficiency.
@since0.1.0
Rect2i
,
type SpriteSheet = SpriteSheet
class SpriteSheet
Sprite-sheet wrapper around a loaded image asset. The class keeps the original image available for CPU-side inspection while lazily creating and caching a GPU texture for rendering. When possible, `load()` also pre-decodes the source into an `ImageBitmap` so texture uploads preserve pixel-art alpha and color values more reliably. After calling `indexize()`, the sheet stores palette indices rather than RGBA data. The GPU texture becomes an `r8uint` format uploaded via `writeTexture`. The original RGBA bytes are retained so `reindexize()` can re-convert without reloading the image.
@since0.1.0
SpriteSheet
, class Vector2i
Integer 2D vector for pixel-perfect positioning. Used for points, sizes, directions, and camera offsets throughout the engine. The API includes both allocation-free `*To()` / `*InPlace()` variants and convenience methods that return new vectors.
@since0.1.0
Vector2i
} from 'blit386';
import { import applyThemeapplyTheme, import uiui, import UI_ANCHORSUI_ANCHORS } from './shared/ui.js'; /** @typedef {import('blit386').Palette} Palette */ /** @typedef {import('blit386').SpriteSheet} SpriteSheet */ // Where in the palette the sprite's original colors start. The sprites use two colors // (fill and stroke), and the three recolored blocks below stack up to slot 17, so // everything above that stays free for the shared UI theme (slots 240-251). const const COLOR_BASE: 10COLOR_BASE = 10; // Every shape is its own 16x16 PNG, so each one is a small sprite sheet of one cell. const const SHAPE_CELL: 16SHAPE_CELL = 16; // The six shapes: the caption under each one and the PNG that holds it. This single list // drives both the loader in init() and the captions in render(). The captions sit 50 // pixels apart, so the longer names are shortened ('Triangle', 'Gem') to keep each label // inside its column. const const SHAPES: {}SHAPES = [ { name: stringname: 'Square', url: stringurl: '/sprites/square-16.png' }, { name: stringname: 'Circle', url: stringurl: '/sprites/circle-16.png' }, { name: stringname: 'Triangle', url: stringurl: '/sprites/triangle-16.png' }, { name: stringname: 'Star', url: stringurl: '/sprites/star-16.png' }, { name: stringname: 'Heart', url: stringurl: '/sprites/heart-16.png' }, { name: stringname: 'Gem', url: stringurl: '/sprites/gem-16.png' }, ]; // Position of the star in SHAPES. The palette-offset row below the shape grid draws it // four times. const const STAR_SHAPE: 3STAR_SHAPE = 3; // Position of the triangle - the one shape that looks different when turned. const const TRIANGLE_SHAPE: 2TRIANGLE_SHAPE = 2; // The triangle points up in the sheet. These flags turn it to face each way. Each flag is // one bit of a number, so several can be combined with | ("bitwise or", which keeps every // bit that is set on either side). The engine always applies them in the same order, // whichever way round you write them: flips first, then the turn. So // BT.ROT_90_CW | BT.FLIP_V mirrors the triangle top to bottom, then turns it a quarter // turn clockwise. const const FACINGS: {}FACINGS = [ { name: stringname: 'Up', flags: numberflags: 0 }, { name: stringname: 'Right', flags: numberflags:
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
.type ROT_90_CW: number
Rotate 90° clockwise flag for sprite rendering. Pass in {@link SpriteDrawParams.flags } ; combine with the other `FLIP_*` / `ROT_*` bits using `|`.
@since0.1.0@changed1.8.0 Accepted by `BT.drawSprite` through `SpriteDrawParams.flags`.
ROT_90_CW
},
{ name: stringname: 'Down', flags: numberflags:
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
.type ROT_180_CW: number
Rotate 180° flag for sprite rendering. Pass in {@link SpriteDrawParams.flags } ; combine with the other `FLIP_*` / `ROT_*` bits using `|`.
@since0.1.0@changed1.8.0 Accepted by `BT.drawSprite` through `SpriteDrawParams.flags`.
ROT_180_CW
},
{ name: stringname: 'Left', flags: numberflags:
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
.type ROT_270_CW: number
Rotate 270° clockwise flag for sprite rendering. Pass in {@link SpriteDrawParams.flags } ; combine with the other `FLIP_*` / `ROT_*` bits using `|`.
@since0.1.0@changed1.8.0 Accepted by `BT.drawSprite` through `SpriteDrawParams.flags`.
ROT_270_CW
},
]; // Display size for this demo. 640x400 is double the usual 320-pixel width, which leaves // room for the scale and stretch sections next to the original rows. const const DISPLAY_W: 640DISPLAY_W = 640; const const DISPLAY_H: 400DISPLAY_H = 400; // Full width of the bottom code strip: ui.begin() keeps its default 4-pixel margin on // each side. const const CODE_PANEL_WIDTH: numberCODE_PANEL_WIDTH = const DISPLAY_W: 640DISPLAY_W - 8; // Position of the heart - the stretch example squeezes it into a changing box. const const HEART_SHAPE: 4HEART_SHAPE = 4; // Whole-number scales drawn side by side in the Scale section. const const UNIFORM_SCALES: {}UNIFORM_SCALES = [1, 2, 3, 4]; // Where the Pivot section sits: its title row, the y of the planted triangles' marked // dots, and the row where the pivot (0, 0) comparison is drawn. const const PIVOT_TOP: 205PIVOT_TOP = 205; const const PIVOT_PLANTED_Y: 233PIVOT_PLANTED_Y = 233; const const PIVOT_CORNER_Y: 280PIVOT_CORNER_Y = 280; // Part 1 of the Pivot section: the same triangle, drawn with a different flag and scale // each time. const const PLANTED_POSES: {}PLANTED_POSES = [ { flags: numberflags: 0, scale: numberscale: 1 }, { flags: numberflags:
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
.type ROT_90_CW: number
Rotate 90° clockwise flag for sprite rendering. Pass in {@link SpriteDrawParams.flags } ; combine with the other `FLIP_*` / `ROT_*` bits using `|`.
@since0.1.0@changed1.8.0 Accepted by `BT.drawSprite` through `SpriteDrawParams.flags`.
ROT_90_CW
, scale: numberscale: 1 },
{ flags: numberflags:
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
.type ROT_180_CW: number
Rotate 180° flag for sprite rendering. Pass in {@link SpriteDrawParams.flags } ; combine with the other `FLIP_*` / `ROT_*` bits using `|`.
@since0.1.0@changed1.8.0 Accepted by `BT.drawSprite` through `SpriteDrawParams.flags`.
ROT_180_CW
, scale: numberscale: 2 },
{ flags: numberflags:
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
.type FLIP_H: number
Horizontal flip flag for sprite rendering. Pass in {@link SpriteDrawParams.flags } ; combine with the other `FLIP_*` / `ROT_*` bits using `|`.
@since0.1.0@changed1.8.0 Accepted by `BT.drawSprite` through `SpriteDrawParams.flags`.
FLIP_H
, scale: numberscale: 2 },
]; // Part 2: the flags under which pivot (0, 0) and no pivot are compared. const const PIVOT_COMPARISONS: {}PIVOT_COMPARISONS = [ { name: stringname: 'FLIP_H', flags: numberflags:
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
.type FLIP_H: number
Horizontal flip flag for sprite rendering. Pass in {@link SpriteDrawParams.flags } ; combine with the other `FLIP_*` / `ROT_*` bits using `|`.
@since0.1.0@changed1.8.0 Accepted by `BT.drawSprite` through `SpriteDrawParams.flags`.
FLIP_H
},
{ name: stringname: 'ROT_90_CW', flags: numberflags:
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
.type ROT_90_CW: number
Rotate 90° clockwise flag for sprite rendering. Pass in {@link SpriteDrawParams.flags } ; combine with the other `FLIP_*` / `ROT_*` bits using `|`.
@since0.1.0@changed1.8.0 Accepted by `BT.drawSprite` through `SpriteDrawParams.flags`.
ROT_90_CW
},
]; // Sprite sheets, cell picking, palette offsets, flip and rotate flags, scale, pivot, and // stretching into a Rect2i - all on one 640x400 screen. class class DemoDemo { /** @type {Palette | null} */ Demo.palette: Palette | null
@type{Palette | null}
palette
= null;
// One sheet per shape, in SHAPES order. Each is a single 16x16 cell. /** @type {SpriteSheet[]} */ Demo.sheets: {}
@type{SpriteSheet[]}
sheets
= [];
// Slot map for the shared UI kit theme, filled in init() by applyTheme(). theme.bg, // theme.text, and friends are palette indices for our own drawing. Demo.theme: nulltheme = null; // How many distinct colors the sprites use (two), and the Color32 list behind them. Demo.colorCount: numbercolorCount = 0; Demo.baseColors: {}baseColors = []; // The sheets the scale, flag, and stretch sections draw from, picked out of // this.sheets once in init(). Demo.starSheet: nullstarSheet = null; Demo.triangleSheet: nulltriangleSheet = null; Demo.heartSheet: nullheartSheet = null; // Source rect of the triangle sheet, built once in init() (sheet.tileRect makes a new // Rect2i each call, so keep it out of render()). Demo.triangleRect: nulltriangleRect = null; // Source rects for the scale and stretch sections, built once in init(). Demo.starRect: nullstarRect = null; Demo.heartRect: nullheartRect = null; // Uneven scales, built once: a Vector2i scale stretches the width and height by // different whole numbers. Demo.wideScale: Vector2iwideScale = 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
(3, 1);
Demo.tallScale: Vector2itallScale = 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
(1, 3);
// The animated stretch box. render() changes only its width each frame - no new // Rect2i per frame. Demo.stretchBox: Rect2istretchBox = 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
(360, 260, 20, 40);
// A fixed, uneven stretch to compare against the animated one: 16x16 into 30x50. Demo.fixedStretchBox: Rect2ifixedStretchBox = 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
(560, 252, 30, 50);
// Pivot points are in source pixels, measured from the source rect's top-left, before // any flags. Both are built once in init(): centerPivot is the middle of the 16x16 // cell, cornerPivot is its top-left corner (the same corner as no pivot - until a // flag moves it). Demo.centerPivot: nullcenterPivot = null; Demo.cornerPivot: nullcornerPivot = null; // Scratch values for the Pivot section: a dest point and a small square that marks // it. render() rewrites them with set() instead of creating new ones every frame. Demo.pivotDest: Vector2ipivotDest = new new Vector2i(x?: number, y?: number): Vector2i
Creates an integer 2D vector, truncating inputs toward zero.
@paramx - Horizontal component (defaults to 0).@paramy - Vertical component (defaults to 0).
Vector2i
(0, 0);
Demo.pivotMark: Rect2ipivotMark = new new Rect2i(x?: number, y?: number, width?: number, height?: number): Rect2i
Creates an integer rectangle, truncating all inputs toward zero.
@paramx - Left-edge X coordinate (defaults to 0).@paramy - Top-edge Y coordinate (defaults to 0).@paramwidth - Width in pixels (defaults to 0).@paramheight - Height in pixels (defaults to 0).
Rect2i
(0, 0, 3, 3);
// One reusable params object for BT.drawSprite. Create it with every field present // (flags, pivot, scale, paletteOffset) and rewrite the fields between draws - the // engine reads it during the call and never keeps it. pivot: undefined means "place // by the top-left".
Demo.drawParams: {
    flags: number;
    pivot: undefined;
    scale: number;
    paletteOffset: number;
}
drawParams
= { flags: numberflags: 0, pivot: undefinedpivot: var undefinedundefined, scale: numberscale: 1, paletteOffset: numberpaletteOffset: 0 };
// Which way the turnable triangle faces; the Turn button (or Space) toggles it in // render(). Demo.isFacingLeft: booleanisFacingLeft = false; // A wider 640x400 screen, no engine overlay, and Space reserved for the Turn button.
Demo.configure(): {
    displaySize: Vector2i;
    isOverlayEnabled: boolean;
    isCapturingKeyboardScroll: boolean;
}
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: 400DISPLAY_H),
isOverlayEnabled: booleanisOverlayEnabled: false, // Space is the Turn button's shortcut. Without this, the browser also treats // Space as "scroll the page down", so every press would jump the page. isCapturingKeyboardScroll: booleanisCapturingKeyboardScroll: true, }; } // Loads the six shape PNGs, registers their colors in the palette, and calls // sheet.indexize() on each. async Demo.init(): unknowninit() { console.log('[SpriteDemo] Initializing...'); this.Demo.palette: Palette | null
@type{Palette | null}
palette
=
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.paletteCreate: (size?: number) => Palette
Creates a standalone palette instance.
@since1.0.3@paramsize - Palette size. Defaults to 256 colors.@returnsNew mutable palette.
paletteCreate
(256);
// Install the shared UI theme: applyTheme() writes the twelve UI kit colors into // high palette slots (240-251), far above this demo's sprite colors (slots // 10-17), and returns a map of friendly names to those slots (this.theme.bg, // .text, ...). Every caption and the code panel draw with these shared colors. this.Demo.theme: nulltheme = import applyThemeapplyTheme(this.Demo.palette: Palette
@type{Palette | null}
palette
);
try { // Read every distinct color off the first PNG into palette slots starting at // COLOR_BASE, and remember them so the recolored blocks can be derived. All // six PNGs use the same two colors, so one pass covers them all. The option // { sort: 'none' } keeps the order the colors first appear in the image. this.Demo.baseColors: {}baseColors = await class SpriteSheet
Sprite-sheet wrapper around a loaded image asset. The class keeps the original image available for CPU-side inspection while lazily creating and caching a GPU texture for rendering. When possible, `load()` also pre-decodes the source into an `ImageBitmap` so texture uploads preserve pixel-art alpha and color values more reliably. After calling `indexize()`, the sheet stores palette indices rather than RGBA data. The GPU texture becomes an `r8uint` format uploaded via `writeTexture`. The original RGBA bytes are retained so `reindexize()` can re-convert without reloading the image.
@since0.1.0
SpriteSheet
.
SpriteSheet.loadColorsIntoPalette(url: string, palette: Palette, startSlot: number, options?: {
    sort?: "luminance" | "none";
}): Promise<Color32[]>
Walks a PNG's pixels and registers every unique opaque color into the supplied palette starting at `startSlot`. Pixels with alpha 0 are skipped - they map to the engine's transparent sentinel slot 0 at draw time. Opaque pixels are deduplicated on RGB and stored with alpha forced to 255, matching the lookup performed by `indexize()` so a subsequent `sheet.indexize(palette)` call resolves without throwing on missing colors. By default colors are sorted darkest-first by perceived luminance ( {@link Color32.luminance } ); pass `{ sort: 'none' }` to keep the row-major scan order of the source image. Image loading goes through {@link AssetLoader.loadImage } , so the call shares cache and in-flight deduplication with {@link SpriteSheet.load } . The destination range is validated before any write, so the palette is never left partially mutated: if the collected colors would not fit (`startSlot < 1` or `startSlot + count > palette.size`), the method throws without touching any slot.
@paramurl - Path or URL to the PNG file.@parampalette - Target palette to populate.@paramstartSlot - First palette slot to write into.@paramoptions - Optional configuration.@paramoptions.sort - Color ordering. Defaults to `'luminance'`.@returnsRegistered colors in palette-write order.@throwsError if the image cannot be loaded.@throwsRangeError if the discovered colors do not fit in the palette starting at `startSlot`.
loadColorsIntoPalette
(const SHAPES: {}SHAPES[0].url, this.Demo.palette: Palette
@type{Palette | null}
palette
, const COLOR_BASE: 10COLOR_BASE, {
sort?: "luminance" | "none" | undefinedsort: 'none', }); this.Demo.colorCount: numbercolorCount = this.Demo.baseColors: {}baseColors.length; const const colorCount: numbercolorCount = this.Demo.colorCount: numbercolorCount; // Build the theme blocks: Fire, Ice, and Void are static once written here. // palette.fillBlock(start, source, transform) writes transform(baseColor) // into one slot per base color, starting at `start` - it replaces a // hand-written for loop. // Fire: push red up and pull blue down. this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.fillBlock(start: number, source: readonly Color32[], transform: (color: Color32, index: number) => Color32): number
Writes a transformed block of colors into contiguous palette slots. Writes `transform(source[i], i)` into slot `start + i` for every `i` in `[0, source.length)`, delegating each write to {@link set } so it inherits {@link set } 's validation, including the rule that slot 0 must stay transparent. Collapses the common pattern of looping `palette.set(start + i, transform(baseColors[i]))` into one call.
@since1.7.0@paramstart - First palette index to write.@paramsource - Source colors to read from, in order. `source[i]` maps to slot `start + i`.@paramtransform - Called once per source color as `transform(color, i)`; its return value is written to slot `start + i`.@returnsThe next free slot after the written block (`start + source.length`), for chaining further writes.@throwsError if `start` is not a non-negative integer.@throwsError if the block would exceed the palette size.@throwsError if an individual slot write is invalid - see {@link set}.
fillBlock
(
const COLOR_BASE: 10COLOR_BASE + const colorCount: numbercolorCount, this.Demo.baseColors: {}baseColors, (base: Color32base) => 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
(Math.min(255, base: Color32base.Color32.r: number
Red channel (0-255).
r
+ 80), base: Color32base.Color32.g: number
Green channel (0-255).
g
, Math.max(0, base: Color32base.Color32.b: number
Blue channel (0-255).
b
- 80)),
); // Ice: pull red down and push blue up. this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.fillBlock(start: number, source: readonly Color32[], transform: (color: Color32, index: number) => Color32): number
Writes a transformed block of colors into contiguous palette slots. Writes `transform(source[i], i)` into slot `start + i` for every `i` in `[0, source.length)`, delegating each write to {@link set } so it inherits {@link set } 's validation, including the rule that slot 0 must stay transparent. Collapses the common pattern of looping `palette.set(start + i, transform(baseColors[i]))` into one call.
@since1.7.0@paramstart - First palette index to write.@paramsource - Source colors to read from, in order. `source[i]` maps to slot `start + i`.@paramtransform - Called once per source color as `transform(color, i)`; its return value is written to slot `start + i`.@returnsThe next free slot after the written block (`start + source.length`), for chaining further writes.@throwsError if `start` is not a non-negative integer.@throwsError if the block would exceed the palette size.@throwsError if an individual slot write is invalid - see {@link set}.
fillBlock
(
const COLOR_BASE: 10COLOR_BASE + const colorCount: numbercolorCount * 2, this.Demo.baseColors: {}baseColors, (base: Color32base) => 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
(Math.max(0, base: Color32base.Color32.r: number
Red channel (0-255).
r
- 60), base: Color32base.Color32.g: number
Green channel (0-255).
g
, Math.min(255, base: Color32base.Color32.b: number
Blue channel (0-255).
b
+ 80)),
); // Void: darken every channel toward black. this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.fillBlock(start: number, source: readonly Color32[], transform: (color: Color32, index: number) => Color32): number
Writes a transformed block of colors into contiguous palette slots. Writes `transform(source[i], i)` into slot `start + i` for every `i` in `[0, source.length)`, delegating each write to {@link set } so it inherits {@link set } 's validation, including the rule that slot 0 must stay transparent. Collapses the common pattern of looping `palette.set(start + i, transform(baseColors[i]))` into one call.
@since1.7.0@paramstart - First palette index to write.@paramsource - Source colors to read from, in order. `source[i]` maps to slot `start + i`.@paramtransform - Called once per source color as `transform(color, i)`; its return value is written to slot `start + i`.@returnsThe next free slot after the written block (`start + source.length`), for chaining further writes.@throwsError if `start` is not a non-negative integer.@throwsError if the block would exceed the palette size.@throwsError if an individual slot write is invalid - see {@link set}.
fillBlock
(
const COLOR_BASE: 10COLOR_BASE + const colorCount: numbercolorCount * 3, this.Demo.baseColors: {}baseColors, (base: Color32base) => 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
(Math.floor(base: Color32base.Color32.r: number
Red channel (0-255).
r
* 0.25), Math.floor(base: Color32base.Color32.g: number
Green channel (0-255).
g
* 0.25), Math.floor(base: Color32base.Color32.b: number
Blue channel (0-255).
b
* 0.25)),
); // Load every PNG and convert its pixels to palette slot numbers. Each file is // one SHAPE_CELL square, so tileSize makes cell 0 the whole image and // BT.drawTile can draw it with no Rect2i bookkeeping. this.Demo.sheets: {}
@type{SpriteSheet[]}
sheets
= await Promise.all(const SHAPES: {}SHAPES.map(({ url: anyurl }) => class SpriteSheet
Sprite-sheet wrapper around a loaded image asset. The class keeps the original image available for CPU-side inspection while lazily creating and caching a GPU texture for rendering. When possible, `load()` also pre-decodes the source into an `ImageBitmap` so texture uploads preserve pixel-art alpha and color values more reliably. After calling `indexize()`, the sheet stores palette indices rather than RGBA data. The GPU texture becomes an `r8uint` format uploaded via `writeTexture`. The original RGBA bytes are retained so `reindexize()` can re-convert without reloading the image.
@since0.1.0
SpriteSheet
.
SpriteSheet.load(url: string, options?: {
    tileSize?: number | Vector2i;
}): Promise<SpriteSheet>
Loads a sprite sheet from an image URL. Attempts to create an `ImageBitmap` with explicit alpha and color-space settings for more predictable GPU uploads. If bitmap creation fails, the instance still works and falls back to uploading the `HTMLImageElement`.
@changed1.8.0 Added `options.tileSize`.@paramurl - Path or URL to the image file.@paramoptions - Optional configuration.@paramoptions.tileSize - Tile grid cell size (see {@link SpriteSheet.tileSize}).@returnsPromise resolving to the loaded SpriteSheet.@throwsRangeError if `options.tileSize` is not a positive integer on both axes.
load
(url: anyurl, { tileSize?: number | Vector2i | undefinedtileSize: const SHAPE_CELL: 16SHAPE_CELL })));
for (const const sheet: anysheet of this.Demo.sheets: {}
@type{SpriteSheet[]}
sheets
) {
const sheet: anysheet.indexize(this.Demo.palette: Palette
@type{Palette | null}
palette
);
} this.Demo.starSheet: nullstarSheet = this.Demo.sheets: {}
@type{SpriteSheet[]}
sheets
[const STAR_SHAPE: 3STAR_SHAPE];
this.Demo.triangleSheet: nulltriangleSheet = this.Demo.sheets: {}
@type{SpriteSheet[]}
sheets
[const TRIANGLE_SHAPE: 2TRIANGLE_SHAPE];
this.Demo.heartSheet: nullheartSheet = this.Demo.sheets: {}
@type{SpriteSheet[]}
sheets
[const HEART_SHAPE: 4HEART_SHAPE];
this.Demo.triangleRect: nulltriangleRect = this.Demo.triangleSheet: nulltriangleSheet.tileRect(0); this.Demo.starRect: nullstarRect = this.Demo.starSheet: nullstarSheet.tileRect(0); this.Demo.heartRect: nullheartRect = this.Demo.heartSheet: nullheartSheet.tileRect(0); this.Demo.centerPivot: nullcenterPivot = 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 SHAPE_CELL: 16SHAPE_CELL / 2, const SHAPE_CELL: 16SHAPE_CELL / 2);
this.Demo.cornerPivot: nullcornerPivot = new new Vector2i(x?: number, y?: number): Vector2i
Creates an integer 2D vector, truncating inputs toward zero.
@paramx - Horizontal component (defaults to 0).@paramy - Vertical component (defaults to 0).
Vector2i
(0, 0);
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.paletteSet: (palette: Palette) => void
Stores the active engine palette. Use this to swap the **entire palette** (e.g. switch between a day and night theme). After this call the renderer uploads the new palette uniform on the next frame. **Palette-value swap (change what a slot looks like):** mutate the live {@link BT.palette } in place with `palette.set(slot, newColor)`. The renderer uploads dirty slots on the next frame; no `paletteSet()` or {@link BT.spritesRefresh } needed. **Palette-layout swap (same colors, different slot positions):** build a new palette with the same colors at new indices, call `paletteSet()`, then call {@link BT.spritesRefresh } so every sprite sheet re-maps its original RGBA pixels against the new slot layout.
@since1.0.3@parampalette - Palette to make active.
paletteSet
(this.Demo.palette: Palette
@type{Palette | null}
palette
);
console.log(`[SpriteDemo] Loaded ${const SHAPES: {}SHAPES.length} shape sheets, ${const colorCount: numbercolorCount} unique colors`); } catch (function (local var) error: unknownerror) { console.error('[SpriteDemo] Failed to load shape sheets:', function (local var) error: unknownerror); return false; } console.log('[SpriteDemo] Initialization complete!'); return true; } // Nothing moves on its own here; update() only feeds the UI kit. Demo.update(): voidupdate() { // Always first: this latches the Turn button's Space shortcut, so a quick key // press is never missed (the button itself is declared in render()). import uiui.tick(); } // Draws every section. The left column holds the shape grid, palette offsets, flags, // and pivot; the right column holds scale and stretch; the code panel runs along the // bottom. Demo.render(): voidrender() { // Clear the whole screen with the shared UI theme's background color.
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.clear: (paletteIndex: number) => void
Sets the frame clear color using a palette index. The renderer uses this color when clearing the full display at the start of the next frame.
@since0.1.0@parampaletteIndex - Palette index for the full-screen clear pass.
clear
(this.Demo.theme: nulltheme.bg);
const
const params: {
    flags: number;
    pivot: undefined;
    scale: number;
    paletteOffset: number;
}
params
= this.
Demo.drawParams: {
    flags: number;
    pivot: undefined;
    scale: number;
    paletteOffset: number;
}
drawParams
;
// Left column. // Row 1: six shapes - each draw call picks a different sheet. const const shapeY: 24shapeY = 24; const const shapeSpacing: 55shapeSpacing = 55; import uiui.caption(12, 11, 'Source rects - one region per shape', { color: stringcolor: 'dim' }); for (let let i: numberi = 0; let i: numberi < const SHAPES: {}SHAPES.length; let i: numberi++) { const const destX: numberdestX = 12 + let i: numberi * const shapeSpacing: 55shapeSpacing;
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
.drawTile: (spriteSheet: SpriteSheet, index: number, destPos: Vector2i, paletteOffset?: number) => void (+2 overloads)drawTile(this.Demo.sheets: {}
@type{SpriteSheet[]}
sheets
[let i: numberi], 0, 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 destX: numberdestX, const shapeY: 24shapeY), 0);
import uiui.caption(const destX: numberdestX, const shapeY: 24shapeY + 24, const SHAPES: {}SHAPES[let i: numberi].name, { color: stringcolor: 'dim' }); } // Row 2: palette offsets on the star shape. Offset n * i shifts every pixel's // slot into the i-th color block (0 = original, 1 = Fire, 2 = Ice, 3 = Void). const const n: numbern = this.Demo.colorCount: numbercolorCount; const const themeY: 90themeY = 90; const const themeSpacing: 72themeSpacing = 72; const const themes: {}themes = ['Original', 'Fire', 'Ice', 'Void']; import uiui.caption(12, const themeY: 90themeY - 13, 'Palette offsets - one star, four color blocks', { color: stringcolor: 'dim' }); for (let let i: numberi = 0; let i: numberi < const themes: {}themes.length; let i: numberi++) { const const destX: numberdestX = 12 + let i: numberi * const themeSpacing: 72themeSpacing;
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
.drawTile: (spriteSheet: SpriteSheet, index: number, destPos: Vector2i, paletteOffset?: number) => void (+2 overloads)drawTile(this.Demo.starSheet: nullstarSheet, 0, 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 destX: numberdestX, const themeY: 90themeY), const n: numbern * let i: numberi);
import uiui.caption(12 + let i: numberi * const themeSpacing: 72themeSpacing, const themeY: 90themeY + 24, const themes: {}themes[let i: numberi], { color: stringcolor: 'dim' }); } // Row 3: flags. The same triangle cell, turned by params.flags instead of being // drawn four times in the sheet. const const facingY: 156facingY = 156; import uiui.caption(12, const facingY: 156facingY - 13, 'Flags - flip and quarter turns', { color: stringcolor: 'dim' });
const params: {
    flags: number;
    pivot: undefined;
    scale: number;
    paletteOffset: number;
}
params
.scale: numberscale = 1;
for (let let i: numberi = 0; let i: numberi < const FACINGS: {}FACINGS.length; let i: numberi++) { const const destX: numberdestX = 12 + let i: numberi * const shapeSpacing: 55shapeSpacing;
const params: {
    flags: number;
    pivot: undefined;
    scale: number;
    paletteOffset: number;
}
params
.flags: numberflags = const FACINGS: {}FACINGS[let i: numberi].flags;
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
.drawSprite: (spriteSheet: SpriteSheet, srcRect: Rect2i, destPos: Vector2i, paletteOffset?: number) => void (+1 overload)drawSprite(this.Demo.triangleSheet: nulltriangleSheet, this.Demo.triangleRect: nulltriangleRect, 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 destX: numberdestX, const facingY: 156facingY),
const params: {
    flags: number;
    pivot: undefined;
    scale: number;
    paletteOffset: number;
}
params
);
import uiui.caption(const destX: numberdestX, const facingY: 156facingY + 24, const FACINGS: {}FACINGS[let i: numberi].name, { color: stringcolor: 'dim' }); } // The turnable one: right or left, with a 20-pixel gap after the four fixed // examples. const const playerX: numberplayerX = 12 + 4 * const shapeSpacing: 55shapeSpacing; // The Turn button sits just right of the 16-pixel-wide triangle. Tapping it, // clicking it, or pressing Space flips which way the triangle faces. import uiui.begin(import UI_ANCHORSUI_ANCHORS.TOP_LEFT, { x: numberx: const playerX: numberplayerX + 16, y: numbery: const facingY: 156facingY - 2 }); if (import uiui.button('Turn', { key: stringkey: 'Space' })) { this.Demo.isFacingLeft: booleanisFacingLeft = !this.Demo.isFacingLeft: booleanisFacingLeft; } import uiui.end(); // ROT_90_CW points the triangle right, ROT_270_CW points it left.
const params: {
    flags: number;
    pivot: undefined;
    scale: number;
    paletteOffset: number;
}
params
.flags: numberflags = this.Demo.isFacingLeft: booleanisFacingLeft ?
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
.type ROT_270_CW: number
Rotate 270° clockwise flag for sprite rendering. Pass in {@link SpriteDrawParams.flags } ; combine with the other `FLIP_*` / `ROT_*` bits using `|`.
@since0.1.0@changed1.8.0 Accepted by `BT.drawSprite` through `SpriteDrawParams.flags`.
ROT_270_CW
:
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
.type ROT_90_CW: number
Rotate 90° clockwise flag for sprite rendering. Pass in {@link SpriteDrawParams.flags } ; combine with the other `FLIP_*` / `ROT_*` bits using `|`.
@since0.1.0@changed1.8.0 Accepted by `BT.drawSprite` through `SpriteDrawParams.flags`.
ROT_90_CW
;
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
.drawSprite: (spriteSheet: SpriteSheet, srcRect: Rect2i, destPos: Vector2i, paletteOffset?: number) => void (+1 overload)drawSprite(this.Demo.triangleSheet: nulltriangleSheet, this.Demo.triangleRect: nulltriangleRect, 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 playerX: numberplayerX, const facingY: 156facingY),
const params: {
    flags: number;
    pivot: undefined;
    scale: number;
    paletteOffset: number;
}
params
);
this.Demo.renderScale(params: any): voidrenderScale(
const params: {
    flags: number;
    pivot: undefined;
    scale: number;
    paletteOffset: number;
}
params
); // right column: scale
this.Demo.renderPivot(params: any): voidrenderPivot(
const params: {
    flags: number;
    pivot: undefined;
    scale: number;
    paletteOffset: number;
}
params
); // left column, below flags: pivot
this.Demo.renderStretch(params: any): voidrenderStretch(
const params: {
    flags: number;
    pivot: undefined;
    scale: number;
    paletteOffset: number;
}
params
); // lower band: stretch to a Rect2i
this.Demo.renderCodeSnippet(): voidrenderCodeSnippet(); } // The Scale section. params.scale takes whole numbers. A plain number scales both // ways; a Vector2i scales width and height separately. Scaling happens after the // flags, in screen directions, so scale.x is always the on-screen width - even for a // sprite turned a quarter turn. Demo.renderScale(params: any): voidrenderScale(params: anyparams) { const const left: 360left = 360; // Every y below hangs off this one line - the bottom edge the 1x-4x stars stand // on - so the whole section moves together if you change it. const const baselineY: 90baselineY = 90; import uiui.caption(const left: 360left, 11, 'Scale - whole numbers only', { color: stringcolor: 'dim' }); // 1x, 2x, 3x, 4x side by side: 16, 32, 48, 64 pixels wide, all sitting on one // baseline. params: anyparams.flags = 0; let let x: numberx = const left: 360left; for (const const s: anys of const UNIFORM_SCALES: {}UNIFORM_SCALES) { params: anyparams.scale = const s: anys;
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
.drawSprite: (spriteSheet: SpriteSheet, srcRect: Rect2i, destPos: Vector2i, paletteOffset?: number) => void (+1 overload)drawSprite(this.Demo.starSheet: nullstarSheet, this.Demo.starRect: nullstarRect, 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 x: numberx, const baselineY: 90baselineY - const SHAPE_CELL: 16SHAPE_CELL * const s: anys), params: anyparams);
import uiui.caption(let x: numberx, const baselineY: 90baselineY + 10, `${const s: anys}x`, { color: stringcolor: 'dim' }); let x: numberx += const SHAPE_CELL: 16SHAPE_CELL * const s: anys + 12; } // Uneven whole-number scales: 3x wide, then 3x tall. params: anyparams.scale = this.Demo.wideScale: Vector2iwideScale;
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
.drawSprite: (spriteSheet: SpriteSheet, srcRect: Rect2i, destPos: Vector2i, paletteOffset?: number) => void (+1 overload)drawSprite(this.Demo.starSheet: nullstarSheet, this.Demo.starRect: nullstarRect, 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 left: 360left, const baselineY: 90baselineY + 36), params: anyparams);
import uiui.caption(const left: 360left, const baselineY: 90baselineY + 60, 'Vector2i(3, 1)', { color: stringcolor: 'dim' }); params: anyparams.scale = this.Demo.tallScale: Vector2itallScale;
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
.drawSprite: (spriteSheet: SpriteSheet, srcRect: Rect2i, destPos: Vector2i, paletteOffset?: number) => void (+1 overload)drawSprite(this.Demo.starSheet: nullstarSheet, this.Demo.starRect: nullstarRect, 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 left: 360left + 100, const baselineY: 90baselineY + 24), params: anyparams);
import uiui.caption(const left: 360left + 100, const baselineY: 90baselineY + 80, 'Vector2i(1, 3)', { color: stringcolor: 'dim' }); // Turned a quarter turn, then 3x wide: still 48 pixels wide on screen. params: anyparams.flags =
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
.type ROT_90_CW: number
Rotate 90° clockwise flag for sprite rendering. Pass in {@link SpriteDrawParams.flags } ; combine with the other `FLIP_*` / `ROT_*` bits using `|`.
@since0.1.0@changed1.8.0 Accepted by `BT.drawSprite` through `SpriteDrawParams.flags`.
ROT_90_CW
;
params: anyparams.scale = this.Demo.wideScale: Vector2iwideScale;
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
.drawSprite: (spriteSheet: SpriteSheet, srcRect: Rect2i, destPos: Vector2i, paletteOffset?: number) => void (+1 overload)drawSprite(this.Demo.triangleSheet: nulltriangleSheet, this.Demo.triangleRect: nulltriangleRect, 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 left: 360left + 160, const baselineY: 90baselineY + 36), params: anyparams);
import uiui.caption(const left: 360left + 160, const baselineY: 90baselineY + 60, 'ROT_90_CW + (3, 1)', { color: stringcolor: 'dim' }); // Leave the shared params object neutral for the next section. params: anyparams.flags = 0; params: anyparams.scale = 1; } // Marks the dest point with a small square so you can see where the sprite is // planted. Demo.markPivotDest(x: any, y: any): voidmarkPivotDest(x: anyx, y: anyy) {
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.drawRectFill: (rect: Rect2i, paletteIndex: number) => void
Draws a filled rectangle.
@since0.1.0@paramrect - Rectangle bounds in display coordinates.@parampaletteIndex - Palette color index.
drawRectFill
(this.Demo.pivotMark: Rect2ipivotMark.Rect2i.set(x: number, y: number, width: number, height: number): Rect2i
Sets all components of this rectangle. Modifies this rectangle directly for maximum performance. WARNING: Mutates this rectangle. Don't use on frozen/cached singletons.
@paramx - New x position.@paramy - New y position.@paramwidth - New width.@paramheight - New height.@returnsThis rectangle for chaining.
set
(x: anyx - 1, y: anyy - 1, 3, 3), this.Demo.theme: nulltheme.text);
} // The Pivot section. Normally dest is where the sprite's top-left lands. params.pivot // picks a different point of the sprite - in source pixels, before flags - to put on // dest instead. Part 1 plants a triangle by its center: flags and scale change, but // the marked point never moves. Part 2 shows that pivot (0, 0) is not the same as no // pivot: it is the source's top-left corner, and a flip or turn moves that corner to // the other side of the sprite. Demo.renderPivot(params: any): voidrenderPivot(params: anyparams) { import uiui.caption(12, const PIVOT_TOP: 205PIVOT_TOP, 'Pivot - place a sprite by a point', { color: stringcolor: 'dim' }); // Part 1: the center pivot. Each triangle uses the same pivot but a different // flag and scale, and each is drawn at its own marked point. params: anyparams.pivot = this.Demo.centerPivot: nullcenterPivot; for (let let i: numberi = 0; let i: numberi < const PLANTED_POSES: {}PLANTED_POSES.length; let i: numberi++) { const const destX: numberdestX = 40 + let i: numberi * 70; params: anyparams.flags = const PLANTED_POSES: {}PLANTED_POSES[let i: numberi].flags; params: anyparams.scale = const PLANTED_POSES: {}PLANTED_POSES[let i: numberi].scale;
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
.drawSprite: (spriteSheet: SpriteSheet, srcRect: Rect2i, destPos: Vector2i, paletteOffset?: number) => void (+1 overload)drawSprite(this.Demo.triangleSheet: nulltriangleSheet, this.Demo.triangleRect: nulltriangleRect, this.Demo.pivotDest: Vector2ipivotDest.Vector2i.set(x: number, y: number): Vector2i
Sets both components of this vector. Modifies this vector directly for maximum performance. WARNING: Mutates this vector. Don't use on frozen/cached singletons.
@paramx - New x component.@paramy - New y component.@returnsThis vector for chaining.
set
(const destX: numberdestX, const PIVOT_PLANTED_Y: 233PIVOT_PLANTED_Y), params: anyparams);
this.Demo.markPivotDest(x: any, y: any): voidmarkPivotDest(const destX: numberdestX, const PIVOT_PLANTED_Y: 233PIVOT_PLANTED_Y); } import uiui.caption(12, const PIVOT_PLANTED_Y: 233PIVOT_PLANTED_Y + 26, 'pivot (8, 8): the dot never moves', { color: stringcolor: 'dim' }); // Part 2: no pivot vs pivot (0, 0), drawn at the same marked point. With no pivot // the sprite's footprint starts at the dot; with (0, 0) the source's top-left // corner is on the dot, and the flag has moved that corner, so the footprint ends // up on the other side. params: anyparams.scale = 1; for (let let i: numberi = 0; let i: numberi < const PIVOT_COMPARISONS: {}PIVOT_COMPARISONS.length; let i: numberi++) { const const destX: numberdestX = 60 + let i: numberi * 130; params: anyparams.flags = const PIVOT_COMPARISONS: {}PIVOT_COMPARISONS[let i: numberi].flags; params: anyparams.pivot = var undefinedundefined;
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
.drawSprite: (spriteSheet: SpriteSheet, srcRect: Rect2i, destPos: Vector2i, paletteOffset?: number) => void (+1 overload)drawSprite(this.Demo.triangleSheet: nulltriangleSheet, this.Demo.triangleRect: nulltriangleRect, this.Demo.pivotDest: Vector2ipivotDest.Vector2i.set(x: number, y: number): Vector2i
Sets both components of this vector. Modifies this vector directly for maximum performance. WARNING: Mutates this vector. Don't use on frozen/cached singletons.
@paramx - New x component.@paramy - New y component.@returnsThis vector for chaining.
set
(const destX: numberdestX, const PIVOT_CORNER_Y: 280PIVOT_CORNER_Y), params: anyparams);
params: anyparams.pivot = this.Demo.cornerPivot: nullcornerPivot;
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
.drawSprite: (spriteSheet: SpriteSheet, srcRect: Rect2i, destPos: Vector2i, paletteOffset?: number) => void (+1 overload)drawSprite(this.Demo.triangleSheet: nulltriangleSheet, this.Demo.triangleRect: nulltriangleRect, this.Demo.pivotDest: Vector2ipivotDest.Vector2i.set(x: number, y: number): Vector2i
Sets both components of this vector. Modifies this vector directly for maximum performance. WARNING: Mutates this vector. Don't use on frozen/cached singletons.
@paramx - New x component.@paramy - New y component.@returnsThis vector for chaining.
set
(const destX: numberdestX, const PIVOT_CORNER_Y: 280PIVOT_CORNER_Y), params: anyparams);
this.Demo.markPivotDest(x: any, y: any): voidmarkPivotDest(const destX: numberdestX, const PIVOT_CORNER_Y: 280PIVOT_CORNER_Y); // Left sprite is the (0, 0) pivot, right sprite is no pivot. const const label: stringlabel = `${const PIVOT_COMPARISONS: {}PIVOT_COMPARISONS[let i: numberi].name}: (0,0) | none`; import uiui.caption(const destX: numberdestX - 40, const PIVOT_CORNER_Y: 280PIVOT_CORNER_Y + 24, const label: stringlabel, { color: stringcolor: 'dim' }); } } // The Stretch section. A Rect2i destination stretches the sprite to any size, even // one that is not a whole multiple. Each screen pixel copies the sprite pixel under // its center, so the result stays crisp - no blur - and looks the same on every // backend. scale must stay 1 here: the box already sets the size. Demo.renderStretch(params: any): voidrenderStretch(params: anyparams) { const const top: numbertop = this.Demo.stretchBox: Rect2istretchBox.Rect2i.y: number
Top edge Y coordinate (defaults to 0).
y
;
import uiui.caption(360, const top: numbertop - 16, 'Stretch to a Rect2i - any size', { color: stringcolor: 'dim' }); // The box width swings between 20 and 200 pixels and back, about every four // seconds. Math.sin() wanders between -1 and +1; adding 1 and halving turns that // into 0 to 1. const const swing: numberswing = (Math.sin(
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.ticks: number
Current fixed-update tick counter. Increments once per engine update. Reset via {@link BT.ticksReset } .
@since1.0.4@returnsCurrent tick count since initialization or last reset.
ticks
/ 40) + 1) / 2;
this.Demo.stretchBox: Rect2istretchBox.Rect2i.width: number
Width in pixels (defaults to 0).
width
= Math.round(20 + const swing: numberswing * 180);
// A Rect2i dest sets the size itself, so a pivot with it throws. Clear it first. params: anyparams.pivot = var undefinedundefined; params: anyparams.flags = 0; params: anyparams.scale = 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
.drawSprite: (spriteSheet: SpriteSheet, srcRect: Rect2i, destPos: Vector2i, paletteOffset?: number) => void (+1 overload)drawSprite(this.Demo.heartSheet: nullheartSheet, this.Demo.heartRect: nullheartRect, this.Demo.stretchBox: Rect2istretchBox, params: anyparams);
import uiui.caption(360, const top: numbertop + 44, `16x16 into ${this.Demo.stretchBox: Rect2istretchBox.Rect2i.width: number
Width in pixels (defaults to 0).
width
}x40`, { color: stringcolor: 'dim' });
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.drawSprite: (spriteSheet: SpriteSheet, srcRect: Rect2i, destPos: Vector2i, paletteOffset?: number) => void (+1 overload)drawSprite(this.Demo.heartSheet: nullheartSheet, this.Demo.heartRect: nullheartRect, this.Demo.fixedStretchBox: Rect2ifixedStretchBox, params: anyparams);
import uiui.caption(this.Demo.fixedStretchBox: Rect2ifixedStretchBox.Rect2i.x: number
Left edge X coordinate (defaults to 0).
x
, const top: numbertop + 44, '30x50', { color: stringcolor: 'dim' });
} // The "how you would load a real PNG" cheat sheet, as a full-width kit panel along // the bottom edge of the screen. Demo.renderCodeSnippet(): voidrenderCodeSnippet() { import uiui.begin(import UI_ANCHORSUI_ANCHORS.BOTTOM_LEFT, { width: numberwidth: const CODE_PANEL_WIDTH: numberCODE_PANEL_WIDTH }); import uiui.panel('Production PNG load:'); import uiui.label("const indexed = await SpriteSheet.loadIndexed('/sprites/test.png', palette, 10);", { color: stringcolor: 'info', }); import uiui.label('const params = { flags: 0, pivot: undefined, scale: 1, paletteOffset: 0 };', { color: stringcolor: 'info' }); 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 DemoDemo);