/*
Colors: a closer look at Color32 - named colors, HSL, and blending between two colors.
@description A deep dive into Color32: named colors, HSL, and interpolating between two colors.
Part of the BLIT386 demo series.
Prerequisites:
- Basics: https://demos.blit386.dev/basics
- Primitives: https://demos.blit386.dev/primitives
Live version: https://demos.blit386.dev/colors
Guides:
- Palette: https://blit386.dev/docs/guides/palette#draw-using-absolute-palette-indices
- Overlay: https://blit386.dev/docs/guides/overlay#colors-and-hud-palette-slots
Reference:
- Core types: https://blit386.dev/docs/api/core-types#color32
---
WHAT YOU WILL SEE
-----------------
Three numbered sections stacked on a dark gray-blue screen:
1. NAMED COLORS: a row of seven labeled swatches - red, green, blue, yellow, cyan,
white, and black.
2. HSL RAINBOW: a strip across the full screen width that cycles through every hue, and
keeps scrolling sideways.
3. LERP: a purple "A" square on the left, a teal "B" square on the right, a gradient
bar between them that slides along, and a thin strip below it that pulses from A to
B and back.
The engine overlay is open from the start, with the palette grid showing 4 rows at a time
so you can watch the animated slots change color.
WHAT YOU WILL LEARN
-------------------
- Named shortcut colors: Color32.red, Color32.green, and friends are static properties,
not function calls - ready-made Color32 objects you can copy into a palette.
- HSL: Color32.fromHSL(hue, saturation, lightness) picks a color by its angle on the
color wheel (0-360) instead of mixing red, green, and blue by hand.
- Lerp: colorA.lerp(colorB, t) returns the color a fraction t of the way from A to B,
like a dimmer between two lamps.
- The fourth Color32 argument is alpha: 255 is fully solid, 0 fully invisible.
- Palette animation: static colors go into the palette once in init(); animated colors
are recalculated every tick in update() and written back into reserved slots. render()
only ever uses slot numbers.
- Counting time in seconds with BT.deltaSeconds, the length of one update tick.
HOW THE PALETTE IS LAID OUT
---------------------------
- Slots 1-9: the static basics - white, background, black, and the six named colors.
- Slot 15: the overlay text color. Slots 18-19: the lerp endpoints A and B. Slot 20: the
overlay bar color.
- Slots 30-93: the 64 rainbow hues. Slots 94-125: the 32 gradient steps. Slot 126: the
pulse color. These are rewritten every tick.
- Slots 240-251: the shared UI kit's own twelve colors, used for the numbered
section headers. The swatches and their small labels are hand-drawn on purpose:
they ARE the lesson.
UPDATE VS RENDER
----------------
- update() runs at a fixed rate, 60 times per second (the engine default). Each call is
one "tick", and animTime grows by BT.deltaSeconds (1/60 of a second) per tick, so after
60 ticks it is about 1.0.
- render() may run a different number of times per second on a high-refresh monitor, but
animTime only changes inside update(), so the animation speed is the same everywhere.
TRY THIS
--------
- The overlay is already open. Watch its palette grid at the bottom: slots 30-93 scroll
through the rainbow, 94-125 slide, and 126 pulses, in step with the strips above.
- Press the backquote key (`) or click/tap the symbol in the bottom-left corner to hide
the overlay and see the whole screen.
*/
import { function bootstrap(DemoClass: DemoConstructor, options?: BootstrapOptions): Promise<boolean>One-liner bootstrap function for BLIT386 demos.
Handles canvas retrieval and engine initialization. Backend selection
(WebGPU or software fallback) is managed internally by BTAPI.
This function provides a streamlined way to start a demo with sensible defaults
while allowing customization through options.bootstrap, const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT, 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').Palette} Palette */
/** @typedef {import('blit386').Color32} Color32 */
// These numbers are the palette "addresses". We name them so the code reads well. Slot 0
// is always transparent and reserved - never write to it.
// Basic colors: set once in init(), never changed.
const const C_WHITE: 1C_WHITE = 1; // pure white: the WHITE 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: the BLACK swatch and labels on light 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 slot that holds still.
const const C_OVERLAY_BAR: 20C_OVERLAY_BAR = 20; // soft blue-gray strip behind the engine overlay text
// Animated slots: recalculated every tick in update().
// HSL rainbow strip: 64 hue slots covering the whole 0-360 degree color wheel. Slot
// C_HSL_BASE + i holds the color for the i-th group of columns.
const const C_HSL_BASE: 30C_HSL_BASE = 30;
const const HSL_SLOTS: 64HSL_SLOTS = 64; // 64 slots, each 5 pixels wide, cover the 320-pixel 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, an HSL rainbow, and lerp. All animated colors
// are computed in update() and stored in palette slots; render() uses only slot numbers.
class class DemoDemo {
// 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 a box of 256
// numbered paint cans.
/** @type {Palette | null} */
Demo.palette: Palette | nullpalette = null;
// Palette slot map for the shared UI kit theme, filled in init() by applyTheme(): a
// map like { bg, text, dim, header, ... } of slot numbers.
Demo.theme: nulltheme = null;
// The two Color32 objects the lerp gradient blends between. We keep them here so
// update() can call lerpColorA.lerp(lerpColorB, 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, open the overlay from
// the start, and show its palette grid with 4 visible rows (scroll for the rest).
Demo.configure(): {
isOverlayPaletteEnabled: boolean;
overlayPaletteRowsVisible: number;
isOverlayVisibleAtStart: boolean;
overlayStyle: {
barPaletteIndex: number;
textPaletteIndex: number;
gapPaletteIndex: number;
};
}
configure() {
return {
isOverlayPaletteEnabled: booleanisOverlayPaletteEnabled: true,
overlayPaletteRowsVisible: numberoverlayPaletteRowsVisible: 4,
isOverlayVisibleAtStart: booleanisOverlayVisibleAtStart: true,
overlayStyle: {
barPaletteIndex: number;
textPaletteIndex: number;
gapPaletteIndex: number;
}
overlayStyle: {
// A dedicated static slot - not one of the lerp slots update() rewrites.
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 the two lerp colors.
async Demo.init(): unknowninit() {
// 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;
... 124 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 the static colors, starting with the basics.
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, and so on are static
// properties on the Color32 class - ready-made Color32 objects. Copy them into
// the palette so render() can draw with slot numbers instead of passing a
// 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 is fully
// solid, 0 is 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.
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));
// The HSL, gradient, and pulse slots stay empty here. update() fills 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 go live with the palette.
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;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.paletteSet: (palette: Palette) => voidStores the active engine palette.
Use this to swap the **entire palette** (e.g. switch between a day and night
theme). After this call the renderer uploads the new palette uniform on the
next frame.
**Palette-value swap (change what a slot looks like):** mutate the live
{@link
BT.palette
}
in place with `palette.set(slot, newColor)`. The renderer
uploads dirty slots on the next frame; no `paletteSet()` or
{@link
BT.spritesRefresh
}
needed.
**Palette-layout swap (same colors, different slot positions):** build a new
palette with the same colors at new indices, call `paletteSet()`, then call
{@link
BT.spritesRefresh
}
so every sprite sheet re-maps its original RGBA
pixels against the new slot layout.paletteSet(this.Demo.palette: Palettepalette);
return true;
}
// Advances logical time and recalculates every animated palette slot:
// - HSL rainbow: 64 hue slots that scroll with animTime.
// - Lerp gradient: 32 slots blending from A to B with a sliding phase.
// - Pulse: 1 slot breathing between A and B on a sine wave.
Demo.update(): voidupdate() {
// Add one tick's worth of seconds. BT.deltaSeconds is 1/60 at 60
// ticks per 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;
... 124 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: each slot gets a hue from its position on the color wheel PLUS a
// time-based offset, so the whole rainbow scrolls 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 hues 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 the scroll and wrap into 0-360. % (remainder) can give a negative
// result in JavaScript when its input is negative, so we add 360 first
// and wrap again.
const const hue: numberhue = (((const baseHue: numberbaseHue + const scroll: numberscroll) % 360) + 360) % 360;
// fromHSL(hue, saturation, lightness): a vivid rainbow needs saturation 100
// and lightness 50.
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: phase01 cycles from 0 to 1 over and over, which makes the
// gradient appear to travel along the bar.
const const phase: numberphase = this.Demo.animTime: numberanimTime * 0.35; // speed of the slide
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 at the left, 1 at the 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; adding 1 and dividing by 2 shifts it into 0-1.
const const sinVal: anysinVal = Math.sin(this.Demo.animTime: numberanimTime * 2.5);
const const pulseT: numberpulseT = (const sinVal: anysinVal + 1) / 2; // 0 is all A, 1 is all B
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 slot.
Demo.render(): voidrender() {
// Dark gray-blue background so the 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;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.clear: (paletteIndex: number) => voidSets the frame clear color using a palette index.
The renderer uses this color when clearing the full display at the start
of the next frame.clear(const C_BG: 2C_BG);
// Section 1: ready-made named colors in a row with short labels.
this.Demo.drawNamedColorsSection(): voiddrawNamedColorsSection();
// Section 2: the HSL rainbow strip with a hue that scrolls over time.
this.Demo.drawHslRainbowSection(): voiddrawHslRainbowSection();
// Section 3: a sliding blend between two colors, made with lerp.
this.Demo.drawLerpSection(): voiddrawLerpSection();
}
// Paints the row of swatches: red, green, blue, yellow, cyan, white, black. Each is a
// filled rectangle with its label printed over the top edge.
Demo.drawNamedColorsSection(): voiddrawNamedColorsSection() {
// 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, 50, '1: NAMED COLORS (shortcuts)');
const const rowY: 61rowY = 61;
const const swatchH: 11swatchH = 11;
// Each entry: a short label and the palette slot for that 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 },
];
// Horizontal padding so the row does not touch the screen edge.
const const margin: 6margin = 6;
// How many pixels wide each swatch may 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 to stay
// readable. 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 color.
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.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: 61rowY, 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;
... 124 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: 61rowY - 1), const labelColor: 1 | 3labelColor, const entry: anyentry.label);
}
}
// Draws one horizontal strip where each group of columns uses a slot from C_HSL_BASE.
// The slots are rewritten in update(), so the rainbow scrolls over time; this method
// only maps each x column to the right slot - no Color32 objects needed.
//
// Hue is an angle from 0 to 360 on a color wheel. 64 slots cover the whole
// wheel in steps.
Demo.drawHslRainbowSection(): voiddrawHslRainbowSection() {
import uiui.caption(6, 84, '2: HSL RAINBOW (fromHSL, scrolling hue)');
const const stripY: 95stripY = 95;
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 slots.
for (let let x: numberx = 0; let x: numberx < 320; let x: numberx++) {
// Which slot does this column belong to? 64 slots cover 320 pixels, so each
// slot is 5 columns wide. Math.floor rounds down to a whole slot index, and
// Math.min keeps it inside the range.
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;
... 124 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: 95stripY, 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 as 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.
// lerpColorA.lerp(lerpColorB, 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 from A to B and back on a sine wave.
Demo.drawLerpSection(): voiddrawLerpSection() {
import uiui.caption(6, 118, '3: LERP: slide + pulse (see comments)');
const const barY: 129barY = 129;
// The two end squares are the "lamps" at full brightness: pure A and pure 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;
... 124 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: 129barY, 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;
... 124 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: 129barY - 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;
... 124 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: 129barY, 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;
... 124 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: 129barY - 1), const C_BLACK: 3C_BLACK, 'B');
// The gradient bar: each column is another step on the dimmer between A and B.
// update() already wrote 32 blended colors into slots C_LERP_BASE and up.
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;
... 124 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: 129barY, 1, const barH: 11barH), const C_LERP_BASE: 94C_LERP_BASE + const slot: anyslot);
}
// The thin strip: one slot whose t value waves back and forth, so the whole
// strip pulses. In update(), pulseT follows a sine wave, so the dimmer slides A
// to B to 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;
... 124 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, const barY: 129barY + 15, 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 DemoDemo);