// Palette Cycling: classic retro color rotation using BT.paletteCycle().
// @description Classic retro color rotation with BT.paletteCycle: rotate palette slots to make a still image flow.
//
// Part of the BLIT386 series.
//
// Prerequisites:
//   Basics            https://demos.blit386.dev/basics
//   Primitives        https://demos.blit386.dev/primitives
//   Palette Presets   https://demos.blit386.dev/palette-presets
//   Palette Animation https://demos.blit386.dev/palette-animation
//     (guides: https://blit386.dev/docs/guides/palette-presets,
//      https://blit386.dev/docs/guides/palette#runtime-palette-effects)
//
// Live version: https://demos.blit386.dev/palette-cycling
// Guide: https://blit386.dev/docs/guides/palette#runtime-palette-effects
//
// WHAT IS PALETTE CYCLING?
//
// On old hardware, colors were stored in numbered "slots" called a palette.
// Every pixel on screen was just a number pointing to a slot.
// If you ROTATE the colors - move each color one slot forward, wrap the last
// one to the beginning - everything on screen that uses those slots appears
// to ripple or flow. The picture data never changes; only the paint-bucket
// labels shift around.
//
// This trick powered water, lava, plasma, and aurora effects in classic games
// like Sonic, Secret of Mana, and Chrono Trigger - all without redrawing a
// single pixel.
//
// BLIT386 gives you BT.paletteCycle(start, end, speed) to do exactly that.
// Call it once in init(), and the engine rotates the colors automatically
// each frame. Positive speed = forward, negative = backward.
//
// UPDATE VS RENDER (palette work split):
//   init() registers gradient colors and starts BT.paletteCycle() - the engine
//   rotates those slot ranges automatically every frame.
//   update() only runs the periodic BT.paletteSwap() demo and hides its label.
//   render() draws fixed rectangles using palette index numbers; the bands
//   appear to flow because the engine shifted slot colors, not because render()
//   recomputes Color32 values.
//
// The band headings are drawn with the shared UI kit (src/shared/ui.js); its theme
// colors live in high palette slots (240 and up), far away from every cycling range.
//
// WHAT YOU WILL SEE (three horizontal bands):
//   1. Sky   (top)    - purple-pink slots cycling very slowly = twilight drift
//   2. Fire  (middle) - orange-yellow slots cycling backward = rising flames
//   3. Water (bottom) - blue gradient slots cycling forward = flowing water
//
// Plus a palette swap demonstration every few seconds.

