/*

Palette Presets: six built-in color sets you can load instantly.
@description Six built-in color sets, including VGA, CGA, and C64, you can load into the active palette instantly.

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/palette-presets

Guides:
  - Palette Presets: https://blit386.dev/docs/guides/palette-presets
  - Game Boy preset: https://blit386.dev/docs/guides/palette-presets#game-boy-4-slots
  - CGA preset: https://blit386.dev/docs/guides/palette-presets#cga-16-slots
  - C64 preset: https://blit386.dev/docs/guides/palette-presets#c64-16-slots
  - PICO-8 preset: https://blit386.dev/docs/guides/palette-presets#pico8-16-slots
  - NES preset: https://blit386.dev/docs/guides/palette-presets#nes-64-slots
  - VGA preset: https://blit386.dev/docs/guides/palette-presets#vga-256-slots
  - Palette: https://blit386.dev/docs/guides/palette#create-and-activate-a-palette
  - Overlay: https://blit386.dev/docs/guides/overlay#custom-rows

Reference:
  - Built-in presets: https://blit386.dev/docs/api/palette#built-in-presets
  - Named slots: https://blit386.dev/docs/api/palette#named-slot-aliases
  - Overlay: https://blit386.dev/docs/api/overlay#event-tags

---

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

A 520 by 390 pixel screen with the engine overlay open, and two UI kit panels stacked in
the space between its top and bottom strips.

The "Built-in presets" panel has one row per preset, in this order: Game Boy, CGA, C64,
PICO-8, NES, VGA. Each row starts with the preset name and its slot count in a left
column, then the preset's slots as small 9 by 12 pixel swatches, all rows starting at the
same x. Rows stop at 32 swatches; the NES and VGA rows end with a dim "+32" or "+224" for
the slots not shown. The first swatch of every row is slot 0, the engine's reserved
transparent slot, drawn as a hollow outline because filling it would draw nothing. The
name of the preset the live view is showing is lit in green.

The "Live view" panel below names the current preset and shows 16 large swatches. Every 2
seconds it moves on to the next preset and copies that preset's first 16 slots into
palette slots 200-215, so the big swatches change color while the drawing code stays
exactly the same. Two dim lines in the panel show the setNamed() and getNamed() calls
from init().

The engine overlay also gets a custom row naming the current live-view preset and its
slot count, and its timing chart gets a tag at every switch.

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

- The six preset factories: Palette.gameboy(), Palette.cga(), Palette.c64(),
  Palette.pico8(), Palette.nes(), and Palette.vga(). Each returns a ready-made Palette.
- palette.size tells you how many slots a palette has; palette.get(index) reads one slot.
- Copying colors from one Palette into another with palette.set(), so one active palette
  can show samples of six presets at once.
- Naming a slot with palette.setNamed(name, index) and reading it back with
  palette.getNamed(name).
- A custom overlay row via overlayRows() and timing-chart tags via BT.assignTag().

WHAT IS A PALETTE PRESET?
-------------------------

In the earlier demos we built a palette by hand, one palette.set() call per color.
BLIT386 also ships six ready-made palettes based on real hardware from video game
history:

  - Game Boy    4 slots   (1989 Nintendo handheld - shades of green)
  - CGA        16 slots   (1981 IBM PC graphics card - loud, iconic)
  - C64        16 slots   (1982 Commodore 64 - earthy, distinctive)
  - PICO-8     16 slots   (2015 fantasy console - soft, retro feel)
  - NES        64 slots   (1983 Nintendo console - 56 colors defined, the rest black)
  - VGA       256 slots   (1987 IBM PC graphics standard - rich range)

Slot 0 of every palette is reserved for transparency, so a preset's colors start at
slot 1. A preset palette gives you an authentic retro look in one call.

You can also NAME slots with setNamed() and getNamed() - like writing a label on a paint
can instead of only numbering it. "live-swatch-0" is easier to remember than 200.

TRY THIS
--------

- Press the backquote key (`) to open the engine overlay. The custom row near the bottom
  names the live-view preset, and the timing chart gets a tag each time it switches.
- The overlay's palette grid is turned on with 64 columns. Find slots 200-215 in it: they
  change every 2 seconds, while the swatch copies in slots 10-181 never move.

*/

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
,
type Palette = Palette
class Palette
Mutable palette of indexed {@link Color32 } entries. The palette is the central color authority for all rendering: - **Index 0 is always transparent.** It is initialized with `Color32.transparent` and cannot be set to an opaque color. The primitive and sprite shaders discard any fragment whose palette index resolves to alpha 0. - **Variable sizes:** valid sizes are `2, 4, 16, 32, 64, 128, 256`. The active size determines the range for `set()` / `get()` and named-color lookups. - **Fixed GPU layout:** `toFloat32Array()` always outputs `256 * 4` floats so the renderer can upload a stable 4 KB uniform block regardless of palette size. Slots beyond the active size are padded with transparent black. - **Named aliases:** optional string tags map human-readable names to indices, e.g. `setNamed('player', 3)`. They carry no runtime cost when unused. - **Mutable by design:** palette-effect features modify entries in place. Use `clone()` when a snapshot is needed before modification.
@since1.0.3@changed1.7.0 Added the `fillBlock` method, writing a transformed block of colors into contiguous slots via `set()` and returning the next free slot, so consecutive blocks chain.
Palette
, class Rect2i
Integer rectangle for pixel-perfect bounds and regions. Used throughout the engine for sprite regions, display-space bounds, and zero-allocation geometry helpers. Both convenience getters and allocation-free `*To()` helpers are provided so callers can choose between readability and hot-path efficiency.
@since0.1.0
Rect2i
, class Vector2i
Integer 2D vector for pixel-perfect positioning. Used for points, sizes, directions, and camera offsets throughout the engine. The API includes both allocation-free `*To()` / `*InPlace()` variants and convenience methods that return new vectors.
@since0.1.0
Vector2i
} from 'blit386';
import { import applyThemeapplyTheme, import FONT_WFONT_W, import uiui, import UI_ANCHORSUI_ANCHORS } from './shared/ui.js'; /** @typedef {import('blit386').Palette} Palette */ // How many ticks the live view shows each preset. A tick is one fixed update; at 60 ticks // per second, 120 ticks is 2 seconds. const const LIVE_SWITCH_TICKS: 120LIVE_SWITCH_TICKS = 120; // Maximum number of swatches per row, so the 64-slot NES and 256-slot VGA rows do not run // off the screen. const const MAX_SWATCHES_PER_ROW: 32MAX_SWATCHES_PER_ROW = 32; // Where the two kit panels sit. The overlay starts visible and covers the top 64 and the // bottom 100 pixels of the 390-pixel screen, so the panels stack in the band between: // Built-in presets on top, Live view below it, both nearly full width. const const PANEL_X: 6PANEL_X = 6; const const PANEL_W: 508PANEL_W = 508; const const PRESETS_PANEL_Y: 69PRESETS_PANEL_Y = 69; const const PANEL_GAP: 6PANEL_GAP = 6; // A kit panel with a title starts its content 22 pixels below its top edge and ends 11 // pixels below the last row (the rim and the title strip). const const PANEL_CONTENT_TOP: 22PANEL_CONTENT_TOP = 22; const const PANEL_BOTTOM: 11PANEL_BOTTOM = 11; // Inside the presets panel each row is a name column, then the swatches. The text starts // 10 pixels in from the panel edge (the same inset as the panel title); the swatches all // start at one shared x, 14 characters further right, so every row lines up. const const ROW_TEXT_X: numberROW_TEXT_X = const PANEL_X: 6PANEL_X + 10; const const SWATCH_X: numberSWATCH_X = const ROW_TEXT_X: numberROW_TEXT_X + 14 * import FONT_WFONT_W; const const ROW_PITCH: 15ROW_PITCH = 15; // Swatch size in pixels, and the distance from one swatch to the next (a 2-pixel gap). const const SWATCH_W: 9SWATCH_W = 9; const const SWATCH_H: 12SWATCH_H = 12; const const SWATCH_STEP: numberSWATCH_STEP = const SWATCH_W: 9SWATCH_W + 2; // The presets panel is tall enough for six rows; the last row needs no gap under it. const const PRESETS_PANEL_H: numberPRESETS_PANEL_H = const PANEL_CONTENT_TOP: 22PANEL_CONTENT_TOP + 6 * const ROW_PITCH: 15ROW_PITCH - (const ROW_PITCH: 15ROW_PITCH - const SWATCH_H: 12SWATCH_H) + const PANEL_BOTTOM: 11PANEL_BOTTOM; const const LIVE_PANEL_Y: numberLIVE_PANEL_Y = const PRESETS_PANEL_Y: 69PRESETS_PANEL_Y + const PRESETS_PANEL_H: numberPRESETS_PANEL_H + const PANEL_GAP: 6PANEL_GAP; // The 16 live-view swatches: 28 pixels wide with a 2-pixel gap, centered in the panel. const const LIVE_SWATCH_COUNT: 16LIVE_SWATCH_COUNT = 16; const const LIVE_SWATCH_W: 28LIVE_SWATCH_W = 28; const const LIVE_SWATCH_H: 20LIVE_SWATCH_H = 20; const const LIVE_SWATCH_STEP: numberLIVE_SWATCH_STEP = const LIVE_SWATCH_W: 28LIVE_SWATCH_W + 2; const const LIVE_SWATCH_X: numberLIVE_SWATCH_X = const PANEL_X: 6PANEL_X + (const PANEL_W: 508PANEL_W - (const LIVE_SWATCH_COUNT: 16LIVE_SWATCH_COUNT * const LIVE_SWATCH_STEP: numberLIVE_SWATCH_STEP - 2)) / 2; // First palette slot of the 16 live-view swatches (slots 200-215). const const LIVE_SWATCH_SLOT: 200LIVE_SWATCH_SLOT = 200; // Shared UI theme slots used by configure(). applyTheme() (see init()) writes the twelve // shared UI colors into palette slots 240-251 - its default start slot, safely above this // demo's swatch slots (10-181) and live-view slots (200-215). configure() runs BEFORE // init(), so the overlay styles below have to name those slots as plain numbers instead // of reading this.theme. const const THEME_BG: 240THEME_BG = 240; // deep navy screen background const const THEME_HEADER: 246THEME_HEADER = 246; // warm amber header text const const THEME_ACCENT: 247THEME_ACCENT = 247; // phosphor green accent const const THEME_WARM: 248THEME_WARM = 248; // warm orange (warnings) const const THEME_INFO: 249THEME_INFO = 249; // info blue // Shows the six built-in palette presets side by side, plus a live view that cycles // through them and a named palette slot. class class DemoDemo { // The main palette: holds the swatch copies, the live-view slots, and the UI theme. /** @type {Palette | null} */ Demo.palette: Palette | null
@type{Palette | null}
palette
= null;
// Palette slot map for the shared UI kit theme, filled in init() by applyTheme(). Demo.theme: nulltheme = null; // The six preset Palette objects, loaded in init(). Demo.presets: {}presets = []; // Display name for each preset, in the same order as `presets`. Demo.presetNames: {}presetNames = ['Game Boy', 'CGA', 'C64', 'PICO-8', 'NES', 'VGA']; // Which preset (0-5) the live view currently shows. Demo.currentPresetIndex: numbercurrentPresetIndex = 0; // Tick when the live view last switched presets. Demo.lastSwitchTick: numberlastSwitchTick = 0; // First palette slot of each preset's swatch row, filled in init(). Demo.swatchOffsets: {}swatchOffsets = []; // Reused for every swatch rectangle in render(), so drawing allocates no new Rect2i. Demo.swatchRect: Rect2iswatchRect = 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, 0, 0);
// Reused every frame for the overlay row, so no new array or object is allocated. Demo.overlayRowData: {}overlayRowData = [{ leftText: stringleftText: 'Current Game Boy - 4 colors', textPaletteIndex: numbertextPaletteIndex: const THEME_HEADER: 246THEME_HEADER }]; // A wider canvas, the overlay's palette grid (64 columns), and overlay colors. The // overlay bar, text, and timing chart all borrow shared UI theme slots (240-251, // written by applyTheme() in init()) so the whole demo uses one color scheme.
Demo.configure(): {
    displaySize: Vector2i;
    maxCanvasSize: Vector2i;
    isOverlayPaletteEnabled: boolean;
    isOverlayVisibleAtStart: boolean;
    overlayPaletteColumns: number;
    overlayStyle: {
        barPaletteIndex: number;
        textPaletteIndex: number;
        gapPaletteIndex: number;
    };
    isOverlayTimingChartEnabled: boolean;
    overlayTimingChartStyle: {
        updateBarPaletteIndex: number;
        renderBarPaletteIndex: number;
        warningPaletteIndex: number;
        errorPaletteIndex: number;
        tagPaletteIndex: number;
    };
}
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
(520, 390),
maxCanvasSize: Vector2imaxCanvasSize: 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
(520 * 2, 390 * 2),
isOverlayPaletteEnabled: booleanisOverlayPaletteEnabled: true, isOverlayVisibleAtStart: booleanisOverlayVisibleAtStart: true, overlayPaletteColumns: numberoverlayPaletteColumns: 64,
overlayStyle: {
    barPaletteIndex: number;
    textPaletteIndex: number;
    gapPaletteIndex: number;
}
overlayStyle
: {
barPaletteIndex: numberbarPaletteIndex: const THEME_BG: 240THEME_BG, textPaletteIndex: numbertextPaletteIndex: const THEME_HEADER: 246THEME_HEADER, gapPaletteIndex: numbergapPaletteIndex: const THEME_BG: 240THEME_BG, }, isOverlayTimingChartEnabled: booleanisOverlayTimingChartEnabled: true,
overlayTimingChartStyle: {
    updateBarPaletteIndex: number;
    renderBarPaletteIndex: number;
    warningPaletteIndex: number;
    errorPaletteIndex: number;
    tagPaletteIndex: number;
}
overlayTimingChartStyle
: {
updateBarPaletteIndex: numberupdateBarPaletteIndex: const THEME_ACCENT: 247THEME_ACCENT, renderBarPaletteIndex: numberrenderBarPaletteIndex: const THEME_INFO: 249THEME_INFO, warningPaletteIndex: numberwarningPaletteIndex: const THEME_HEADER: 246THEME_HEADER, errorPaletteIndex: numbererrorPaletteIndex: const THEME_WARM: 248THEME_WARM, tagPaletteIndex: numbertagPaletteIndex: const THEME_HEADER: 246THEME_HEADER, }, }; } // Loads the six presets, copies their colors into one palette, installs the shared UI // theme, names a live-view slot, and activates the palette. async Demo.init(): unknowninit() { console.log('[PalettePresetsDemo] Initializing...'); // Load all six preset palettes. These are static factory methods that return a // ready-made Palette object - think of them as pre-sorted boxes of paint. this.Demo.presets: {}presets = [ class Palette
Mutable palette of indexed {@link Color32 } entries. The palette is the central color authority for all rendering: - **Index 0 is always transparent.** It is initialized with `Color32.transparent` and cannot be set to an opaque color. The primitive and sprite shaders discard any fragment whose palette index resolves to alpha 0. - **Variable sizes:** valid sizes are `2, 4, 16, 32, 64, 128, 256`. The active size determines the range for `set()` / `get()` and named-color lookups. - **Fixed GPU layout:** `toFloat32Array()` always outputs `256 * 4` floats so the renderer can upload a stable 4 KB uniform block regardless of palette size. Slots beyond the active size are padded with transparent black. - **Named aliases:** optional string tags map human-readable names to indices, e.g. `setNamed('player', 3)`. They carry no runtime cost when unused. - **Mutable by design:** palette-effect features modify entries in place. Use `clone()` when a snapshot is needed before modification.
@since1.0.3@changed1.7.0 Added the `fillBlock` method, writing a transformed block of colors into contiguous slots via `set()` and returning the next free slot, so consecutive blocks chain.
Palette
.Palette.gameboy(): Palette
Creates a four-shade Game Boy palette.
@returnsNew Game Boy preset palette.
gameboy
(), // 4 slots: shades of green
class Palette
Mutable palette of indexed {@link Color32 } entries. The palette is the central color authority for all rendering: - **Index 0 is always transparent.** It is initialized with `Color32.transparent` and cannot be set to an opaque color. The primitive and sprite shaders discard any fragment whose palette index resolves to alpha 0. - **Variable sizes:** valid sizes are `2, 4, 16, 32, 64, 128, 256`. The active size determines the range for `set()` / `get()` and named-color lookups. - **Fixed GPU layout:** `toFloat32Array()` always outputs `256 * 4` floats so the renderer can upload a stable 4 KB uniform block regardless of palette size. Slots beyond the active size are padded with transparent black. - **Named aliases:** optional string tags map human-readable names to indices, e.g. `setNamed('player', 3)`. They carry no runtime cost when unused. - **Mutable by design:** palette-effect features modify entries in place. Use `clone()` when a snapshot is needed before modification.
@since1.0.3@changed1.7.0 Added the `fillBlock` method, writing a transformed block of colors into contiguous slots via `set()` and returning the next free slot, so consecutive blocks chain.
Palette
.Palette.cga(): Palette
Creates the classic CGA 16-color palette.
@returnsNew CGA preset palette.
cga
(), // 16 loud IBM PC colors
class Palette
Mutable palette of indexed {@link Color32 } entries. The palette is the central color authority for all rendering: - **Index 0 is always transparent.** It is initialized with `Color32.transparent` and cannot be set to an opaque color. The primitive and sprite shaders discard any fragment whose palette index resolves to alpha 0. - **Variable sizes:** valid sizes are `2, 4, 16, 32, 64, 128, 256`. The active size determines the range for `set()` / `get()` and named-color lookups. - **Fixed GPU layout:** `toFloat32Array()` always outputs `256 * 4` floats so the renderer can upload a stable 4 KB uniform block regardless of palette size. Slots beyond the active size are padded with transparent black. - **Named aliases:** optional string tags map human-readable names to indices, e.g. `setNamed('player', 3)`. They carry no runtime cost when unused. - **Mutable by design:** palette-effect features modify entries in place. Use `clone()` when a snapshot is needed before modification.
@since1.0.3@changed1.7.0 Added the `fillBlock` method, writing a transformed block of colors into contiguous slots via `set()` and returning the next free slot, so consecutive blocks chain.
Palette
.Palette.c64(): Palette
Creates the Commodore 64 16-color palette.
@returnsNew C64 preset palette.
c64
(), // 16 Commodore 64 colors
class Palette
Mutable palette of indexed {@link Color32 } entries. The palette is the central color authority for all rendering: - **Index 0 is always transparent.** It is initialized with `Color32.transparent` and cannot be set to an opaque color. The primitive and sprite shaders discard any fragment whose palette index resolves to alpha 0. - **Variable sizes:** valid sizes are `2, 4, 16, 32, 64, 128, 256`. The active size determines the range for `set()` / `get()` and named-color lookups. - **Fixed GPU layout:** `toFloat32Array()` always outputs `256 * 4` floats so the renderer can upload a stable 4 KB uniform block regardless of palette size. Slots beyond the active size are padded with transparent black. - **Named aliases:** optional string tags map human-readable names to indices, e.g. `setNamed('player', 3)`. They carry no runtime cost when unused. - **Mutable by design:** palette-effect features modify entries in place. Use `clone()` when a snapshot is needed before modification.
@since1.0.3@changed1.7.0 Added the `fillBlock` method, writing a transformed block of colors into contiguous slots via `set()` and returning the next free slot, so consecutive blocks chain.
Palette
.Palette.pico8(): Palette
Creates the PICO-8 16-color palette.
@returnsNew PICO-8 preset palette.
pico8
(), // 16 soft fantasy console colors
class Palette
Mutable palette of indexed {@link Color32 } entries. The palette is the central color authority for all rendering: - **Index 0 is always transparent.** It is initialized with `Color32.transparent` and cannot be set to an opaque color. The primitive and sprite shaders discard any fragment whose palette index resolves to alpha 0. - **Variable sizes:** valid sizes are `2, 4, 16, 32, 64, 128, 256`. The active size determines the range for `set()` / `get()` and named-color lookups. - **Fixed GPU layout:** `toFloat32Array()` always outputs `256 * 4` floats so the renderer can upload a stable 4 KB uniform block regardless of palette size. Slots beyond the active size are padded with transparent black. - **Named aliases:** optional string tags map human-readable names to indices, e.g. `setNamed('player', 3)`. They carry no runtime cost when unused. - **Mutable by design:** palette-effect features modify entries in place. Use `clone()` when a snapshot is needed before modification.
@since1.0.3@changed1.7.0 Added the `fillBlock` method, writing a transformed block of colors into contiguous slots via `set()` and returning the next free slot, so consecutive blocks chain.
Palette
.Palette.nes(): Palette
Creates a 64-slot NES palette.
@returnsNew NES preset palette.
nes
(), // 64 slots, 56 of them defined
class Palette
Mutable palette of indexed {@link Color32 } entries. The palette is the central color authority for all rendering: - **Index 0 is always transparent.** It is initialized with `Color32.transparent` and cannot be set to an opaque color. The primitive and sprite shaders discard any fragment whose palette index resolves to alpha 0. - **Variable sizes:** valid sizes are `2, 4, 16, 32, 64, 128, 256`. The active size determines the range for `set()` / `get()` and named-color lookups. - **Fixed GPU layout:** `toFloat32Array()` always outputs `256 * 4` floats so the renderer can upload a stable 4 KB uniform block regardless of palette size. Slots beyond the active size are padded with transparent black. - **Named aliases:** optional string tags map human-readable names to indices, e.g. `setNamed('player', 3)`. They carry no runtime cost when unused. - **Mutable by design:** palette-effect features modify entries in place. Use `clone()` when a snapshot is needed before modification.
@since1.0.3@changed1.7.0 Added the `fillBlock` method, writing a transformed block of colors into contiguous slots via `set()` and returning the next free slot, so consecutive blocks chain.
Palette
.Palette.vga(): Palette
Creates the default VGA-style 256-color palette.
@returnsNew VGA preset palette.
vga
(), // 256 VGA standard colors
]; // Build the main palette. It holds the swatch copies plus the shared UI colors. // We keep it separate from the preset palettes so the UI is always readable. 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);
// applyTheme() installs the twelve shared UI colors into slots 240-251 and // returns a map of where each color landed (this.theme.bg, .text, .dim, ...). // Every kit panel and label below draws with these colors automatically. this.Demo.theme: nulltheme = import applyThemeapplyTheme(this.Demo.palette: Palette
@type{Palette | null}
palette
);
// Slots 10-181 hold the swatch colors for the six static rows. We copy each // preset's colors into this palette so we can draw swatches without switching the // active palette. Layout: preset 0 at 10-13 (4 slots), preset 1 at 20-35 (16), // and so on; the NES and VGA rows are capped at 32 slots each. const const swatchOffsets: {}swatchOffsets = [10, 20, 40, 60, 80, 150]; for (let let p: numberp = 0; let p: numberp < this.Demo.presets: {}presets.length; let p: numberp++) { const const preset: anypreset = this.Demo.presets: {}presets[let p: numberp]; const const maxColors: anymaxColors = Math.min(const preset: anypreset.size, const MAX_SWATCHES_PER_ROW: 32MAX_SWATCHES_PER_ROW); const const offset: anyoffset = const swatchOffsets: {}swatchOffsets[let p: numberp]; for (let let c: numberc = 0; let c: numberc < const maxColors: anymaxColors; let c: numberc++) { // palette.get(index) returns a copy of the Color32 stored at that slot. const const color: anycolor = const preset: anypreset.get(let c: numberc); if (const color: anycolor) { this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const offset: anyoffset + let c: numberc, const color: anycolor);
} } } // Keep the offsets for render(). this.Demo.swatchOffsets: {}swatchOffsets = const swatchOffsets: {}swatchOffsets; // Slots 200-215 are the live-view swatches. Start them black; // updateLiveSwatches() fills them with the current preset's colors. for (let let i: numberi = 0; let i: numberi < const LIVE_SWATCH_COUNT: 16LIVE_SWATCH_COUNT; let i: numberi++) { this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const LIVE_SWATCH_SLOT: 200LIVE_SWATCH_SLOT + let i: numberi, 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
(0, 0, 0));
} // setNamed() lets you refer to a slot by a descriptive name instead of a number - // like writing "live-swatch-0" on a label instead of "slot 200". (applyTheme() // above did the same for its UI colors: palette.getNamed('ui_bg') answers 240.) this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.setNamed(name: string, index: number): void
Associates a human-readable name with a palette index.
@paramname - Name to register.@paramindex - Palette index referenced by the name.@throwsError if the index is invalid.
setNamed
('live-swatch-0', const LIVE_SWATCH_SLOT: 200LIVE_SWATCH_SLOT);
// Make this palette the one the engine draws with.
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
);
// Fill the live view with the first preset. this.Demo.updateLiveSwatches(): voidupdateLiveSwatches(); console.log('[PalettePresetsDemo] Initialized'); return true; } // Advances the live view: every LIVE_SWITCH_TICKS ticks, move on to the next preset. Demo.update(): voidupdate() { const const tick: numbertick =
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
;
if (const tick: numbertick - this.Demo.lastSwitchTick: numberlastSwitchTick >= const LIVE_SWITCH_TICKS: 120LIVE_SWITCH_TICKS) { // Move to the next preset. `%` is the remainder after division, so the index // wraps back to 0 after the last preset. this.Demo.currentPresetIndex: numbercurrentPresetIndex = (this.Demo.currentPresetIndex: numbercurrentPresetIndex + 1) % this.Demo.presets: {}presets.length; this.Demo.lastSwitchTick: numberlastSwitchTick = const tick: numbertick;
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
.assignTag: (label?: string) => void
Places a labeled marker on the overlay timing chart at the current tick. Requires `isOverlayTimingChartEnabled: true` in `configure()`. Tags scroll with the chart history and are pruned when they leave the visible window. Empty labels become `"Untitled"`. Chart width resets add an automatic `"Start"` tag.
@since1.1.0@paramlabel - Short event name (for example `'Round start'`).
assignTag
(`Preset: ${this.Demo.presetNames: {}presetNames[this.Demo.currentPresetIndex: numbercurrentPresetIndex]}`);
// Copy the new preset's colors into the live-view slots (200-215). this.Demo.updateLiveSwatches(): voidupdateLiveSwatches(); } } // Draws the title strip, the six swatch rows with captions, and the live-view panel. // No Color32 appears in a draw call here - only palette slot numbers (and the kit, // which uses its own theme slots). Demo.render(): voidrender() { // Background: the shared theme's deep navy, so every demo looks alike.
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);
// One row of swatches per preset. this.Demo.renderSwatchRows(): voidrenderSwatchRows(); // The live cycling preview. this.Demo.renderLivePreview(): voidrenderLivePreview(); } // Names the current live-view preset and its slot count for the engine overlay. Demo.overlayRows(): {}overlayRows() { // The overlay can ask for rows before init() has filled the six presets; until // then we answer with the prepared default row. After init(), update() keeps // currentPresetIndex in range with %, so no other check is needed. if (this.Demo.presets: {}presets.length === 0) { return this.Demo.overlayRowData: {}overlayRowData; } const const name: anyname = this.Demo.presetNames: {}presetNames[this.Demo.currentPresetIndex: numbercurrentPresetIndex]; const const size: anysize = this.Demo.presets: {}presets[this.Demo.currentPresetIndex: numbercurrentPresetIndex].size; this.Demo.overlayRowData: {}overlayRowData[0].leftText = `Current ${const name: anyname} - ${const size: anysize} colors`; return this.Demo.overlayRowData: {}overlayRowData; } // Copies the current preset's first 16 slots into the live-view slots (200-215). Any // slot past the preset's size turns black. render() shows the new colors on the next // frame without changing a single draw call. Demo.updateLiveSwatches(): voidupdateLiveSwatches() { const const preset: anypreset = this.Demo.presets: {}presets[this.Demo.currentPresetIndex: numbercurrentPresetIndex]; const const maxColors: anymaxColors = Math.min(const preset: anypreset.size, 16); for (let let i: numberi = 0; let i: numberi < 16; let i: numberi++) { if (let i: numberi < const maxColors: anymaxColors) { const const color: anycolor = const preset: anypreset.get(let i: numberi); this.Demo.palette: Palette | null
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const LIVE_SWATCH_SLOT: 200LIVE_SWATCH_SLOT + let i: numberi, const color: anycolor ?? 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
(0, 0, 0));
} else { this.Demo.palette: Palette | null
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const LIVE_SWATCH_SLOT: 200LIVE_SWATCH_SLOT + let i: numberi, 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
(0, 0, 0));
} } } // Draws one swatch inside a panel. Slot 0 of every palette is the reserved // transparent slot, so filling it would draw nothing; it gets a hollow outline in the // theme's border color instead, so you can see where it is. Both use the kit's // ui.drawRectFill() / ui.drawRect(): kit panels are sprites, and the engine draws // every BT.drawRectFill() before any sprite - so a plain one would hide under the // panel. The kit versions take the same rectangle and slot but draw sprites, which // land on top in the order we call them. Demo.renderSwatch(x: any, y: any, width: any, height: any, slot: any, isTransparentSlot: any): voidrenderSwatch(x: anyx, y: anyy, width: anywidth, height: anyheight, slot: anyslot, isTransparentSlot: anyisTransparentSlot) { this.Demo.swatchRect: Rect2iswatchRect.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, y: anyy, width: anywidth, height: anyheight);
if (isTransparentSlot: anyisTransparentSlot) { import uiui.drawRect(this.Demo.swatchRect: Rect2iswatchRect, this.Demo.theme: nulltheme.border); } else { import uiui.drawRectFill(this.Demo.swatchRect: Rect2iswatchRect, slot: anyslot); } } // Draws the Built-in presets panel: one row per preset with its name and slot count // in a left column, then its swatches from the copies made in init(). The row that // the live view is showing right now has its name lit in the accent color. Demo.renderSwatchRows(): voidrenderSwatchRows() { // The panel frame, with an empty body tall enough for six rows. Everything inside // is drawn after ui.end(), so it lands on top of the panel. import uiui.begin(import UI_ANCHORSUI_ANCHORS.TOP_LEFT, { x: numberx: const PANEL_X: 6PANEL_X, y: numbery: const PRESETS_PANEL_Y: 69PRESETS_PANEL_Y, width: numberwidth: const PANEL_W: 508PANEL_W }); import uiui.panel('Built-in presets - Palette.gameboy(), .cga(), ...'); import uiui.spacer(const PRESETS_PANEL_H: numberPRESETS_PANEL_H - const PANEL_CONTENT_TOP: 22PANEL_CONTENT_TOP - const PANEL_BOTTOM: 11PANEL_BOTTOM); import uiui.end(); for (let let p: numberp = 0; let p: numberp < this.Demo.presets: {}presets.length; let p: numberp++) { const const y: numbery = const PRESETS_PANEL_Y: 69PRESETS_PANEL_Y + const PANEL_CONTENT_TOP: 22PANEL_CONTENT_TOP + let p: numberp * const ROW_PITCH: 15ROW_PITCH; const const size: anysize = this.Demo.presets: {}presets[let p: numberp].size; // Same math as init(): the preset's real slot count, capped at the row limit. const const count: anycount = Math.min(const size: anysize, const MAX_SWATCHES_PER_ROW: 32MAX_SWATCHES_PER_ROW); const const offset: anyoffset = this.Demo.swatchOffsets: {}swatchOffsets[let p: numberp]; // Name on the left, slot count right-aligned just before the swatches. Every // character of the system font is FONT_W (6) pixels wide, so the count is // text.length * FONT_W wide, and starting it that far left of SWATCH_X - 8 // makes it end exactly there. // // Vertical centering: a caption's letters start 1 pixel above the y it is // given (the kit lifts its text 4 pixels, and capital letters begin 3 rows // into the 14-pixel font cell). Passing labelY = y + 3 puts the 7-pixel-tall // capitals on rows y + 2 to y + 8, the middle of the 12-pixel swatch. const const labelY: numberlabelY = const y: numbery + 3; const const isLive: booleanisLive = let p: numberp === this.Demo.currentPresetIndex: numbercurrentPresetIndex; const const sizeText: anysizeText = String(const size: anysize); import uiui.caption(const ROW_TEXT_X: numberROW_TEXT_X, const labelY: numberlabelY, this.Demo.presetNames: {}presetNames[let p: numberp], { color: stringcolor: const isLive: booleanisLive ? 'accent' : 'text' }); import uiui.caption(const SWATCH_X: numberSWATCH_X - 8 - const sizeText: anysizeText.length * import FONT_WFONT_W, const labelY: numberlabelY, const sizeText: anysizeText, { color: stringcolor: 'dim' }); for (let let c: numberc = 0; let c: numberc < const count: anycount; let c: numberc++) { this.Demo.renderSwatch(x: any, y: any, width: any, height: any, slot: any, isTransparentSlot: any): voidrenderSwatch(const SWATCH_X: numberSWATCH_X + let c: numberc * const SWATCH_STEP: numberSWATCH_STEP, const y: numbery, const SWATCH_W: 9SWATCH_W, const SWATCH_H: 12SWATCH_H, const offset: anyoffset + let c: numberc, let c: numberc === 0); } // The NES and VGA rows stop at 32 swatches; say how many slots are not shown. if (const size: anysize > const count: anycount) { import uiui.caption(const SWATCH_X: numberSWATCH_X + const count: anycount * const SWATCH_STEP: numberSWATCH_STEP + 4, const labelY: numberlabelY, `+${const size: anysize - const count: anycount}`, { color: stringcolor: 'dim' }); } } } // Draws the Live view panel: the current preset's name, 16 large swatches from the // live slots (200-215), and the two named-slot calls from init(). The swatches never // change their slot numbers - only the colors stored in those slots change. Demo.renderLivePreview(): voidrenderLivePreview() { const const name: anyname = this.Demo.presetNames: {}presetNames[this.Demo.currentPresetIndex: numbercurrentPresetIndex]; const const size: anysize = this.Demo.presets: {}presets[this.Demo.currentPresetIndex: numbercurrentPresetIndex].size; // The kit flows the rows top to bottom: the preset name, then a ui.spacer() // reserves an empty band for the swatches, then the two code lines. These code // lines show real calls from init(), not decoration. import uiui.begin(import UI_ANCHORSUI_ANCHORS.TOP_LEFT, { x: numberx: const PANEL_X: 6PANEL_X, y: numbery: const LIVE_PANEL_Y: numberLIVE_PANEL_Y, width: numberwidth: const PANEL_W: 508PANEL_W }); import uiui.panel('Live view - slots 200-215, next preset every 2 seconds'); // Game Boy has only 4 slots, so "first 16" would be wrong for it. const const shown: stringshown = const size: anysize > const LIVE_SWATCH_COUNT: 16LIVE_SWATCH_COUNT ? `first ${const LIVE_SWATCH_COUNT: 16LIVE_SWATCH_COUNT} of ${const size: anysize}` : `all ${const size: anysize}`; import uiui.label(`${const name: anyname} - ${const shown: stringshown} slots`, { color: stringcolor: 'accent' }); import uiui.spacer(const LIVE_SWATCH_H: 20LIVE_SWATCH_H + 6); import uiui.label("palette.setNamed('live-swatch-0', 200)", { color: stringcolor: 'dim' }); import uiui.label("palette.getNamed('live-swatch-0') => 200", { color: stringcolor: 'dim' }); import uiui.end(); // The swatches sit in the band the spacer reserved: below the title and the name // row (PANEL_CONTENT_TOP + 10), plus 3 pixels of breathing room. const const swatchY: numberswatchY = const LIVE_PANEL_Y: numberLIVE_PANEL_Y + const PANEL_CONTENT_TOP: 22PANEL_CONTENT_TOP + 10 + 3; for (let let i: numberi = 0; let i: numberi < const LIVE_SWATCH_COUNT: 16LIVE_SWATCH_COUNT; let i: numberi++) { const const x: numberx = const LIVE_SWATCH_X: numberLIVE_SWATCH_X + let i: numberi * const LIVE_SWATCH_STEP: numberLIVE_SWATCH_STEP; this.Demo.renderSwatch(x: any, y: any, width: any, height: any, slot: any, isTransparentSlot: any): voidrenderSwatch(const x: numberx, const swatchY: numberswatchY, const LIVE_SWATCH_W: 28LIVE_SWATCH_W, const LIVE_SWATCH_H: 20LIVE_SWATCH_H, const LIVE_SWATCH_SLOT: 200LIVE_SWATCH_SLOT + let i: numberi, let i: numberi === 0); } } } // Hand the Demo class to BLIT386 to start the demo loop. 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);