// Palette Animation: change palette entries every tick for instant visual effects.
// @description Change palette entries every tick to animate a whole scene at once, without redrawing any pixels.
//
// Part of the BLIT386 series.
//
// Prerequisites:
// Basics https://demos.blit386.dev/basics
// Primitives https://demos.blit386.dev/primitives
// Colors https://demos.blit386.dev/colors
// Palette Presets https://demos.blit386.dev/palette-presets
// (guide: https://blit386.dev/docs/guides/palette-presets)
//
// Live version: https://demos.blit386.dev/palette-animation
// Guide: https://blit386.dev/docs/guides/palette#runtime-palette-effects
//
// WHAT IS PALETTE ANIMATION?
//
// Old game hardware (Super Nintendo, Sega Genesis, Commodore 64) had strict rules:
// each pixel only stored a small number - a "palette index" pointing to one color slot.
// To animate colors, programmers changed what color was IN the slot, not what was on screen.
//
// Imagine 16 buckets of paint, each numbered. A painting only records the bucket number
// for every spot, not the actual color. To change the sky from blue to red, you just
// repaint bucket 5. Every sky-colored spot changes instantly - without touching the painting!
//
// That trick is called "palette animation". Modern engines don't need it, but it's a
// beautiful technique to understand, and BLIT386 lets you do it the same way.
//
// THE KEY RULE:
// render() writes palette indices (numbers) - never Color32 objects.
// update() computes new Color32 values and stores them in palette slots.
//
// The section headings and panels 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 slot
// this demo animates.
//
// WHAT YOU WILL SEE (four panels):
// 1. Scrolling gradient bar - 32 color slots hold a rainbow; the base hue rotates.
// 2. Fire column - colors stack up from black to red to yellow to white.
// 3. Flashing health bar - one slot alternates red / white every 8 ticks.
// 4. Cycling water strip - three blue-green slots ripple in sequence.
import { function bootstrap(DemoClass: DemoConstructor, options?: BootstrapOptions): Promise<boolean>One-liner bootstrap function for BLIT386 demos.
Handles canvas retrieval and engine initialization. Backend selection
(WebGPU or software fallback) is managed internally by BTAPI.
This function provides a streamlined way to start a demo with sensible defaults
while allowing customization through options.bootstrap, const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 106 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT, class Color32Mutable 32-bit RGBA color value with 8-bit channels.Color32, class Rect2iInteger rectangle for pixel-perfect bounds and regions.
Used throughout the engine for sprite regions, display-space bounds, and
zero-allocation geometry helpers. Both convenience getters and allocation-free
`*To()` helpers are provided so callers can choose between readability and
hot-path efficiency.Rect2i, class Vector2iInteger 2D vector for pixel-perfect positioning.
Used for points, sizes, directions, and camera offsets throughout the engine.
The API includes both allocation-free `*To()` / `*InPlace()` variants and
convenience methods that return new vectors.Vector2i } from 'blit386';
import { import applyThemeapplyTheme, import uiui, import UI_ANCHORSUI_ANCHORS } from './shared/ui.js';
/** @typedef {import('blit386').IBTDemo} IBTDemo */
/** @typedef {import('blit386').HardwareSettings} HardwareSettings */
/** @typedef {import('blit386').Palette} Palette */
// Gradient bar
// How many color slots the scrolling gradient uses.
// More slots = smoother rainbow; fewer slots = more "chunky".
const const GRAD_SLOTS: 32GRAD_SLOTS = 32;
// Width of each gradient swatch rectangle in pixels. The swatches sit inside a UI kit
// panel, so they share the panel's 308-pixel content width (320 minus the borders).
const const GRAD_SWATCH_W: anyGRAD_SWATCH_W = Math.floor(308 / const GRAD_SLOTS: 32GRAD_SLOTS); // ~9 px each
// Fire column
// How many color slots make up the fire gradient.
// The bottom of the fire is dark/black; the top is bright yellow-white.
const const FIRE_SLOTS: 20FIRE_SLOTS = 20;
// Height of each fire "band" rectangle in pixels.
const const FIRE_BAND_H: 4FIRE_BAND_H = 4;
// Width of the fire column in pixels.
const const FIRE_COL_W: 60FIRE_COL_W = 60;
// Water strip
// Three slots cycle in sequence to create a ripple shimmer.
const const WATER_SLOTS: 3WATER_SLOTS = 3;
// Health bar
// The health bar flashes every N ticks (8 ticks = about 0.13 seconds at 60 FPS).
const const FLASH_PERIOD: 8FLASH_PERIOD = 8;
// How low "health" must fall before the bar starts flashing.
// (Simulated health: counts down from HEALTH_MAX to 0, then loops.)
const const HEALTH_LOW: 30HEALTH_LOW = 30;
const const HEALTH_MAX: 100HEALTH_MAX = 100;
// How many ticks for one full health drain cycle.
const const HEALTH_DRAIN_TICKS: 360HEALTH_DRAIN_TICKS = 360; // ~6 seconds to drain completely.
// Palette slot constants - we group our palette like compartments in a paint box.
// Each animated section owns a range of slots that it fills in update() every tick.
// The shared UI kit adds its own 12 theme colors in slots 240..251 (see init()),
// safely above everything listed here.
// 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 four
// low slots, which init() fills by hand with fixed colors.
const const C_OVERLAY_BAR: 2C_OVERLAY_BAR = 2; // Overlay bar background (very dark navy).
const const C_OVERLAY_ERR: 3C_OVERLAY_ERR = 3; // Timing chart error bars (dark blue).
const const C_OVERLAY_TEXT: 4C_OVERLAY_TEXT = 4; // Overlay text and chart update bars (golden yellow).
const const C_OVERLAY_DIM: 5C_OVERLAY_DIM = 5; // Chart render/warning bars (cool gray-blue).
// Gradient section: 32 slots, one per swatch column.
// We update all 32 every tick to scroll the hue.
const const C_GRAD_BASE: 10C_GRAD_BASE = 10; // Slots 10..41.
// Fire section: 20 slots, one per horizontal band.
// Slot 10+GRAD_SLOTS = 42 might overlap, so we start fire at 50.
const const C_FIRE_BASE: 50C_FIRE_BASE = 50; // Slots 50..69.
// Health bar: one slot. We toggle it between red and white.
const const C_HEALTH_BAR: 80C_HEALTH_BAR = 80; // Slot 80.
// Water strip: three slots that cycle.
const const C_WATER_BASE: 90C_WATER_BASE = 90; // Slots 90..92.
/**
* Demonstrates the "palette animation" technique: change palette entries every tick
* to create scrolling gradients, fire, flashing effects, and rippling water
* all without touching the geometry drawn in render().
*
* @implements {IBTDemo}
*/
class class DemoDemonstrates the "palette animation" technique: change palette entries every tick
to create scrolling gradients, fire, flashing effects, and rippling water
all without touching the geometry drawn in render().Demo {
// The single palette used for all drawing.
/** @type {Palette | null} */
Demo.palette: Palette | nullpalette = null;
// Palette slot map for the shared UI kit theme, filled in init() by applyTheme().
// theme.bg, theme.border, theme.header, ... are palette indices ready for BT calls.
Demo.theme: nulltheme = null;
// Counts up by 1/60 every frame (in seconds).
// Used to drive continuous animation in update().
Demo.animTime: numberanimTime = 0;
// Simulated health value (0..100), drained over time.
Demo.health: numberhealth = const HEALTH_MAX: 100HEALTH_MAX;
// Which water slot is currently "brightest" (0, 1, or 2).
Demo.waterPhase: numberwaterPhase = 0;
// How many ticks since the water last advanced one step.
Demo.waterTick: numberwaterTick = 0;
/**
* Wider canvas, overlay palette grid (64 columns), and overlay style colors from
* the dedicated low slots (plus the animated health slot as a playful gap color).
*
* @returns {Partial<HardwareSettings>}
*/
Demo.configure(): Partial<HardwareSettings>Wider canvas, overlay palette grid (64 columns), and overlay style colors from
the dedicated low slots (plus the animated health slot as a playful gap color).configure() {
return {
displaySize: Vector2idisplaySize: new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(520, 390),
maxCanvasSize: Vector2imaxCanvasSize: new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(520 * 2, 390 * 2),
isOverlayPaletteEnabled: booleanisOverlayPaletteEnabled: true,
overlayPaletteColumns: numberoverlayPaletteColumns: 64,
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,
// The gap reuses the animated health slot, so it flashes with the demo.
gapPaletteIndex: numbergapPaletteIndex: const C_HEALTH_BAR: 80C_HEALTH_BAR,
},
isOverlayTimingChartEnabled: booleanisOverlayTimingChartEnabled: true,
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_DIM: 5C_OVERLAY_DIM,
warningPaletteIndex: numberwarningPaletteIndex: const C_OVERLAY_DIM: 5C_OVERLAY_DIM,
errorPaletteIndex: numbererrorPaletteIndex: const C_OVERLAY_ERR: 3C_OVERLAY_ERR,
tagPaletteIndex: numbertagPaletteIndex: const C_OVERLAY_TEXT: 4C_OVERLAY_TEXT,
},
};
}
/**
* Builds the palette with overlay slots, zeroed dynamic slots, and the shared UI
* theme, then primes one update() so every animated slot has real colors before
* the first render().
*
* @returns {Promise<boolean>}
*/
async Demo.init(): Promise<boolean>Builds the palette with overlay slots, zeroed dynamic slots, and the shared UI
theme, then primes one update() so every animated slot has real colors before
the first render().init() {
console.log('[PaletteAnimationDemo] Initializing...');
// Build the main palette
// Think of this like setting up your paint box before you start painting.
// Static entries (overlay colors, UI theme) go in now and never change.
// Dynamic entries (gradient, fire, health, water) start as black and get
// overwritten in update() every tick.
this.Demo.palette: Palette | nullpalette = const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 106 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.paletteCreate: (size?: number) => PaletteCreates a standalone palette instance.paletteCreate(256);
// Colors for the engine overlay (the stats HUD). These match the shared UI
// theme's look but live in low slots so configure() could reference them.
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_OVERLAY_BAR: 2C_OVERLAY_BAR, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(10, 12, 20)); // Very dark navy.
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_OVERLAY_ERR: 3C_OVERLAY_ERR, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(20, 24, 36)); // Dark blue.
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_OVERLAY_TEXT: 4C_OVERLAY_TEXT, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(255, 210, 80)); // Golden yellow.
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_OVERLAY_DIM: 5C_OVERLAY_DIM, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(120, 130, 160)); // Cool gray-blue.
// Initialize all dynamic slots to black (invisible for now).
// They will be filled with real colors on the very first update() call.
for (let let i: numberi = 0; let i: numberi < const GRAD_SLOTS: 32GRAD_SLOTS; let i: numberi++) {
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_GRAD_BASE: 10C_GRAD_BASE + let i: numberi, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(0, 0, 0));
}
for (let let i: numberi = 0; let i: numberi < const FIRE_SLOTS: 20FIRE_SLOTS; let i: numberi++) {
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_FIRE_BASE: 50C_FIRE_BASE + let i: numberi, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(0, 0, 0));
}
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_HEALTH_BAR: 80C_HEALTH_BAR, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(220, 40, 40));
for (let let i: numberi = 0; let i: numberi < const WATER_SLOTS: 3WATER_SLOTS; let i: numberi++) {
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_WATER_BASE: 90C_WATER_BASE + let i: numberi, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(0, 80, 160));
}
// Install the shared UI kit colors (panel fills, borders, headings, dim text).
// applyTheme() writes 12 colors into slots 240..251 - far above every range this
// demo animates (gradient 10..41, fire 50..69, health 80, water 90..92), so the
// palette animation can never overwrite the UI theme. The returned map remembers
// where each color landed (this.theme.bg, this.theme.header, ...).
this.Demo.theme: nulltheme = import applyThemeapplyTheme(this.Demo.palette: Palettepalette);
// Activate palette
// Tell the engine to use our palette from this point forward.
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) => voidStores the active engine palette.
Use this to swap the **entire palette** (e.g. switch between a day and night
theme). After this call the renderer uploads the new palette uniform on the
next frame.
**Palette-value swap (change what a slot looks like):** mutate the live
{@link
BT.palette
}
in place with `palette.set(slot, newColor)`. The renderer
uploads dirty slots on the next frame; no `paletteSet()` or
{@link
BT.spritesRefresh
}
needed.
**Palette-layout swap (same colors, different slot positions):** build a new
palette with the same colors at new indices, call `paletteSet()`, then call
{@link
BT.spritesRefresh
}
so every sprite sheet re-maps its original RGBA
pixels against the new slot layout.paletteSet(this.Demo.palette: Palettepalette);
// Run one update cycle so all dynamic slots have real colors before the first render.
// update() takes no arguments - this priming call still advances animTime by one tick,
// same as every regular call from the engine's game loop.
this.Demo.update(): voidCalled 60 times per second. Computes new Color32 values for every dynamic slot.
render() will never see Color32 - it only reads slot indices we set here.update();
console.log('[PaletteAnimationDemo] Initialized');
return true;
}
/**
* Called 60 times per second. Computes new Color32 values for every dynamic slot.
* render() will never see Color32 - it only reads slot indices we set here.
*/
Demo.update(): voidCalled 60 times per second. Computes new Color32 values for every dynamic slot.
render() will never see Color32 - it only reads slot indices we set here.update() {
// Advance the clock. animTime grows by 1/60 each frame.
this.Demo.animTime: numberanimTime += 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.deltaSeconds: numberFixed-step seconds per update tick.
Equivalent to `1 / BT.targetFPS` when `BT.targetFPS` is finite and positive.
Falls back to `1 / 60` when target FPS is non-finite or non-positive.deltaSeconds;
// Advance health drain.
// We simulate a health bar that empties over HEALTH_DRAIN_TICKS ticks,
// then resets to full so the demo loops forever.
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: numberCurrent fixed-update tick counter.
Increments once per engine update. Reset via
{@link
BT.ticksReset
}
.ticks;
this.Demo.health: numberhealth = const HEALTH_MAX: 100HEALTH_MAX - Math.floor((const tick: numbertick % const HEALTH_DRAIN_TICKS: 360HEALTH_DRAIN_TICKS) * (const HEALTH_MAX: 100HEALTH_MAX / const HEALTH_DRAIN_TICKS: 360HEALTH_DRAIN_TICKS));
// Panel 1: Scrolling gradient
// We rotate a base hue forward every frame so the gradient appears to scroll.
// animTime * 60 gives us degrees per second (one full rotation per ~6 seconds).
this.Demo.updateGradient(): voidScrolling gradient: rotates 32 hue slots so the rainbow appears to slide across.
The "base hue" advances each frame; each slot gets a hue slightly ahead of the previous.updateGradient();
// Panel 2: Fire column
// Each slot maps to a position along the fire column.
// Lower slots = closer to the bottom = darker/cooler colors.
this.Demo.updateFire(): voidFire column: each slot represents a horizontal band of flame.
The bottom is black/dark red; moving up transitions through orange to bright yellow-white.
We shift the transition point over time so the flame flickers.updateFire();
// Panel 3: Flashing health bar
// The slot alternates between red and white based on the tick count.
this.Demo.updateHealthBar(tick: number): voidHealth bar: toggles one slot between red and near-white every FLASH_PERIOD ticks.
Only flashes when health is critically low.updateHealthBar(const tick: numbertick);
// Panel 4: Cycling water
// Three slots take turns being the bright highlight.
this.Demo.updateWater(tick: number): voidWater strip: three slots take turns being the brightest highlight.
Each slot cycles: bright -> medium -> dim -> bright -> ...
The phases are offset by one slot so the bright spot appears to travel.updateWater(const tick: numbertick);
}
/**
* Draws all four panels. Only palette indices appear here - no Color32 objects.
*/
Demo.render(): voidDraws all four panels. Only palette indices appear here - no Color32 objects.render() {
// Clear the screen with the UI theme's dark background.
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) => voidSets the frame clear color using a palette index.
The renderer uses this color when clearing the full display at the start
of the next frame.clear(this.Demo.theme: nulltheme.bg);
// Draw each of the four panels.
this.Demo.renderGradientPanel(): voidPanel 1: Scrolling gradient bar.
A UI kit panel holds the heading; 32 thin rectangles sit inside it, each using a
different palette slot. Because update() rotates the hues in those slots, the bar
appears to scroll.renderGradientPanel();
this.Demo.renderFirePanel(): voidPanel 2: Fire column.
FIRE_SLOTS horizontal bands stacked vertically; the colors change in update() to flicker.renderFirePanel();
this.Demo.renderHealthPanel(): voidPanel 3: Flashing health bar.
One palette slot toggles between red and white when health is critically low.renderHealthPanel();
this.Demo.renderWaterPanel(): voidPanel 4: Cycling water strip.
Three adjacent rectangles each use one of the three water palette slots.
The brightness cycles across them to look like a ripple.renderWaterPanel();
}
/**
* Scrolling gradient: rotates 32 hue slots so the rainbow appears to slide across.
* The "base hue" advances each frame; each slot gets a hue slightly ahead of the previous.
*/
Demo.updateGradient(): voidScrolling gradient: rotates 32 hue slots so the rainbow appears to slide across.
The "base hue" advances each frame; each slot gets a hue slightly ahead of the previous.updateGradient() {
// Base hue grows over time. Math.floor() converts to a whole number of degrees.
// % 360 keeps hue in the 0..359 range (a full color wheel).
const const baseHue: numberbaseHue = Math.floor(this.Demo.animTime: numberanimTime * 60) % 360;
for (let let i: numberi = 0; let i: numberi < const GRAD_SLOTS: 32GRAD_SLOTS; let i: numberi++) {
// Each slot is spread evenly around the color wheel.
// (i / GRAD_SLOTS) * 360 spaces 32 hues evenly over 360 degrees.
const const hue: numberhue = (const baseHue: numberbaseHue + (let i: numberi / const GRAD_SLOTS: 32GRAD_SLOTS) * 360) % 360;
// Color32.fromHSL(hue, saturation, lightness):
// hue 0..360 = position on the color wheel (0=red, 120=green, 240=blue)
// saturation 0..100 = how vivid the color is (0=gray, 100=pure rainbow)
// lightness 0..100 = how bright (0=black, 50=pure color, 100=white)
this.Demo.palette: Palette | nullpalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_GRAD_BASE: 10C_GRAD_BASE + let i: numberi, class Color32Mutable 32-bit RGBA color value with 8-bit channels.Color32.Color32.fromHSL(h: number, s: number, l: number, a?: number): Color32Creates a color from HSL values.fromHSL(const hue: numberhue, 90, 55));
}
}
/**
* Fire column: each slot represents a horizontal band of flame.
* The bottom is black/dark red; moving up transitions through orange to bright yellow-white.
* We shift the transition point over time so the flame flickers.
*/
Demo.updateFire(): voidFire column: each slot represents a horizontal band of flame.
The bottom is black/dark red; moving up transitions through orange to bright yellow-white.
We shift the transition point over time so the flame flickers.updateFire() {
for (let let i: numberi = 0; let i: numberi < const FIRE_SLOTS: 20FIRE_SLOTS; let i: numberi++) {
// t is 0 at the bottom slot, 1 at the top slot.
const const t: numbert = let i: numberi / (const FIRE_SLOTS: 20FIRE_SLOTS - 1);
// Add a gentle flicker by modulating the transition with a sine wave.
// Math.sin() returns -1..1; we scale and shift it to 0..0.15 for a subtle wobble.
const const flicker: numberflicker = (Math.sin(this.Demo.animTime: numberanimTime * 7 + let i: numberi * 0.8) + 1) * 0.075;
// Apply flicker to t, clamped between 0 and 1.
const const ft: anyft = Math.min(1, Math.max(0, const t: numbert + const flicker: numberflicker));
this.Demo.palette: Palette | nullpalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_FIRE_BASE: 50C_FIRE_BASE + let i: numberi, this.Demo.fireColor(ft: number): Color32Maps one band position ft (0..1) to a fire color.
We blend through a four-color gradient:
0.0 = black (cold, no flame)
0.3 = dark red (embers just starting)
0.6 = bright orange (active flame)
1.0 = pale yellow (hottest, near the tip)fireColor(const ft: anyft));
}
}
/**
* Maps one band position ft (0..1) to a fire color.
* We blend through a four-color gradient:
* 0.0 = black (cold, no flame)
* 0.3 = dark red (embers just starting)
* 0.6 = bright orange (active flame)
* 1.0 = pale yellow (hottest, near the tip)
*
* @param {number} ft - Position along the flame, 0 at the bottom, 1 at the top.
* @returns {Color32} The blended color for that position.
*/
Demo.fireColor(ft: number): Color32Maps one band position ft (0..1) to a fire color.
We blend through a four-color gradient:
0.0 = black (cold, no flame)
0.3 = dark red (embers just starting)
0.6 = bright orange (active flame)
1.0 = pale yellow (hottest, near the tip)fireColor(ft: number- Position along the flame, 0 at the bottom, 1 at the top.ft) {
if (ft: number- Position along the flame, 0 at the bottom, 1 at the top.ft < 0.3) {
// Black to dark red.
const const s: numbers = ft: number- Position along the flame, 0 at the bottom, 1 at the top.ft / 0.3; // Rescales 0..0.3 to 0..1.
return new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(Math.floor(const s: numbers * 160), 0, 0);
}
if (ft: number- Position along the flame, 0 at the bottom, 1 at the top.ft < 0.6) {
// Dark red to bright orange.
const const s: numbers = (ft: number- Position along the flame, 0 at the bottom, 1 at the top.ft - 0.3) / 0.3;
return new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(160 + Math.floor(const s: numbers * 95), Math.floor(const s: numbers * 100), 0);
}
// Orange to pale yellow-white.
const const s: numbers = (ft: number- Position along the flame, 0 at the bottom, 1 at the top.ft - 0.6) / 0.4;
return new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(255, 100 + Math.floor(const s: numbers * 155), Math.floor(const s: numbers * 120));
}
/**
* Health bar: toggles one slot between red and near-white every FLASH_PERIOD ticks.
* Only flashes when health is critically low.
*
* @param {number} tick - Current tick count from BT.ticks.
*/
Demo.updateHealthBar(tick: number): voidHealth bar: toggles one slot between red and near-white every FLASH_PERIOD ticks.
Only flashes when health is critically low.updateHealthBar(tick: number- Current tick count from BT.ticks.tick) {
if (this.Demo.health: numberhealth <= const HEALTH_LOW: 30HEALTH_LOW) {
// Flash! % is the remainder operator: tick % FLASH_PERIOD gives 0..(FLASH_PERIOD-1).
// Math.floor(tick / FLASH_PERIOD) % 2 alternates between 0 and 1 every FLASH_PERIOD ticks.
const const flashOn: booleanflashOn = Math.floor(tick: number- Current tick count from BT.ticks.tick / const FLASH_PERIOD: 8FLASH_PERIOD) % 2 === 0;
this.Demo.palette: Palette | nullpalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_HEALTH_BAR: 80C_HEALTH_BAR, const flashOn: booleanflashOn ? new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(255, 50, 50) : new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(255, 255, 255));
} else {
// Healthy: steady red.
this.Demo.palette: Palette | nullpalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_HEALTH_BAR: 80C_HEALTH_BAR, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(200, 40, 40));
}
}
/**
* Water strip: three slots take turns being the brightest highlight.
* Each slot cycles: bright -> medium -> dim -> bright -> ...
* The phases are offset by one slot so the bright spot appears to travel.
*
* @param {number} tick - Current tick count from BT.ticks.
*/
Demo.updateWater(tick: number): voidWater strip: three slots take turns being the brightest highlight.
Each slot cycles: bright -> medium -> dim -> bright -> ...
The phases are offset by one slot so the bright spot appears to travel.updateWater(tick: number- Current tick count from BT.ticks.tick) {
// Advance the ripple every 8 ticks (about 7 ripples per second).
if (tick: number- Current tick count from BT.ticks.tick - this.Demo.waterTick: numberwaterTick >= 8) {
this.Demo.waterPhase: numberwaterPhase = (this.Demo.waterPhase: numberwaterPhase + 1) % const WATER_SLOTS: 3WATER_SLOTS;
this.Demo.waterTick: numberwaterTick = tick: number- Current tick count from BT.ticks.tick;
}
for (let let i: numberi = 0; let i: numberi < const WATER_SLOTS: 3WATER_SLOTS; let i: numberi++) {
// The ripple highlights one slot at a time.
// phase distance: how far is slot i from the current bright spot?
const const dist: numberdist = (let i: numberi - this.Demo.waterPhase: numberwaterPhase + const WATER_SLOTS: 3WATER_SLOTS) % const WATER_SLOTS: 3WATER_SLOTS;
// dist == 0 -> brightest, dist == 1 -> medium, dist == 2 -> darkest
let let color: anycolor;
if (const dist: numberdist === 0) {
// Bright highlight - the "wave crest".
let color: anycolor = new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(80, 200, 255);
} else if (const dist: numberdist === 1) {
// Medium shade - just before or after the crest.
let color: anycolor = new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(30, 100, 200);
} else {
// Dark trough - between ripples.
let color: anycolor = new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(10, 40, 120);
}
this.Demo.palette: Palette | nullpalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_WATER_BASE: 90C_WATER_BASE + let i: numberi, let color: Color32color);
}
}
/**
* Panel 1: Scrolling gradient bar.
* A UI kit panel holds the heading; 32 thin rectangles sit inside it, each using a
* different palette slot. Because update() rotates the hues in those slots, the bar
* appears to scroll.
*/
Demo.renderGradientPanel(): voidPanel 1: Scrolling gradient bar.
A UI kit panel holds the heading; 32 thin rectangles sit inside it, each using a
different palette slot. Because update() rotates the hues in those slots, the bar
appears to scroll.renderGradientPanel() {
const const bandY: 6bandY = 6;
// The heading and subtitle live in a kit panel pinned to the band position.
// ui.end() draws the panel right away, so the swatches 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('Scrolling Gradient');
import uiui.label('Hues rotate in update() each tick', { color: stringcolor: 'dim' });
import uiui.spacer(18);
import uiui.end();
// Draw one rectangle per gradient slot, side by side inside the panel.
for (let let i: numberi = 0; let i: numberi < const GRAD_SLOTS: 32GRAD_SLOTS; let i: numberi++) {
// Each swatch is GRAD_SWATCH_W pixels wide and 14 pixels tall, starting
// 6 pixels in so the strip clears the panel border.
const const x: numberx = 6 + let i: numberi * const GRAD_SWATCH_W: anyGRAD_SWATCH_W;
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) => voidDraws a filled rectangle.drawRectFill(new new Rect2i(x?: number, y?: number, width?: number, height?: number): Rect2iCreates an integer rectangle, truncating all inputs toward zero.Rect2i(const x: numberx, const bandY: 6bandY + 38, const GRAD_SWATCH_W: anyGRAD_SWATCH_W, 14), const C_GRAD_BASE: 10C_GRAD_BASE + let i: numberi);
}
}
/**
* Panel 2: Fire column.
* FIRE_SLOTS horizontal bands stacked vertically; the colors change in update() to flicker.
*/
Demo.renderFirePanel(): voidPanel 2: Fire column.
FIRE_SLOTS horizontal bands stacked vertically; the colors change in update() to flicker.renderFirePanel() {
const const bandY: 70bandY = 70;
// Kit panel with the heading; the spacer reserves room for the 80-pixel column.
import uiui.begin(import UI_ANCHORSUI_ANCHORS.TOP_LEFT, { x: numberx: 0, y: numbery: const bandY: 70bandY, width: numberwidth: 320 });
import uiui.panel('Fire Column');
import uiui.label('Color stack shifts upward in update()', { color: stringcolor: 'dim' });
import uiui.spacer(86);
import uiui.end();
// Fire bands, from bottom (slot 0 = darkest) to top (slot FIRE_SLOTS-1 = brightest).
// We draw them from bottom up so slot 0 is at the base of the column.
const const colX: 6colX = 6;
const const colBottom: numbercolBottom = const bandY: 70bandY + 118;
for (let let i: numberi = 0; let i: numberi < const FIRE_SLOTS: 20FIRE_SLOTS; let i: numberi++) {
// i=0 -> bottom of column, i=FIRE_SLOTS-1 -> top.
// Each band is FIRE_BAND_H pixels tall.
const const y: numbery = const colBottom: numbercolBottom - (let i: numberi + 1) * const FIRE_BAND_H: 4FIRE_BAND_H;
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 106 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.drawRectFill: (rect: Rect2i, paletteIndex: number) => voidDraws a filled rectangle.drawRectFill(new new Rect2i(x?: number, y?: number, width?: number, height?: number): Rect2iCreates an integer rectangle, truncating all inputs toward zero.Rect2i(const colX: 6colX, const y: numbery, const FIRE_COL_W: 60FIRE_COL_W, const FIRE_BAND_H: 4FIRE_BAND_H), const C_FIRE_BASE: 50C_FIRE_BASE + let i: numberi);
}
// Explanatory notes beside the column, lined up with the parts they describe:
// the brightest band is at the top of the column, the darkest at the bottom.
// "Band" here means the position in the fire stack (0..19), not the palette
// slot number - the actual slots are C_FIRE_BASE + band, i.e. 50..69.
const const noteX: numbernoteX = const colX: 6colX + const FIRE_COL_W: 60FIRE_COL_W + 6;
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.systemPrint: (pos: Vector2i, paletteIndex: number, text: string) => voidDraws text using the built-in 6x14 system font.
The system font covers printable ASCII (characters 32-126). For custom
bitmap fonts with proportional glyphs, use
{@link
BT.printFont
}
instead.systemPrint(new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(const noteX: numbernoteX, const bandY: 70bandY + 44), this.Demo.theme: nulltheme.dim, 'band 19 = white');
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.systemPrint: (pos: Vector2i, paletteIndex: number, text: string) => voidDraws text using the built-in 6x14 system font.
The system font covers printable ASCII (characters 32-126). For custom
bitmap fonts with proportional glyphs, use
{@link
BT.printFont
}
instead.systemPrint(new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(const noteX: numbernoteX, const bandY: 70bandY + 74), this.Demo.theme: nulltheme.dim, '... = red');
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.systemPrint: (pos: Vector2i, paletteIndex: number, text: string) => voidDraws text using the built-in 6x14 system font.
The system font covers printable ASCII (characters 32-126). For custom
bitmap fonts with proportional glyphs, use
{@link
BT.printFont
}
instead.systemPrint(new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(const noteX: numbernoteX, const bandY: 70bandY + 104), this.Demo.theme: nulltheme.dim, 'band 0 = black');
}
/**
* Panel 3: Flashing health bar.
* One palette slot toggles between red and white when health is critically low.
*/
Demo.renderHealthPanel(): voidPanel 3: Flashing health bar.
One palette slot toggles between red and white when health is critically low.renderHealthPanel() {
const const bandY: 202bandY = 202;
// Kit panel: heading, a spacer for the bar artwork, then the live status line.
// The status text turns warm orange when health is critical, dim gray otherwise.
const const isCritical: booleanisCritical = this.Demo.health: numberhealth <= const HEALTH_LOW: 30HEALTH_LOW;
import uiui.begin(import UI_ANCHORSUI_ANCHORS.TOP_LEFT, { x: numberx: 0, y: numbery: const bandY: 202bandY, width: numberwidth: 320 });
import uiui.panel('Flashing Health Bar');
import uiui.spacer(18);
import uiui.label(const isCritical: booleanisCritical ? 'CRITICAL! slot 80 flashes red/white' : 'Healthy: slot 80 = steady red', {
color: stringcolor: const isCritical: booleanisCritical ? 'warm' : 'dim',
});
import uiui.end();
// Compute the width of the filled portion from the current health value.
// health / HEALTH_MAX is a fraction from 0 to 1; multiply by max bar width (200 px).
const const barMaxW: 200barMaxW = 200;
const const barW: anybarW = Math.max(1, Math.floor((this.Demo.health: numberhealth / const HEALTH_MAX: 100HEALTH_MAX) * const barMaxW: 200barMaxW));
// Background trough (dark, always full width) with a theme-colored outline.
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) => voidDraws a filled rectangle.drawRectFill(new new Rect2i(x?: number, y?: number, width?: number, height?: number): Rect2iCreates an integer rectangle, truncating all inputs toward zero.Rect2i(6, const bandY: 202bandY + 22, const barMaxW: 200barMaxW, 12), this.Demo.theme: nulltheme.bg);
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 106 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.drawRect: (rect: Rect2i, paletteIndex: number) => voidDraws an unfilled rectangle outline.drawRect(new new Rect2i(x?: number, y?: number, width?: number, height?: number): Rect2iCreates an integer rectangle, truncating all inputs toward zero.Rect2i(6, const bandY: 202bandY + 22, const barMaxW: 200barMaxW, 12), this.Demo.theme: nulltheme.border);
// Filled bar - uses C_HEALTH_BAR, which flashes in update() when health is low.
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) => voidDraws a filled rectangle.drawRectFill(new new Rect2i(x?: number, y?: number, width?: number, height?: number): Rect2iCreates an integer rectangle, truncating all inputs toward zero.Rect2i(6, const bandY: 202bandY + 22, const barW: anybarW, 12), const C_HEALTH_BAR: 80C_HEALTH_BAR);
// Health value as text, in the theme's amber heading color.
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 106 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.systemPrint: (pos: Vector2i, paletteIndex: number, text: string) => voidDraws text using the built-in 6x14 system font.
The system font covers printable ASCII (characters 32-126). For custom
bitmap fonts with proportional glyphs, use
{@link
BT.printFont
}
instead.systemPrint(new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(212, const bandY: 202bandY + 21), this.Demo.theme: nulltheme.header, `HP: ${this.Demo.health: numberhealth}`);
}
/**
* Panel 4: Cycling water strip.
* Three adjacent rectangles each use one of the three water palette slots.
* The brightness cycles across them to look like a ripple.
*/
Demo.renderWaterPanel(): voidPanel 4: Cycling water strip.
Three adjacent rectangles each use one of the three water palette slots.
The brightness cycles across them to look like a ripple.renderWaterPanel() {
const const bandY: 266bandY = 266;
// Kit panel with the heading; the spacer reserves room for the tile strip.
import uiui.begin(import UI_ANCHORSUI_ANCHORS.TOP_LEFT, { x: numberx: 0, y: numbery: const bandY: 266bandY, width: numberwidth: 320 });
import uiui.panel('Cycling Water Strip');
import uiui.label('3 slots (90..92) ripple in sequence', { color: stringcolor: 'dim' });
import uiui.spacer(18);
import uiui.end();
// Draw 15 water tiles (5 repetitions of the 3-slot cycle) to make a wide strip.
const const tileW: 18tileW = 18;
const const tileH: 14tileH = 14;
const const totalTiles: 15totalTiles = 15;
for (let let i: numberi = 0; let i: numberi < const totalTiles: 15totalTiles; let i: numberi++) {
// Map tile index to one of the 3 water slots using remainder (%).
const const slot: numberslot = const C_WATER_BASE: 90C_WATER_BASE + (let i: numberi % const WATER_SLOTS: 3WATER_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) => voidDraws a filled rectangle.drawRectFill(new new Rect2i(x?: number, y?: number, width?: number, height?: number): Rect2iCreates an integer rectangle, truncating all inputs toward zero.Rect2i(6 + let i: numberi * const tileW: 18tileW, const bandY: 266bandY + 38, const tileW: 18tileW - 1, const tileH: 14tileH), const slot: numberslot);
}
}
}
// 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.bootstrap(class DemoDemonstrates the "palette animation" technique: change palette entries every tick
to create scrolling gradients, fire, flashing effects, and rippling water
all without touching the geometry drawn in render().Demo);