import { function bootstrap(DemoClass: DemoConstructor, options?: BootstrapOptions): Promise<boolean>
One-liner bootstrap function for BLIT386 demos. Handles canvas retrieval and engine initialization. Backend selection (WebGPU or software fallback) is managed internally by BTAPI. This function provides a streamlined way to start a demo with sensible defaults while allowing customization through options.
@since0.2.0@changed1.4.0 Calling `bootstrap()` again while already initialized now routes to a hot swap (via {@link registerHotReload}) when a Vite HMR context is registered, or logs a double-bootstrap guard and returns `false` otherwise - previously it silently started a second, unstoppable `GameLoop`.@changed1.7.0 Exposes `BT` on `window.BT` after bootstrap finishes, gated by {@link BootstrapOptions.exposeGlobal} (default: {@link BT.isDevMode}).@paramDemoClass - Demo class constructor implementing `IBTDemo` (optional `configure()` for hardware settings).@paramoptions - Optional configuration for IDs and callbacks.@returns`true` when the demo boots successfully; otherwise `false`.@example// Simplest usage - uses default IDs. bootstrap(MyDemo);@example// With custom options. bootstrap(MyDemo, { canvasID: 'custom-canvas', containerID: 'custom-container', onSuccess: () => console.log('Demo started!'), onError: (err) => analytics.trackError(err), });@example// Await the result. const success = await bootstrap(MyDemo); if (success) { console.log('Demo is running'); }
bootstrap
,
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
, class Color32
Mutable 32-bit RGBA color value with 8-bit channels.
@since0.1.0
Color32
, 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
} from 'blit386';
import { import applyThemeapplyTheme, import uiui, import UI_ANCHORSUI_ANCHORS } from './shared/ui.js'; /** @typedef {import('blit386').IBTDemo} IBTDemo */ /** @typedef {import('blit386').HardwareSettings} HardwareSettings */ /** @typedef {import('blit386').Palette} Palette */ // Water // 8 blue-gradient slots cycling at 4 steps per second. const const WATER_SLOTS: 8WATER_SLOTS = 8; const const WATER_SPEED: 4WATER_SPEED = 4; // Fire // 6 orange-yellow slots cycling backward at 6 steps per second. const const FIRE_SLOTS: 6FIRE_SLOTS = 6; const const FIRE_SPEED: -6FIRE_SPEED = -6; // Sky // 10 purple-pink slots cycling very slowly at 0.5 steps per second. const const SKY_SLOTS: 10SKY_SLOTS = 10; const const SKY_SPEED: 0.5SKY_SPEED = 0.5; // Palette swap demo // Every SWAP_INTERVAL ticks, swap two fire slots to show BT.paletteSwap(). const const SWAP_INTERVAL: 180SWAP_INTERVAL = 180; // ~3 seconds at 60 FPS // Slot 0: always transparent (reserved by the engine). // Engine overlay style slots. configure() runs BEFORE init() installs the shared UI // theme, so the overlay style cannot use theme slots - instead it points at these six // low slots, which init() fills by hand with fixed colors. const const C_OVERLAY_TAG: 1C_OVERLAY_TAG = 1; // Chart milestone tags (white). const const C_OVERLAY_BAR: 2C_OVERLAY_BAR = 2; // Overlay bar background (very dark navy). const const C_OVERLAY_RENDER: 3C_OVERLAY_RENDER = 3; // Timing chart render bars (dark blue). const const C_OVERLAY_TEXT: 4C_OVERLAY_TEXT = 4; // Overlay text and chart update bars (golden yellow). const const C_OVERLAY_WARN: 5C_OVERLAY_WARN = 5; // Timing chart warnings (cool gray-blue). const const C_OVERLAY_ERR: 6C_OVERLAY_ERR = 6; // Timing chart error bars (dark gray-violet). // Sky gradient: slots 10..19 (10 slots). const const C_SKY_BASE: 10C_SKY_BASE = 10; // Fire gradient: slots 30..35 (6 slots). const const C_FIRE_BASE: 30C_FIRE_BASE = 30; // Water gradient: slots 50..57 (8 slots). const const C_WATER_BASE: 50C_WATER_BASE = 50; /** * Fills a run of palette slots with a smooth gradient. * * For each slot we compute t = i / (count - 1). Think of t as "how far along the * gradient are we?" - the first slot gets t = 0 (the start), the last slot gets * t = 1 (the end), and the slots between get evenly spaced fractions like 0.25 * or 0.5 (exactly halfway). The colorAt callback turns that fraction into the * actual Color32 for the slot, so each gradient only has to describe its own * start and end colors. * * @param {Palette} palette - The palette to write into. * @param {number} baseSlot - The first slot of the gradient run. * @param {number} count - How many slots to fill. * @param {(t: number) => Color32} colorAt - Returns the color for progress t (0..1). */ function function fillGradient(palette: Palette, baseSlot: number, count: number, colorAt: (t: number) => Color32): void
Fills a run of palette slots with a smooth gradient. For each slot we compute t = i / (count - 1). Think of t as "how far along the gradient are we?" - the first slot gets t = 0 (the start), the last slot gets t = 1 (the end), and the slots between get evenly spaced fractions like 0.25 or 0.5 (exactly halfway). The colorAt callback turns that fraction into the actual Color32 for the slot, so each gradient only has to describe its own start and end colors.
@parampalette - The palette to write into.@parambaseSlot - The first slot of the gradient run.@paramcount - How many slots to fill.@paramcolorAt - Returns the color for progress t (0..1).
fillGradient
(palette: Palette
- The palette to write into.
@parampalette - The palette to write into.
palette
, baseSlot: number
- The first slot of the gradient run.
@parambaseSlot - The first slot of the gradient run.
baseSlot
, count: number
- How many slots to fill.
@paramcount - How many slots to fill.
count
, colorAt: (t: number) => Color32
- Returns the color for progress t (0..1).
@paramcolorAt - Returns the color for progress t (0..1).
colorAt
) {
for (let let i: numberi = 0; let i: numberi < count: number
- How many slots to fill.
@paramcount - How many slots to fill.
count
; let i: numberi++) {
palette: Palette
- The palette to write into.
@parampalette - The palette to write into.
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
(baseSlot: number
- The first slot of the gradient run.
@parambaseSlot - The first slot of the gradient run.
baseSlot
+ let i: numberi, colorAt: (t: number) => Color32
- Returns the color for progress t (0..1).
@paramcolorAt - Returns the color for progress t (0..1).
colorAt
(let i: numberi / (count: number
- How many slots to fill.
@paramcount - How many slots to fill.
count
- 1)));
} } /** * Demonstrates BT.paletteCycle() for automatic palette rotation, plus * BT.paletteSwap() for instant entry exchange and BT.paletteClearEffects() * for stopping all running effects. * * @implements {IBTDemo} */ class class Demo
Demonstrates BT.paletteCycle() for automatic palette rotation, plus BT.paletteSwap() for instant entry exchange and BT.paletteClearEffects() for stopping all running effects.
@implementsIBTDemo
Demo
{
/** @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(). // theme.bg, theme.dim, ... are palette indices ready for BT draw calls. Demo.theme: nulltheme = null; // Track the last swap tick so we know when to do the next swap demo. Demo.lastSwapTick: numberlastSwapTick = 0; // Which two slots were last swapped (for the UI label). Demo.swappedA: numberswappedA = 0; Demo.swappedB: numberswappedB = 0; Demo.showSwapLabel: booleanshowSwapLabel = false; /** * Palette cycling runs in the engine each frame; the chart shows update vs render * time. The overlay style colors come from the dedicated low slots set in init(). * * @returns {Partial<HardwareSettings>} */ Demo.configure(): Partial<HardwareSettings>
Palette cycling runs in the engine each frame; the chart shows update vs render time. The overlay style colors come from the dedicated low slots set in init().
@returns
configure
() {
return { isOverlayTimingChartEnabled: booleanisOverlayTimingChartEnabled: true,
overlayStyle: {
    barPaletteIndex: number;
    textPaletteIndex: number;
    gapPaletteIndex: number;
}
overlayStyle
: {
barPaletteIndex: numberbarPaletteIndex: const C_OVERLAY_BAR: 2C_OVERLAY_BAR, textPaletteIndex: numbertextPaletteIndex: const C_OVERLAY_TEXT: 4C_OVERLAY_TEXT, gapPaletteIndex: numbergapPaletteIndex: const C_OVERLAY_BAR: 2C_OVERLAY_BAR, },
overlayTimingChartStyle: {
    updateBarPaletteIndex: number;
    renderBarPaletteIndex: number;
    warningPaletteIndex: number;
    errorPaletteIndex: number;
    tagPaletteIndex: number;
}
overlayTimingChartStyle
: {
updateBarPaletteIndex: numberupdateBarPaletteIndex: const C_OVERLAY_TEXT: 4C_OVERLAY_TEXT, renderBarPaletteIndex: numberrenderBarPaletteIndex: const C_OVERLAY_RENDER: 3C_OVERLAY_RENDER, warningPaletteIndex: numberwarningPaletteIndex: const C_OVERLAY_WARN: 5C_OVERLAY_WARN, errorPaletteIndex: numbererrorPaletteIndex: const C_OVERLAY_ERR: 6C_OVERLAY_ERR, tagPaletteIndex: numbertagPaletteIndex: const C_OVERLAY_TAG: 1C_OVERLAY_TAG, }, }; } /** * Builds the palette with gradient colors and starts the cycling effects. * * @returns {Promise<boolean>} */ async Demo.init(): Promise<boolean>
Builds the palette with gradient colors and starts the cycling effects.
@returns
init
() {
console.log('[PaletteCyclingDemo] Initializing...'); this.Demo.palette: Palette | null
@type{Palette | null}
palette
=
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.paletteCreate: (size?: number) => Palette
Creates a standalone palette instance.
@since1.0.3@paramsize - Palette size. Defaults to 256 colors.@returnsNew mutable palette.
paletteCreate
(256);
// Colors for the engine overlay (the stats HUD). These live in low slots so // configure() could reference them before the UI theme existed. this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const C_OVERLAY_TAG: 1C_OVERLAY_TAG, 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
(255, 255, 255));
this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const C_OVERLAY_BAR: 2C_OVERLAY_BAR, 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
(10, 12, 20));
this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const C_OVERLAY_RENDER: 3C_OVERLAY_RENDER, 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
(20, 24, 36));
this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const C_OVERLAY_TEXT: 4C_OVERLAY_TEXT, 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
(255, 210, 80));
this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const C_OVERLAY_WARN: 5C_OVERLAY_WARN, 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
(120, 130, 160));
this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const C_OVERLAY_ERR: 6C_OVERLAY_ERR, 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
(70, 70, 90));
// The three gradients below all use the same fillGradient() helper (defined // above the class): each one only supplies its own start and end channel values. // Sky gradient: deep purple to soft pink (red 40..160, green 20..60, blue 80..180). function fillGradient(palette: Palette, baseSlot: number, count: number, colorAt: (t: number) => Color32): void
Fills a run of palette slots with a smooth gradient. For each slot we compute t = i / (count - 1). Think of t as "how far along the gradient are we?" - the first slot gets t = 0 (the start), the last slot gets t = 1 (the end), and the slots between get evenly spaced fractions like 0.25 or 0.5 (exactly halfway). The colorAt callback turns that fraction into the actual Color32 for the slot, so each gradient only has to describe its own start and end colors.
@parampalette - The palette to write into.@parambaseSlot - The first slot of the gradient run.@paramcount - How many slots to fill.@paramcolorAt - Returns the color for progress t (0..1).
fillGradient
(
this.Demo.palette: Palette
@type{Palette | null}
palette
,
const C_SKY_BASE: 10C_SKY_BASE, const SKY_SLOTS: 10SKY_SLOTS, (t: numbert) => new new Color32(r?: number, g?: number, b?: number, a?: number): Color32
Creates a clamped 8-bit RGBA color.
@paramr - Red channel (0-255, defaults to 255).@paramg - Green channel (0-255, defaults to 255).@paramb - Blue channel (0-255, defaults to 255).@parama - Alpha channel (0-255, defaults to 255 = opaque).
Color32
(Math.floor(40 + t: numbert * 120), Math.floor(20 + t: numbert * 40), Math.floor(80 + t: numbert * 100)),
); // Fire gradient: dark red to bright yellow (red 80..255, green 0..200, blue 0..40). function fillGradient(palette: Palette, baseSlot: number, count: number, colorAt: (t: number) => Color32): void
Fills a run of palette slots with a smooth gradient. For each slot we compute t = i / (count - 1). Think of t as "how far along the gradient are we?" - the first slot gets t = 0 (the start), the last slot gets t = 1 (the end), and the slots between get evenly spaced fractions like 0.25 or 0.5 (exactly halfway). The colorAt callback turns that fraction into the actual Color32 for the slot, so each gradient only has to describe its own start and end colors.
@parampalette - The palette to write into.@parambaseSlot - The first slot of the gradient run.@paramcount - How many slots to fill.@paramcolorAt - Returns the color for progress t (0..1).
fillGradient
(
this.Demo.palette: Palette
@type{Palette | null}
palette
,
const C_FIRE_BASE: 30C_FIRE_BASE, const FIRE_SLOTS: 6FIRE_SLOTS, (t: numbert) => new new Color32(r?: number, g?: number, b?: number, a?: number): Color32
Creates a clamped 8-bit RGBA color.
@paramr - Red channel (0-255, defaults to 255).@paramg - Green channel (0-255, defaults to 255).@paramb - Blue channel (0-255, defaults to 255).@parama - Alpha channel (0-255, defaults to 255 = opaque).
Color32
(Math.floor(80 + t: numbert * 175), Math.floor(t: numbert * 200), Math.floor(t: numbert * 40)),
); // Water gradient: dark blue to bright cyan (red 0..60, green 40..200, blue 100..255). function fillGradient(palette: Palette, baseSlot: number, count: number, colorAt: (t: number) => Color32): void
Fills a run of palette slots with a smooth gradient. For each slot we compute t = i / (count - 1). Think of t as "how far along the gradient are we?" - the first slot gets t = 0 (the start), the last slot gets t = 1 (the end), and the slots between get evenly spaced fractions like 0.25 or 0.5 (exactly halfway). The colorAt callback turns that fraction into the actual Color32 for the slot, so each gradient only has to describe its own start and end colors.
@parampalette - The palette to write into.@parambaseSlot - The first slot of the gradient run.@paramcount - How many slots to fill.@paramcolorAt - Returns the color for progress t (0..1).
fillGradient
(
this.Demo.palette: Palette
@type{Palette | null}
palette
,
const C_WATER_BASE: 50C_WATER_BASE, const WATER_SLOTS: 8WATER_SLOTS, (t: numbert) => new new Color32(r?: number, g?: number, b?: number, a?: number): Color32
Creates a clamped 8-bit RGBA color.
@paramr - Red channel (0-255, defaults to 255).@paramg - Green channel (0-255, defaults to 255).@paramb - Blue channel (0-255, defaults to 255).@parama - Alpha channel (0-255, defaults to 255 = opaque).
Color32
(Math.floor(t: numbert * 60), Math.floor(40 + t: numbert * 160), Math.floor(100 + t: numbert * 155)),
); // Install the shared UI kit colors (panel fills, borders, headings, dim text). // applyTheme() writes 12 colors into slots 240..251 - far above every range the // engine cycles here (sky 10..19, fire 30..35, water 50..57), so the rotation // can never touch the UI theme. this.Demo.theme: nulltheme = import applyThemeapplyTheme(this.Demo.palette: Palette
@type{Palette | null}
palette
);
// Activate palette
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.paletteSet: (palette: Palette) => void
Stores the active engine palette. Use this to swap the **entire palette** (e.g. switch between a day and night theme). After this call the renderer uploads the new palette uniform on the next frame. **Palette-value swap (change what a slot looks like):** mutate the live {@link BT.palette } in place with `palette.set(slot, newColor)`. The renderer uploads dirty slots on the next frame; no `paletteSet()` or {@link BT.spritesRefresh } needed. **Palette-layout swap (same colors, different slot positions):** build a new palette with the same colors at new indices, call `paletteSet()`, then call {@link BT.spritesRefresh } so every sprite sheet re-maps its original RGBA pixels against the new slot layout.
@since1.0.3@parampalette - Palette to make active.
paletteSet
(this.Demo.palette: Palette
@type{Palette | null}
palette
);
// Start cycling effects // These run automatically each frame until we call BT.paletteClearEffects().
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.paletteCycle: (start: number, end: number, speed: number) => void
Starts rotating a range of palette entries at a constant speed. Classic water/fire/plasma animation technique. Runs indefinitely until canceled via {@link BT.paletteClearEffects } . Uses a fractional accumulator for sub-frame precision.
@since1.0.3@paramstart - First palette index in the cycling range (inclusive).@paramend - Last palette index in the cycling range (inclusive).@paramspeed - Steps per second. Positive = forward, negative = backward.
paletteCycle
(const C_SKY_BASE: 10C_SKY_BASE, const C_SKY_BASE: 10C_SKY_BASE + const SKY_SLOTS: 10SKY_SLOTS - 1, const SKY_SPEED: 0.5SKY_SPEED);
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.paletteCycle: (start: number, end: number, speed: number) => void
Starts rotating a range of palette entries at a constant speed. Classic water/fire/plasma animation technique. Runs indefinitely until canceled via {@link BT.paletteClearEffects } . Uses a fractional accumulator for sub-frame precision.
@since1.0.3@paramstart - First palette index in the cycling range (inclusive).@paramend - Last palette index in the cycling range (inclusive).@paramspeed - Steps per second. Positive = forward, negative = backward.
paletteCycle
(const C_FIRE_BASE: 30C_FIRE_BASE, const C_FIRE_BASE: 30C_FIRE_BASE + const FIRE_SLOTS: 6FIRE_SLOTS - 1, const FIRE_SPEED: -6FIRE_SPEED);
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.paletteCycle: (start: number, end: number, speed: number) => void
Starts rotating a range of palette entries at a constant speed. Classic water/fire/plasma animation technique. Runs indefinitely until canceled via {@link BT.paletteClearEffects } . Uses a fractional accumulator for sub-frame precision.
@since1.0.3@paramstart - First palette index in the cycling range (inclusive).@paramend - Last palette index in the cycling range (inclusive).@paramspeed - Steps per second. Positive = forward, negative = backward.
paletteCycle
(const C_WATER_BASE: 50C_WATER_BASE, const C_WATER_BASE: 50C_WATER_BASE + const WATER_SLOTS: 8WATER_SLOTS - 1, const WATER_SPEED: 4WATER_SPEED);
console.log('[PaletteCyclingDemo] Initialized'); return true; } /** * Periodically swaps two fire palette entries to demonstrate BT.paletteSwap(). */ Demo.update(): void
Periodically swaps two fire palette entries to demonstrate BT.paletteSwap().
update
() {
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;
    ... 106 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
;
// Every SWAP_INTERVAL ticks, swap two fire slots to demonstrate BT.paletteSwap(). // The pair is not random - it is computed from the tick counter, so each swap // picks a predictable pair: tick % FIRE_SLOTS and (tick + 3) % FIRE_SLOTS. if (const tick: numbertick - this.Demo.lastSwapTick: numberlastSwapTick >= const SWAP_INTERVAL: 180SWAP_INTERVAL) { // Pick two different slots within the fire range. this.Demo.swappedA: numberswappedA = const C_FIRE_BASE: 30C_FIRE_BASE + (const tick: numbertick % const FIRE_SLOTS: 6FIRE_SLOTS); this.Demo.swappedB: numberswappedB = const C_FIRE_BASE: 30C_FIRE_BASE + ((const tick: numbertick + 3) % const FIRE_SLOTS: 6FIRE_SLOTS); if (this.Demo.swappedA: numberswappedA !== this.Demo.swappedB: numberswappedB) {
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.paletteSwap: (indexA: number, indexB: number) => void
Instantly exchanges two palette entries. This is an immediate operation, not an animated effect. The visual change takes effect on the next frame.
@since1.0.3@paramindexA - First palette index.@paramindexB - Second palette index.
paletteSwap
(this.Demo.swappedA: numberswappedA, this.Demo.swappedB: numberswappedB);
this.Demo.showSwapLabel: booleanshowSwapLabel = true; } this.Demo.lastSwapTick: numberlastSwapTick = const tick: numbertick; } // Hide the swap label after 60 ticks (~1 second). if (this.Demo.showSwapLabel: booleanshowSwapLabel && const tick: numbertick - this.Demo.lastSwapTick: numberlastSwapTick > 60) { this.Demo.showSwapLabel: booleanshowSwapLabel = false; } } /** * Draws the three animated bands and their kit-panel headings. * No Color32 objects here - only palette indices. */ Demo.render(): void
Draws the three animated bands and their kit-panel headings. No Color32 objects here - only palette indices.
render
() {
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.clear: (paletteIndex: number) => void
Sets the frame clear color using a palette index. The renderer uses this color when clearing the full display at the start of the next frame.
@since0.1.0@parampaletteIndex - Palette index for the full-screen clear pass.
clear
(this.Demo.theme: nulltheme.bg);
this.Demo.renderSkyPanel(): void
Sky band: 10 horizontal stripes at the top, each using one sky slot. The slow cycling makes the twilight colors gently shift.
renderSkyPanel
();
this.Demo.renderFirePanel(): void
Fire band: 6 vertical columns in the middle section. Negative speed makes the colors cycle backward (rising flame illusion).
renderFirePanel
();
this.Demo.renderWaterPanel(): void
Water band: 8 vertical columns at the bottom. Forward cycling at 4 steps/sec makes colors flow like water.
renderWaterPanel
();
} /** * Sky band: 10 horizontal stripes at the top, each using one sky slot. * The slow cycling makes the twilight colors gently shift. */ Demo.renderSkyPanel(): void
Sky band: 10 horizontal stripes at the top, each using one sky slot. The slow cycling makes the twilight colors gently shift.
renderSkyPanel
() {
const const bandY: 6bandY = 6; const const stripeH: 5stripeH = 5; // The heading lives in a kit panel pinned to the band position. ui.end() draws // the panel right away, so the stripes drawn after it land ON TOP of the panel // background. ui.spacer() reserves empty rows for that artwork. import uiui.begin(import UI_ANCHORSUI_ANCHORS.TOP_LEFT, { x: numberx: 0, y: numbery: const bandY: 6bandY, width: numberwidth: 320 }); import uiui.panel('Sky (0.5 steps/sec, forward)'); import uiui.spacer(40); import uiui.end(); // Draw 10 horizontal stripes, repeated twice for fullness. for (let let row: numberrow = 0; let row: numberrow < 2; let row: numberrow++) { for (let let i: numberi = 0; let i: numberi < const SKY_SLOTS: 10SKY_SLOTS; let i: numberi++) { const const y: numbery = const bandY: 6bandY + 20 + (let row: numberrow * (const stripeH: 5stripeH * const SKY_SLOTS: 10SKY_SLOTS)) / 2 + let i: numberi * const stripeH: 5stripeH; // Only draw stripes that fit inside the panel's content area. if (const y: numbery + const stripeH: 5stripeH <= const bandY: 6bandY + 60) {
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.drawRectFill: (rect: Rect2i, paletteIndex: number) => void
Draws a filled rectangle.
@since0.1.0@paramrect - Rectangle bounds in display coordinates.@parampaletteIndex - Palette color index.
drawRectFill
(new new Rect2i(x?: number, y?: number, width?: number, height?: number): Rect2i
Creates an integer rectangle, truncating all inputs toward zero.
@paramx - Left-edge X coordinate (defaults to 0).@paramy - Top-edge Y coordinate (defaults to 0).@paramwidth - Width in pixels (defaults to 0).@paramheight - Height in pixels (defaults to 0).
Rect2i
(6, const y: numbery, 308, const stripeH: 5stripeH), const C_SKY_BASE: 10C_SKY_BASE + (let i: numberi % const SKY_SLOTS: 10SKY_SLOTS));
} } } } /** * Fire band: 6 vertical columns in the middle section. * Negative speed makes the colors cycle backward (rising flame illusion). */ Demo.renderFirePanel(): void
Fire band: 6 vertical columns in the middle section. Negative speed makes the colors cycle backward (rising flame illusion).
renderFirePanel
() {
const const bandY: 76bandY = 76; const const colW: anycolW = Math.floor(308 / const FIRE_SLOTS: 6FIRE_SLOTS); // Kit panel with the heading; the spacer reserves room for the fire columns. import uiui.begin(import UI_ANCHORSUI_ANCHORS.TOP_LEFT, { x: numberx: 0, y: numbery: const bandY: 76bandY, width: numberwidth: 320 }); import uiui.panel('Fire (-6 steps/sec, backward)'); import uiui.spacer(40); import uiui.end(); // Draw fire columns. for (let let i: numberi = 0; let i: numberi < const FIRE_SLOTS: 6FIRE_SLOTS; let i: numberi++) { // Each column is drawn with multiple rows of the same slot to make it taller. for (let let row: numberrow = 0; let row: numberrow < 8; let row: numberrow++) { const const x: numberx = 6 + let i: numberi * const colW: anycolW; const const y: numbery = const bandY: 76bandY + 20 + let row: numberrow * 5;
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.drawRectFill: (rect: Rect2i, paletteIndex: number) => void
Draws a filled rectangle.
@since0.1.0@paramrect - Rectangle bounds in display coordinates.@parampaletteIndex - Palette color index.
drawRectFill
(new new Rect2i(x?: number, y?: number, width?: number, height?: number): Rect2i
Creates an integer rectangle, truncating all inputs toward zero.
@paramx - Left-edge X coordinate (defaults to 0).@paramy - Top-edge Y coordinate (defaults to 0).@paramwidth - Width in pixels (defaults to 0).@paramheight - Height in pixels (defaults to 0).
Rect2i
(const x: numberx, const y: numbery, const colW: anycolW - 1, 5), const C_FIRE_BASE: 30C_FIRE_BASE + ((let i: numberi + let row: numberrow) % const FIRE_SLOTS: 6FIRE_SLOTS));
} } // Show swap label if active (shared caption helper, same as patterns.js). if (this.Demo.showSwapLabel: booleanshowSwapLabel) { import uiui.caption(6, const bandY: 76bandY + 44, `Swapped slots ${this.Demo.swappedA: numberswappedA} <-> ${this.Demo.swappedB: numberswappedB}`); } } /** * Water band: 8 vertical columns at the bottom. * Forward cycling at 4 steps/sec makes colors flow like water. */ Demo.renderWaterPanel(): void
Water band: 8 vertical columns at the bottom. Forward cycling at 4 steps/sec makes colors flow like water.
renderWaterPanel
() {
const const bandY: 146bandY = 146; const const colW: anycolW = Math.floor(308 / const WATER_SLOTS: 8WATER_SLOTS); // Kit panel with the heading; the spacer reserves room for the water tiles. import uiui.begin(import UI_ANCHORSUI_ANCHORS.TOP_LEFT, { x: numberx: 0, y: numbery: const bandY: 146bandY, width: numberwidth: 320 }); import uiui.panel('Water (4 steps/sec, forward)'); import uiui.spacer(54); import uiui.end(); // Draw water tiles in a grid pattern. for (let let row: numberrow = 0; let row: numberrow < 10; let row: numberrow++) { for (let let col: numbercol = 0; let col: numbercol < const WATER_SLOTS: 8WATER_SLOTS; let col: numbercol++) { const const x: numberx = 6 + let col: numbercol * const colW: anycolW; const const y: numbery = const bandY: 146bandY + 20 + let row: numberrow * 5; // Offset the slot index by the row to create a diagonal wave pattern. const const slot: numberslot = const C_WATER_BASE: 50C_WATER_BASE + ((let col: numbercol + let row: numberrow) % const WATER_SLOTS: 8WATER_SLOTS);
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.drawRectFill: (rect: Rect2i, paletteIndex: number) => void
Draws a filled rectangle.
@since0.1.0@paramrect - Rectangle bounds in display coordinates.@parampaletteIndex - Palette color index.
drawRectFill
(new new Rect2i(x?: number, y?: number, width?: number, height?: number): Rect2i
Creates an integer rectangle, truncating all inputs toward zero.
@paramx - Left-edge X coordinate (defaults to 0).@paramy - Top-edge Y coordinate (defaults to 0).@paramwidth - Width in pixels (defaults to 0).@paramheight - Height in pixels (defaults to 0).
Rect2i
(const x: numberx, const y: numbery, const colW: anycolW - 1, 5), const slot: numberslot);
} } // Explanatory text over the tiles (shared caption helper, same as patterns.js). import uiui.caption(6, const bandY: 146bandY + 58, 'BT.paletteCycle() runs automatically'); } } function bootstrap(DemoClass: DemoConstructor, options?: BootstrapOptions): Promise<boolean>
One-liner bootstrap function for BLIT386 demos. Handles canvas retrieval and engine initialization. Backend selection (WebGPU or software fallback) is managed internally by BTAPI. This function provides a streamlined way to start a demo with sensible defaults while allowing customization through options.
@since0.2.0@changed1.4.0 Calling `bootstrap()` again while already initialized now routes to a hot swap (via {@link registerHotReload}) when a Vite HMR context is registered, or logs a double-bootstrap guard and returns `false` otherwise - previously it silently started a second, unstoppable `GameLoop`.@changed1.7.0 Exposes `BT` on `window.BT` after bootstrap finishes, gated by {@link BootstrapOptions.exposeGlobal} (default: {@link BT.isDevMode}).@paramDemoClass - Demo class constructor implementing `IBTDemo` (optional `configure()` for hardware settings).@paramoptions - Optional configuration for IDs and callbacks.@returns`true` when the demo boots successfully; otherwise `false`.@example// Simplest usage - uses default IDs. bootstrap(MyDemo);@example// With custom options. bootstrap(MyDemo, { canvasID: 'custom-canvas', containerID: 'custom-container', onSuccess: () => console.log('Demo started!'), onError: (err) => analytics.trackError(err), });@example// Await the result. const success = await bootstrap(MyDemo); if (success) { console.log('Demo is running'); }
bootstrap
(class Demo
Demonstrates BT.paletteCycle() for automatic palette rotation, plus BT.paletteSwap() for instant entry exchange and BT.paletteClearEffects() for stopping all running effects.
@implementsIBTDemo
Demo
);