// Colors Demo - a deep dive into Color32 and palettes in BLIT386.
// @description A deep dive into Color32: named colors, HSL, and interpolating between two colors.
//
// Part of the BLIT386 demo series, written for young learners (around 12)
// who are getting comfortable with code. You will see:
//
// - Named shortcut colors (Color32.red and friends - static properties, not function calls)
// - HSL: another way to pick colors (hue, saturation, lightness) and a scrolling rainbow
// - Lerp: smoothly sliding between two colors (like a dimmer between two lights)
//
// We learned about the demo lifecycle, Vector2i, Rect2i, and clearing the screen in the Basics demo:
// https://demos.blit386.dev/basics
//
// Live version: https://demos.blit386.dev/colors
// Guide: https://blit386.dev/docs/api/core-types#color32
//
// IMPORTANT - palettes and how they changed from older demos:
//
// The engine now uses a "palette" - a table of up to 256 numbered colors.
// Instead of passing a Color32 to every draw call, you pick a number (an "index")
// from the palette. Think of it like numbered paint cans: you choose which can to use,
// not the exact mix of paint every time you pick up the brush.
//
// Static colors (named swatches, overlay text) go into the palette once during init().
// Animated colors (HSL rainbow, lerp gradient, pulse) are recalculated every tick
// inside update() and written back into their reserved palette slots.
// render() only ever uses palette index numbers - no Color32 objects there.
//
// The numbered section headers are drawn with the shared UI kit (src/shared/ui.js),
// which parks its own twelve colors in high slots 240-251 - far away from every slot
// this lesson uses. The swatches and their little labels stay hand-drawn on purpose:
// they ARE the lesson.
//
// IMPORTANT - update() ticks vs render() frames:
// update() runs at a fixed rate (here, 60 times per second when the tab is active).
// Each call to update() is one "tick". Our animTime adds 1/60 on every tick, so after
// 60 ticks (about one second), animTime is about 1.0. That is time measured in ticks,
// not in how often the monitor redraws. render() can run a different number of times
// per second on high-refresh screens, but animTime still only changes inside update().
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, type Color32 = Color32
class Color32
Mutable 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';
// The shared demo UI kit: applyTheme() installs the series' standard UI colors into the
// palette, and ui.caption() prints the section headers with them. We met the kit in the
// Basics demo: https://demos.blit386.dev/basics
import { import applyThemeapplyTheme, import uiui } from './shared/ui.js';
/** @typedef {import('blit386').IBTDemo} IBTDemo */
/** @typedef {import('blit386').HardwareSettings} HardwareSettings */
/** @typedef {import('blit386').Palette} Palette */
/** @typedef {import('blit386').Color32} Color32 */
//
// These numbers are the palette "addresses". We name them so the code is readable.
// Index 0 is always transparent and reserved - never assign to it.
// Basic colors (set once in init, never change).
const const C_WHITE: 1C_WHITE = 1; // Pure white - the WHT swatch and labels on dark swatches.
const const C_BG: 2C_BG = 2; // Dark gray-blue background.
const const C_BLACK: 3C_BLACK = 3; // Pure black - labels on light-colored swatches.
const const C_RED: 4C_RED = 4; // Color32.red - (255, 0, 0).
const const C_GREEN_N: 5C_GREEN_N = 5; // Color32.green - (0, 255, 0).
const const C_BLUE_N: 6C_BLUE_N = 6; // Color32.blue - (0, 0, 255).
const const C_YELLOW_N: 7C_YELLOW_N = 7; // Color32.yellow - (255, 255, 0).
const const C_CYAN_N: 8C_CYAN_N = 8; // Color32.cyan - (0, 255, 255).
const const C_MAGENTA_N: 9C_MAGENTA_N = 9; // Color32.magenta - (255, 0, 255).
// Overlay text color: a muted purple, set once in init() like the basic colors above.
const const C_OVERLAY_TEXT: 15C_OVERLAY_TEXT = 15; // (200, 80, 200, 140) - semi-transparent purple.
// Lerp endpoints (the two colors being blended).
const const C_LERP_A: 18C_LERP_A = 18; // (180, 40, 220) - purple.
const const C_LERP_B: 19C_LERP_B = 19; // (40, 220, 160) - teal.
// Static overlay bar color - never animated (configure() needs a fixed slot).
const const C_OVERLAY_BAR: 20C_OVERLAY_BAR = 20; // Soft blue-gray for the engine overlay background strip.
// Dynamic slots - recalculated every tick in update().
// HSL rainbow strip: 64 hue slots covering the full 0..360 degree color wheel.
// Slot C_HSL_BASE+i represents the color for column group i.
const const C_HSL_BASE: 30C_HSL_BASE = 30;
const const HSL_SLOTS: 64HSL_SLOTS = 64; // 64 slots * (320/64 ≈ 5 pixels wide each) covers the screen.
// Lerp gradient bar: 32 color steps blending from C_LERP_A to C_LERP_B.
const const C_LERP_BASE: 94C_LERP_BASE = 94;
const const LERP_SLOTS: 32LERP_SLOTS = 32;
// Pulse slot: a single color that breathes back and forth between A and B.
const const C_PULSE: 126C_PULSE = 126;
/**
* Shows how Color32 works: named colors, HSL rainbow, and lerp.
* All animated colors are computed in update() and stored in palette slots.
* render() uses only palette index numbers - no Color32 objects there.
*
* @implements {IBTDemo}
*/
class class DemoShows how Color32 works: named colors, HSL rainbow, and lerp.
All animated colors are computed in update() and stored in palette slots.
render() uses only palette index numbers - no Color32 objects there.Demo {
// animTime is "how many seconds of game time have passed".
// We only change it in update(), so it follows logical time, not drawing time.
Demo.animTime: numberanimTime = 0;
// The palette holds all the colors we are allowed to draw with.
// Imagine it as a box of 256 numbered paint cans.
/** @type {Palette | null} */
Demo.palette: Palette | nullpalette = null;
// theme remembers which palette slots the shared UI kit colors landed in.
// applyTheme() in init() fills it with a map like { bg, text, dim, header, ... }.
Demo.theme: nulltheme = null;
// The two Color32 objects used to compute the lerp gradient.
// We store them here so update() can call colorA.lerp(colorB, t) every tick.
/** @type {Color32 | null} */
Demo.lerpColorA: Color32 | nulllerpColorA = null;
/** @type {Color32 | null} */
Demo.lerpColorB: Color32 | nulllerpColorB = null;
/**
* Optional engine settings. We keep the default 320x240 screen and show the
* palette grid in the overlay with 4 visible rows (scroll for the rest).
*
* @returns {Partial<HardwareSettings>}
*/
Demo.configure(): Partial<HardwareSettings>Optional engine settings. We keep the default 320x240 screen and show the
palette grid in the overlay with 4 visible rows (scroll for the rest).configure() {
return {
isOverlayPaletteEnabled: booleanisOverlayPaletteEnabled: true,
overlayPaletteRowsVisible: numberoverlayPaletteRowsVisible: 4,
overlayStyle: {
barPaletteIndex: number;
textPaletteIndex: number;
gapPaletteIndex: number;
}
overlayStyle: {
// Dedicated static slot - not C_LERP_BASE, which update() rewrites every tick.
barPaletteIndex: numberbarPaletteIndex: const C_OVERLAY_BAR: 20C_OVERLAY_BAR,
textPaletteIndex: numbertextPaletteIndex: const C_OVERLAY_TEXT: 15C_OVERLAY_TEXT,
gapPaletteIndex: numbergapPaletteIndex: const C_BLACK: 3C_BLACK,
},
};
}
/**
* Sets up the palette and prepares lerp color objects.
*
* @returns {Promise<boolean>}
*/
async Demo.init(): Promise<boolean>Sets up the palette and prepares lerp color objects.init() {
// Step 1: Create the palette
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);
// Step 2: Fill in static colors
// Basic colors.
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_WHITE: 1C_WHITE, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(255, 255, 255));
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_BG: 2C_BG, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(24, 28, 40));
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_BLACK: 3C_BLACK, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(0, 0, 0));
// Named shortcut colors: Color32.red, Color32.green, etc. are static properties
// on the Color32 class (ready-made Color32 objects). Copy them into the palette
// so render() can draw with index numbers instead of passing Color32 each time.
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_RED: 4C_RED, class Color32Mutable 32-bit RGBA color value with 8-bit channels.Color32.Color32.red: Color32Pure red color (255, 0, 0, 255).
Cached frozen singleton - do not modify.red);
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_GREEN_N: 5C_GREEN_N, class Color32Mutable 32-bit RGBA color value with 8-bit channels.Color32.Color32.green: Color32Pure green color (0, 255, 0, 255).
Cached frozen singleton - do not modify.green);
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_BLUE_N: 6C_BLUE_N, class Color32Mutable 32-bit RGBA color value with 8-bit channels.Color32.Color32.blue: Color32Pure blue color (0, 0, 255, 255).
Cached frozen singleton - do not modify.blue);
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_YELLOW_N: 7C_YELLOW_N, class Color32Mutable 32-bit RGBA color value with 8-bit channels.Color32.Color32.yellow: Color32Yellow color (255, 255, 0, 255).
Cached frozen singleton - do not modify.yellow);
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_CYAN_N: 8C_CYAN_N, class Color32Mutable 32-bit RGBA color value with 8-bit channels.Color32.Color32.cyan: Color32Cyan color (0, 255, 255, 255).
Cached frozen singleton - do not modify.cyan);
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_MAGENTA_N: 9C_MAGENTA_N, class Color32Mutable 32-bit RGBA color value with 8-bit channels.Color32.Color32.magenta: Color32Magenta color (255, 0, 255, 255).
Cached frozen singleton - do not modify.magenta);
// Overlay text color. The fourth argument to Color32 is alpha: 255 = fully
// solid, 0 = fully invisible.
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_OVERLAY_TEXT: 15C_OVERLAY_TEXT, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(200, 80, 200, 140));
// Lerp endpoints - the two colors the gradient blends between.
this.Demo.lerpColorA: Color32 | nulllerpColorA = new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(180, 40, 220); // Purple.
this.Demo.lerpColorB: Color32 | nulllerpColorB = new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(40, 220, 160); // Teal.
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_LERP_A: 18C_LERP_A, this.Demo.lerpColorA: Color32lerpColorA);
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_LERP_B: 19C_LERP_B, this.Demo.lerpColorB: Color32lerpColorB);
// Overlay bar: a calm static color so the HUD strip does not pulse with the lerp demo.
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_OVERLAY_BAR: 20C_OVERLAY_BAR, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(40, 48, 64));
// HSL, lerp gradient, and pulse slots are left empty here.
// update() will fill them before the first frame is drawn.
// Step 3: Install the shared UI theme
// applyTheme() writes the series' twelve standard UI colors into slots 240-251,
// safely above every slot this lesson uses (the static colors in 1-20 and the
// animated ranges 30-93, 94-125, and 126). The section headers draw with them.
// This must happen BEFORE BT.paletteSet() below so the colors are included.
this.Demo.theme: nulltheme = import applyThemeapplyTheme(this.Demo.palette: Palettepalette);
// Step 4: Activate the 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) => 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);
return true;
}
/**
* Advances logical time and recalculates all animated palette entries.
*
* - HSL rainbow: 64 hue slots scroll with animTime.
* - Lerp gradient: 32 slots blend from colorA to colorB with a sliding phase.
* - Pulse: 1 slot breathes between colorA and colorB using a sine wave.
*/
Demo.update(): voidAdvances logical time and recalculates all animated palette entries.
- HSL rainbow: 64 hue slots scroll with animTime.
- Lerp gradient: 32 slots blend from colorA to colorB with a sliding phase.
- Pulse: 1 slot breathes between colorA and colorB using a sine wave.update() {
// Add one tick's worth of seconds. At 60 ticks per second, each tick is 1/60 of a second.
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;
// HSL rainbow: 64 animated hue slots
// Each slot gets a hue based on its position on the color wheel PLUS
// a time-based scroll offset so the whole rainbow moves over time.
const const scroll: numberscroll = this.Demo.animTime: numberanimTime * 90; // 90 degrees per second.
for (let let i: numberi = 0; let i: numberi < const HSL_SLOTS: 64HSL_SLOTS; let i: numberi++) {
// Spread the base hue evenly: slot 0 is hue 0, slot 63 is hue 337.5.
const const baseHue: numberbaseHue = (let i: numberi / const HSL_SLOTS: 64HSL_SLOTS) * 360;
// Add scroll and wrap into 0..360 range.
// % can give negative values in JS if the input is negative, so we add 360 first.
const const hue: numberhue = (((const baseHue: numberbaseHue + const scroll: numberscroll) % 360) + 360) % 360;
// fromHSL(hue, saturation, lightness): vivid rainbow needs 100% saturation, 50% lightness.
this.Demo.palette: Palette | nullpalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_HSL_BASE: 30C_HSL_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, 100, 50));
}
// Lerp gradient: 32 sliding color steps
// phase01 cycles from 0 to 1 repeatedly, making the gradient appear to travel.
const const phase: numberphase = this.Demo.animTime: numberanimTime * 0.35; // Speed of the scroll.
const const phase01: numberphase01 = const phase: numberphase - Math.floor(const phase: numberphase); // Only the fractional part (0..1).
for (let let j: numberj = 0; let j: numberj < const LERP_SLOTS: 32LERP_SLOTS; let j: numberj++) {
// u is this slot's position along the bar (0 = left, 1 = right).
const const u: numberu = let j: numberj / (const LERP_SLOTS: 32LERP_SLOTS - 1);
// Combine the bar position with the animated phase so the pattern moves.
const const t: numbert = (const u: numberu + const phase01: numberphase01) % 1; // Wraps at 1 to keep cycling.
// lerp returns a new Color32 blended between A and B at position t.
this.Demo.palette: Palette | nullpalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_LERP_BASE: 94C_LERP_BASE + let j: numberj, this.Demo.lerpColorA: Color32 | nulllerpColorA.Color32.lerp(other: Color32, t: number): Color32Linearly interpolates between this color and another.
Useful for color transitions and gradients.lerp(this.Demo.lerpColorB: Color32 | nulllerpColorB, const t: numbert));
}
// Pulse: one color that breathes back and forth
// Math.sin() returns a wave between -1 and 1.
// We shift it to 0..1 by adding 1 and dividing by 2.
const const sinVal: anysinVal = Math.sin(this.Demo.animTime: numberanimTime * 2.5);
const const pulseT: numberpulseT = (const sinVal: anysinVal + 1) / 2; // 0 when all colorA, 1 when all colorB.
this.Demo.palette: Palette | nullpalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_PULSE: 126C_PULSE, this.Demo.lerpColorA: Color32 | nulllerpColorA.Color32.lerp(other: Color32, t: number): Color32Linearly interpolates between this color and another.
Useful for color transitions and gradients.lerp(this.Demo.lerpColorB: Color32 | nulllerpColorB, const pulseT: numberpulseT));
}
/**
* Draws every section each frame. Always clear first, then paint from back to front.
*
* Notice: NO Color32 objects appear here. Every draw call uses a palette index.
*/
Demo.render(): voidDraws every section each frame. Always clear first, then paint from back to front.
Notice: NO Color32 objects appear here. Every draw call uses a palette index.render() {
// Dark gray-blue background so bright color samples pop.
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(const C_BG: 2C_BG);
// Section 1: ready-made named colors in a row with short labels.
this.Demo.drawNamedColorsSection(): voidPaints the top row of preset Color32 colors (red(), green(), and so on).
Each block is a filled rectangle; the label sits above it in small text.drawNamedColorsSection();
// Section 2: HSL rainbow strip with hue that scrolls over time.
this.Demo.drawHslRainbowSection(): voidDraws one horizontal strip where each column group uses a palette slot from C_HSL_BASE.
The HSL slots are updated in update() so the rainbow scrolls over time.
This function only maps each x column to the right slot - no Color32 objects needed.
Hue is an angle 0..360 on a color wheel. 64 slots cover the whole wheel in steps.drawHslRainbowSection();
// Section 3: sliding blend between two colors using colorA.lerp(colorB, t).
this.Demo.drawLerpSection(): voidSection 3: lerp (linear interpolation) between two colors.
Think of t like a dimmer switch between two lamps: t = 0 is only lamp A (purple),
t = 1 is only lamp B (teal), and t = 0.5 is an even mix halfway between them.
colorA.lerp(colorB, t) returns a new Color32 at that blend point.
The wide bar uses 32 palette slots that slide over time (a moving gradient).
The thin strip below uses one slot that breathes A <-> B with a sine wave.drawLerpSection();
}
/**
* Paints the top row of preset Color32 colors (red(), green(), and so on).
* Each block is a filled rectangle; the label sits above it in small text.
*/
Demo.drawNamedColorsSection(): voidPaints the top row of preset Color32 colors (red(), green(), and so on).
Each block is a filled rectangle; the label sits above it in small text.drawNamedColorsSection() {
// Section header, drawn with ui.caption() from the shared UI kit. Every demo in
// the series uses this same widget, so all headers look identical everywhere.
import uiui.caption(6, 3, '1: NAMED COLORS (shortcuts)');
const const rowY: 16rowY = 16;
const const swatchH: 11swatchH = 11;
// Each entry: a short label and the palette index for that named color.
const const entries: {}entries = [
{ label: stringlabel: 'RED', index: numberindex: const C_RED: 4C_RED },
{ label: stringlabel: 'GREEN', index: numberindex: const C_GREEN_N: 5C_GREEN_N },
{ label: stringlabel: 'BLUE', index: numberindex: const C_BLUE_N: 6C_BLUE_N },
{ label: stringlabel: 'YELLOW', index: numberindex: const C_YELLOW_N: 7C_YELLOW_N },
{ label: stringlabel: 'CYAN', index: numberindex: const C_CYAN_N: 8C_CYAN_N },
{ label: stringlabel: 'WHITE', index: numberindex: const C_WHITE: 1C_WHITE },
{ label: stringlabel: 'BLACK', index: numberindex: const C_BLACK: 3C_BLACK },
];
// Shared horizontal padding so the row does not touch the screen edge.
const const margin: 6margin = 6;
// How many pixels wide each swatch can be if we split the row evenly.
const const slotW: anyslotW = Math.floor((320 - const margin: 6margin * 2) / const entries: {}entries.length);
for (let let slotIndex: numberslotIndex = 0; let slotIndex: numberslotIndex < const entries: {}entries.length; let slotIndex: numberslotIndex++) {
const const entry: anyentry = const entries: {}entries[let slotIndex: numberslotIndex];
const const x: numberx = const margin: 6margin + let slotIndex: numberslotIndex * const slotW: anyslotW;
const const swatchW: numberswatchW = const slotW: anyslotW - 4;
// Light swatches (white, yellow, green, cyan) need black labels so you can read them.
// Dark swatches get white labels.
const const isLight: booleanisLight =
const entry: anyentry.index === const C_WHITE: 1C_WHITE ||
const entry: anyentry.index === const C_YELLOW_N: 7C_YELLOW_N ||
const entry: anyentry.index === const C_GREEN_N: 5C_GREEN_N ||
const entry: anyentry.index === const C_CYAN_N: 8C_CYAN_N;
const const labelColor: 1 | 3labelColor = const isLight: booleanisLight ? const C_BLACK: 3C_BLACK : const C_WHITE: 1C_WHITE;
// Fill a rectangle with that named 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.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 rowY: 16rowY, const swatchW: numberswatchW, const swatchH: 11swatchH), const entry: anyentry.index);
// Print the label into the swatch.
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 x: numberx + 2, const rowY: 16rowY - 1), const labelColor: 1 | 3labelColor, const entry: anyentry.label);
}
}
/**
* Draws one horizontal strip where each column group uses a palette slot from C_HSL_BASE.
*
* The HSL slots are updated in update() so the rainbow scrolls over time.
* This function only maps each x column to the right slot - no Color32 objects needed.
*
* Hue is an angle 0..360 on a color wheel. 64 slots cover the whole wheel in steps.
*/
Demo.drawHslRainbowSection(): voidDraws one horizontal strip where each column group uses a palette slot from C_HSL_BASE.
The HSL slots are updated in update() so the rainbow scrolls over time.
This function only maps each x column to the right slot - no Color32 objects needed.
Hue is an angle 0..360 on a color wheel. 64 slots cover the whole wheel in steps.drawHslRainbowSection() {
import uiui.caption(6, 30, '2: HSL RAINBOW (fromHSL, scrolling hue)');
const const stripY: 43stripY = 43;
const const stripH: 11stripH = 11;
// Walk every x column on the screen from left to right.
// Each column maps to one of the 64 HSL palette slots.
for (let let x: numberx = 0; let x: numberx < 320; let x: numberx++) {
// Which slot does this column belong to? There are 64 slots covering 320 pixels.
// Math.floor(...) rounds down to get an integer slot index.
const const slot: anyslot = Math.min(Math.floor((let x: numberx / 320) * const HSL_SLOTS: 64HSL_SLOTS), const HSL_SLOTS: 64HSL_SLOTS - 1);
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 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(let x: numberx, const stripY: 43stripY, 1, const stripH: 11stripH), const C_HSL_BASE: 30C_HSL_BASE + const slot: anyslot);
}
}
/**
* Section 3: lerp (linear interpolation) between two colors.
*
* Think of t like a dimmer switch between two lamps: t = 0 is only lamp A (purple),
* t = 1 is only lamp B (teal), and t = 0.5 is an even mix halfway between them.
* colorA.lerp(colorB, t) returns a new Color32 at that blend point.
*
* The wide bar uses 32 palette slots that slide over time (a moving gradient).
* The thin strip below uses one slot that breathes A <-> B with a sine wave.
*/
Demo.drawLerpSection(): voidSection 3: lerp (linear interpolation) between two colors.
Think of t like a dimmer switch between two lamps: t = 0 is only lamp A (purple),
t = 1 is only lamp B (teal), and t = 0.5 is an even mix halfway between them.
colorA.lerp(colorB, t) returns a new Color32 at that blend point.
The wide bar uses 32 palette slots that slide over time (a moving gradient).
The thin strip below uses one slot that breathes A <-> B with a sine wave.drawLerpSection() {
import uiui.caption(6, 57, '3: LERP: slide + pulse (see comments)');
const const barY: 70barY = 70;
// Dimmer analogy: these end squares are the two "lamps" at full brightness (pure A and B).
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 barY: 70barY, 11, 11), const C_LERP_A: 18C_LERP_A);
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(8, const barY: 70barY - 1), const C_BLACK: 3C_BLACK, 'A');
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(300, const barY: 70barY, 11, 11), const C_LERP_B: 19C_LERP_B);
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(302, const barY: 70barY - 1), const C_BLACK: 3C_BLACK, 'B');
// Middle gradient bar: each column is another step on the dimmer between A and B.
// update() already wrote 32 blended colors into palette slots C_LERP_BASE.. .
const const barX: 24barX = 24;
const const barW: 268barW = 268;
const const barH: 11barH = 11;
for (let let i: numberi = 0; let i: numberi < const barW: 268barW; let i: numberi++) {
// Pick which of the 32 pre-blended "dimmer steps" this pixel column uses.
const const slot: anyslot = Math.min(Math.floor((let i: numberi / const barW: 268barW) * const LERP_SLOTS: 32LERP_SLOTS), const LERP_SLOTS: 32LERP_SLOTS - 1);
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 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 barX: 24barX + let i: numberi, const barY: 70barY, 1, const barH: 11barH), const C_LERP_BASE: 94C_LERP_BASE + const slot: anyslot);
}
// Thin strip: one color slot whose t value waves back and forth (whole bar pulses).
// In update(), pulseT follows a sine wave so the dimmer slides A -> B -> A smoothly.
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(24, 82, 268, 11), const C_PULSE: 126C_PULSE);
}
}
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 DemoShows how Color32 works: named colors, HSL rainbow, and lerp.
All animated colors are computed in update() and stored in palette slots.
render() uses only palette index numbers - no Color32 objects there.Demo);