/*
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.bootstrap, const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT, class Color32Mutable 32-bit RGBA color value with 8-bit channels.Color32, class Rect2iInteger rectangle for pixel-perfect bounds and regions.
Used throughout the engine for sprite regions, display-space bounds, and
zero-allocation geometry helpers. Both convenience getters and allocation-free
`*To()` helpers are provided so callers can choose between readability and
hot-path efficiency.Rect2i, 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.SpriteSheet, class Vector2iInteger 2D vector for pixel-perfect positioning.
Used for points, sizes, directions, and camera offsets throughout the engine.
The API includes both allocation-free `*To()` / `*InPlace()` variants and
convenience methods that return new vectors.Vector2i } from 'blit386';
import { import applyThemeapplyTheme, import uiui, import UI_ANCHORSUI_ANCHORS } from './shared/ui.js';
/** @typedef {import('blit386').Palette} Palette */
/** @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: numberRotate 90° clockwise flag for sprite rendering. Pass in
{@link
SpriteDrawParams.flags
}
; combine with the other `FLIP_*` / `ROT_*` bits using `|`.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: numberRotate 180° flag for sprite rendering. Pass in
{@link
SpriteDrawParams.flags
}
; combine with the other `FLIP_*` / `ROT_*` bits using `|`.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: numberRotate 270° clockwise flag for sprite rendering. Pass in
{@link
SpriteDrawParams.flags
}
; combine with the other `FLIP_*` / `ROT_*` bits using `|`.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: numberRotate 90° clockwise flag for sprite rendering. Pass in
{@link
SpriteDrawParams.flags
}
; combine with the other `FLIP_*` / `ROT_*` bits using `|`.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: numberRotate 180° flag for sprite rendering. Pass in
{@link
SpriteDrawParams.flags
}
; combine with the other `FLIP_*` / `ROT_*` bits using `|`.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: numberHorizontal flip flag for sprite rendering. Pass in
{@link
SpriteDrawParams.flags
}
; combine with the other `FLIP_*` / `ROT_*` bits using `|`.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: numberHorizontal flip flag for sprite rendering. Pass in
{@link
SpriteDrawParams.flags
}
; combine with the other `FLIP_*` / `ROT_*` bits using `|`.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: numberRotate 90° clockwise flag for sprite rendering. Pass in
{@link
SpriteDrawParams.flags
}
; combine with the other `FLIP_*` / `ROT_*` bits using `|`.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 | nullpalette = null;
// One sheet per shape, in SHAPES order. Each is a single 16x16 cell.
/** @type {SpriteSheet[]} */
Demo.sheets: {}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): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(3, 1);
Demo.tallScale: Vector2itallScale = new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.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): Rect2iCreates an integer rectangle, truncating all inputs toward zero.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): Rect2iCreates an integer rectangle, truncating all inputs toward zero.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): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(0, 0);
Demo.pivotMark: Rect2ipivotMark = new new Rect2i(x?: number, y?: number, width?: number, height?: number): Rect2iCreates an integer rectangle, truncating all inputs toward zero.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): Vector2iCreates an integer 2D vector, truncating inputs toward zero.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 | nullpalette = const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.paletteCreate: (size?: number) => PaletteCreates a standalone palette instance.paletteCreate(256);
// 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: Palettepalette);
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 SpriteSheetSprite-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.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.loadColorsIntoPalette(const SHAPES: {}SHAPES[0].url, this.Demo.palette: Palettepalette, 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: Palettepalette.Palette.fillBlock(start: number, source: readonly Color32[], transform: (color: Color32, index: number) => Color32): numberWrites 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.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): Color32Creates a clamped 8-bit RGBA color.Color32(Math.min(255, base: Color32base.Color32.r: numberRed channel (0-255).r + 80), base: Color32base.Color32.g: numberGreen channel (0-255).g, Math.max(0, base: Color32base.Color32.b: numberBlue channel (0-255).b - 80)),
);
// Ice: pull red down and push blue up.
this.Demo.palette: Palettepalette.Palette.fillBlock(start: number, source: readonly Color32[], transform: (color: Color32, index: number) => Color32): numberWrites 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.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): Color32Creates a clamped 8-bit RGBA color.Color32(Math.max(0, base: Color32base.Color32.r: numberRed channel (0-255).r - 60), base: Color32base.Color32.g: numberGreen channel (0-255).g, Math.min(255, base: Color32base.Color32.b: numberBlue channel (0-255).b + 80)),
);
// Void: darken every channel toward black.
this.Demo.palette: Palettepalette.Palette.fillBlock(start: number, source: readonly Color32[], transform: (color: Color32, index: number) => Color32): numberWrites 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.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): Color32Creates a clamped 8-bit RGBA color.Color32(Math.floor(base: Color32base.Color32.r: numberRed channel (0-255).r * 0.25), Math.floor(base: Color32base.Color32.g: numberGreen channel (0-255).g * 0.25), Math.floor(base: Color32base.Color32.b: numberBlue 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: {}sheets = await Promise.all(const SHAPES: {}SHAPES.map(({ url: anyurl }) => class SpriteSheetSprite-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.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`.load(url: anyurl, { tileSize?: number | Vector2i | undefinedtileSize: const SHAPE_CELL: 16SHAPE_CELL })));
for (const const sheet: anysheet of this.Demo.sheets: {}sheets) {
const sheet: anysheet.indexize(this.Demo.palette: Palettepalette);
}
this.Demo.starSheet: nullstarSheet = this.Demo.sheets: {}sheets[const STAR_SHAPE: 3STAR_SHAPE];
this.Demo.triangleSheet: nulltriangleSheet = this.Demo.sheets: {}sheets[const TRIANGLE_SHAPE: 2TRIANGLE_SHAPE];
this.Demo.heartSheet: nullheartSheet = this.Demo.sheets: {}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): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(const SHAPE_CELL: 16SHAPE_CELL / 2, const SHAPE_CELL: 16SHAPE_CELL / 2);
this.Demo.cornerPivot: nullcornerPivot = new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.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) => voidStores the active engine palette.
Use this to swap the **entire palette** (e.g. switch between a day and night
theme). After this call the renderer uploads the new palette uniform on the
next frame.
**Palette-value swap (change what a slot looks like):** mutate the live
{@link
BT.palette
}
in place with `palette.set(slot, newColor)`. The renderer
uploads dirty slots on the next frame; no `paletteSet()` or
{@link
BT.spritesRefresh
}
needed.
**Palette-layout swap (same colors, different slot positions):** build a new
palette with the same colors at new indices, call `paletteSet()`, then call
{@link
BT.spritesRefresh
}
so every sprite sheet re-maps its original RGBA
pixels against the new slot layout.paletteSet(this.Demo.palette: Palettepalette);
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) => voidSets the frame clear color using a palette index.
The renderer uses this color when clearing the full display at the start
of the next frame.clear(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: {}sheets[let i: numberi], 0, new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.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): Vector2iCreates an integer 2D vector, truncating inputs toward zero.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): Vector2iCreates an integer 2D vector, truncating inputs toward zero.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: numberRotate 270° clockwise flag for sprite rendering. Pass in
{@link
SpriteDrawParams.flags
}
; combine with the other `FLIP_*` / `ROT_*` bits using `|`.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: numberRotate 90° clockwise flag for sprite rendering. Pass in
{@link
SpriteDrawParams.flags
}
; combine with the other `FLIP_*` / `ROT_*` bits using `|`.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): Vector2iCreates an integer 2D vector, truncating inputs toward zero.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): Vector2iCreates an integer 2D vector, truncating inputs toward zero.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): Vector2iCreates an integer 2D vector, truncating inputs toward zero.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): Vector2iCreates an integer 2D vector, truncating inputs toward zero.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: numberRotate 90° clockwise flag for sprite rendering. Pass in
{@link
SpriteDrawParams.flags
}
; combine with the other `FLIP_*` / `ROT_*` bits using `|`.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): Vector2iCreates an integer 2D vector, truncating inputs toward zero.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) => voidDraws a filled rectangle.drawRectFill(this.Demo.pivotMark: Rect2ipivotMark.Rect2i.set(x: number, y: number, width: number, height: number): Rect2iSets all components of this rectangle.
Modifies this rectangle directly for maximum performance.
WARNING: Mutates this rectangle. Don't use on frozen/cached singletons.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): Vector2iSets both components of this vector.
Modifies this vector directly for maximum performance.
WARNING: Mutates this vector. Don't use on frozen/cached singletons.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): Vector2iSets both components of this vector.
Modifies this vector directly for maximum performance.
WARNING: Mutates this vector. Don't use on frozen/cached singletons.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): Vector2iSets both components of this vector.
Modifies this vector directly for maximum performance.
WARNING: Mutates this vector. Don't use on frozen/cached singletons.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: numberTop 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: numberCurrent fixed-update tick counter.
Increments once per engine update. Reset via
{@link
BT.ticksReset
}
.ticks / 40) + 1) / 2;
this.Demo.stretchBox: Rect2istretchBox.Rect2i.width: numberWidth 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: numberWidth 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: numberLeft 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.bootstrap(class DemoDemo);