// Patterns: animated mathematical art using only primitive drawing.
// @description Animated mathematical art from primitives alone: spirals, Lissajous curves, waves, and a tunnel.
//
// Prerequisites: We learned about drawing and the game loop in Basics demo
// (https://demos.blit386.dev/basics), shapes in Primitives demo
// (https://demos.blit386.dev/primitives), and color in Colors demo
// (https://demos.blit386.dev/colors).
//
// We also use the palette system introduced in Colors demo
// (https://demos.blit386.dev/colors). You will see much more of
// palettes later, in palette-presets demo and beyond.
//
// Live version: https://demos.blit386.dev/patterns
// Guide: https://blit386.dev/docs/api/rendering#primitives
//
// All six patterns here are drawn using just pixels, lines, and rectangles
// no images needed. Each pattern is based on simple math (angles, waves, circles)
// that creates surprisingly complex-looking results.
//
// The six patterns arranged in a 2x3 grid are:
//   Spiral    - dots expanding outward in a spinning coil
//   Radial    - lines radiating from a center point like sun rays
//   Wave      - overlapping wave curves that interfere with each other
//   Circle    - a circle drawn from many tiny line segments
//   Lissajous - a smooth looping curve used in physics and electronics
//   Tunnel    - concentric rectangles that spin to look like a tunnel
//
// HOW COLORS WORK IN THIS DEMO:
//
// Every color must be registered in a "palette" before drawing. Think of it
// like choosing all your paint colors before starting a painting - you pick
// them out first, then use them by number ("color 5", "color 12", etc.).
//
// Some colors never change (white, background, wave colors) - those are set
// once during setup. Other colors animate (spiral, Lissajous, tunnel) - those
// are recalculated every tick in update() and stored back in the palette.
// The render() function only ever uses color numbers (indices), never Color32 objects.
//
// The six pattern captions ("Spiral", "Radial", ...) are drawn with ui.caption() from the
// shared UI kit (src/shared/ui.js), in the same amber header color every other demo in the
// series uses. The patterns themselves are the lesson and are still drawn by hand below.

import { function bootstrap(DemoClass: DemoConstructor, options?: BootstrapOptions): Promise<boolean>
One-liner bootstrap function for BLIT386 demos. Handles canvas retrieval and engine initialization. Backend selection (WebGPU or software fallback) is managed internally by BTAPI. This function provides a streamlined way to start a demo with sensible defaults while allowing customization through options.
@since0.2.0@changed1.4.0 Calling `bootstrap()` again while already initialized now routes to a hot swap (via {@link registerHotReload}) when a Vite HMR context is registered, or logs a double-bootstrap guard and returns `false` otherwise - previously it silently started a second, unstoppable `GameLoop`.@changed1.7.0 Exposes `BT` on `window.BT` after bootstrap finishes, gated by {@link BootstrapOptions.exposeGlobal} (default: {@link BT.isDevMode}).@paramDemoClass - Demo class constructor implementing `IBTDemo` (optional `configure()` for hardware settings).@paramoptions - Optional configuration for IDs and callbacks.@returns`true` when the demo boots successfully; otherwise `false`.@example// Simplest usage - uses default IDs. bootstrap(MyDemo);@example// With custom options. bootstrap(MyDemo, { canvasID: 'custom-canvas', containerID: 'custom-container', onSuccess: () => console.log('Demo started!'), onError: (err) => analytics.trackError(err), });@example// Await the result. const success = await bootstrap(MyDemo); if (success) { console.log('Demo is running'); }
bootstrap
,
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
, class Color32
Mutable 32-bit RGBA color value with 8-bit channels.
@since0.1.0
Color32
, class Rect2i
Integer rectangle for pixel-perfect bounds and regions. Used throughout the engine for sprite regions, display-space bounds, and zero-allocation geometry helpers. Both convenience getters and allocation-free `*To()` helpers are provided so callers can choose between readability and hot-path efficiency.
@since0.1.0
Rect2i
, class Vector2i
Integer 2D vector for pixel-perfect positioning. Used for points, sizes, directions, and camera offsets throughout the engine. The API includes both allocation-free `*To()` / `*InPlace()` variants and convenience methods that return new vectors.
@since0.1.0
Vector2i
} from 'blit386';
import { import applyThemeapplyTheme, import uiui } from './shared/ui.js'; /** @typedef {import('blit386').IBTDemo} IBTDemo */ /** @typedef {import('blit386').HardwareSettings} HardwareSettings */ /** @typedef {import('blit386').Palette} Palette */ // // These numbers are the "addresses" in the palette table. // Index 0 is always reserved for transparent - we never use it. // We give each color a readable name so the code is easier to follow. // Static colors (set once in init, never change). const const C_WHITE: 1C_WHITE = 1; // Pure white - overlay bar style color (see configure()). const const C_BG: 2C_BG = 2; // Very dark blue-black background. const const C_TAG: 3C_TAG = 3; // Dim white - only feeds the overlay timing-chart tag color (see configure()). const const C_WAVE_1: 5C_WAVE_1 = 5; // Blue - primary sine wave. const const C_WAVE_2: 6C_WAVE_2 = 6; // Orange - secondary cosine wave. const const C_WAVE_3: 7C_WAVE_3 = 7; // Green - interference (both waves combined). // Circle segments: 32 entries at indices 8..39. // Each segment gets a different hue. Hue never changes so this is static. const const C_CIRCLE_BASE: 8C_CIRCLE_BASE = 8; const const CIRCLE_SEGMENTS: 32CIRCLE_SEGMENTS = 32; // Radial rays: 12 entries at indices 40..51. // Each ray has a fixed hue. Static. const const C_RADIAL_BASE: 40C_RADIAL_BASE = 40; const const RADIAL_LINES: 12RADIAL_LINES = 12; // Dynamic colors (updated every tick in update()). // Spiral dots: 100 entries at indices 60..159. // The hue of each dot shifts forward as animTime grows, making colors scroll. const const C_SPIRAL_BASE: 60C_SPIRAL_BASE = 60; const const SPIRAL_POINTS: 100SPIRAL_POINTS = 100; // Lissajous curve: 32 color bands at indices 160..191. // The 200-point curve is divided into 32 color groups that rotate over time. const const C_LISSAJOUS_BASE: 160C_LISSAJOUS_BASE = 160; const const LISSAJOUS_BANDS: 32LISSAJOUS_BANDS = 32; // Tunnel rectangles: 20 entries at indices 192..211. // Outer rectangles are brighter and more saturated; inner ones are darker. const const C_TUNNEL_BASE: 192C_TUNNEL_BASE = 192; const const TUNNEL_RECTS: 20TUNNEL_RECTS = 20; // Engine overlay: measured FPS, target FPS, and demo name (enabled by default). /** * Demonstrates animated mathematical patterns using primitive drawing. * Each section shows a different algorithmic visual effect arranged in a 2x3 grid. * * @implements {IBTDemo} */ class class Demo
Demonstrates animated mathematical patterns using primitive drawing. Each section shows a different algorithmic visual effect arranged in a 2x3 grid.
@implementsIBTDemo
Demo
{
// animTime counts up in seconds. We use it to make patterns move. Demo.animTime: numberanimTime = 0; // The palette holds all the colors we are allowed to use. // Think of it as a box of 256 numbered paint colors. /** @type {Palette | null} */ Demo.palette: Palette | null
@type{Palette | null}
palette
= null;
// Where the shared UI theme colors landed in the palette, filled by applyTheme() in // init(). The UI kit draws the six pattern captions with these slots. Demo.theme: nulltheme = null; // These Vector2i and Rect2i objects are created once and reused every frame. // Creating new objects inside a loop every frame can slow things down because // the browser has to clean up old objects. Reusing them avoids that. Demo.tempVec1: Vector2itempVec1 = new new Vector2i(x?: number, y?: number): Vector2i
Creates an integer 2D vector, truncating inputs toward zero.
@paramx - Horizontal component (defaults to 0).@paramy - Vertical component (defaults to 0).
Vector2i
(0, 0);
Demo.tempVec2: Vector2itempVec2 = new new Vector2i(x?: number, y?: number): Vector2i
Creates an integer 2D vector, truncating inputs toward zero.
@paramx - Horizontal component (defaults to 0).@paramy - Vertical component (defaults to 0).
Vector2i
(0, 0);
Demo.tempRect: Rect2itempRect = new new Rect2i(x?: number, y?: number, width?: number, height?: number): Rect2i
Creates an integer rectangle, truncating all inputs toward zero.
@paramx - Left-edge X coordinate (defaults to 0).@paramy - Top-edge Y coordinate (defaults to 0).@paramwidth - Width in pixels (defaults to 0).@paramheight - Height in pixels (defaults to 0).
Rect2i
(0, 0, 0, 0);
/** * Optional engine settings. We keep the default 320x240 screen and show the full * 256-slot palette in the overlay grid (default column count). Rich diagnostics and * the renderer diagnostics bar are enabled so GPU pipeline pressure is visible. * * @returns {Partial<HardwareSettings>} */ Demo.configure(): Partial<HardwareSettings>
Optional engine settings. We keep the default 320x240 screen and show the full 256-slot palette in the overlay grid (default column count). Rich diagnostics and the renderer diagnostics bar are enabled so GPU pipeline pressure is visible.
@returns
configure
() {
return { isOverlayPaletteEnabled: booleanisOverlayPaletteEnabled: true, isOverlayVisibleAtStart: booleanisOverlayVisibleAtStart: true,
overlayStyle: {
    barPaletteIndex: number;
    textPaletteIndex: number;
    gapPaletteIndex: number;
}
overlayStyle
: {
barPaletteIndex: numberbarPaletteIndex: const C_WHITE: 1C_WHITE, textPaletteIndex: numbertextPaletteIndex: const C_BG: 2C_BG, gapPaletteIndex: numbergapPaletteIndex: const C_BG: 2C_BG, }, isOverlayTimingChartEnabled: booleanisOverlayTimingChartEnabled: true, overlayTimingChartDiagnostics: stringoverlayTimingChartDiagnostics: 'rich', isOverlayRendererDiagnosticsBarEnabled: booleanisOverlayRendererDiagnosticsBarEnabled: true,
overlayTimingChartStyle: {
    updateBarPaletteIndex: number;
    renderBarPaletteIndex: number;
    warningPaletteIndex: number;
    errorPaletteIndex: number;
    tagPaletteIndex: number;
}
overlayTimingChartStyle
: {
updateBarPaletteIndex: numberupdateBarPaletteIndex: const C_WAVE_3: 7C_WAVE_3, renderBarPaletteIndex: numberrenderBarPaletteIndex: const C_WAVE_1: 5C_WAVE_1, warningPaletteIndex: numberwarningPaletteIndex: const C_WAVE_2: 6C_WAVE_2, errorPaletteIndex: numbererrorPaletteIndex: const C_WAVE_2: 6C_WAVE_2, tagPaletteIndex: numbertagPaletteIndex: const C_TAG: 3C_TAG, }, }; } /** * Runs once when the demo starts. Sets up the palette. * * IMPORTANT ORDER: * 1. Create palette - make the 256-slot color table. * 2. Fill in static colors - the ones that never change. * 3. BT.paletteSet() - tell the engine to use this palette. * * @returns {Promise<boolean>} Returns true when ready to run. */ async Demo.init(): Promise<boolean>
Runs once when the demo starts. Sets up the palette. IMPORTANT ORDER: 1. Create palette - make the 256-slot color table. 2. Fill in static colors - the ones that never change. 3. BT.paletteSet() - tell the engine to use this palette.
@returnsReturns true when ready to run.
init
() {
// Step 1: Create the palette // BT.paletteCreate(256) makes a color table with 256 numbered slots. // Slot 0 is always transparent and cannot be changed. this.Demo.palette: Palette | null
@type{Palette | null}
palette
=
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.paletteCreate: (size?: number) => Palette
Creates a standalone palette instance.
@since1.0.3@paramsize - Palette size. Defaults to 256 colors.@returnsNew mutable palette.
paletteCreate
(256);
// Step 2: Register static colors // Basic UI colors. this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const C_WHITE: 1C_WHITE, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32
Creates a clamped 8-bit RGBA color.
@paramr - Red channel (0-255, defaults to 255).@paramg - Green channel (0-255, defaults to 255).@paramb - Blue channel (0-255, defaults to 255).@parama - Alpha channel (0-255, defaults to 255 = opaque).
Color32
(255, 255, 255)); // White for title text.
this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const C_BG: 2C_BG, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32
Creates a clamped 8-bit RGBA color.
@paramr - Red channel (0-255, defaults to 255).@paramg - Green channel (0-255, defaults to 255).@paramb - Blue channel (0-255, defaults to 255).@parama - Alpha channel (0-255, defaults to 255 = opaque).
Color32
(15, 15, 25)); // Very dark blue-black background.
this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const C_TAG: 3C_TAG, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32
Creates a clamped 8-bit RGBA color.
@paramr - Red channel (0-255, defaults to 255).@paramg - Green channel (0-255, defaults to 255).@paramb - Blue channel (0-255, defaults to 255).@parama - Alpha channel (0-255, defaults to 255 = opaque).
Color32
(200, 200, 200)); // Dim white for the timing-chart tag.
// Wave pattern: three fixed colors. this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const C_WAVE_1: 5C_WAVE_1, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32
Creates a clamped 8-bit RGBA color.
@paramr - Red channel (0-255, defaults to 255).@paramg - Green channel (0-255, defaults to 255).@paramb - Blue channel (0-255, defaults to 255).@parama - Alpha channel (0-255, defaults to 255 = opaque).
Color32
(100, 200, 255)); // Blue wave.
this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const C_WAVE_2: 6C_WAVE_2, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32
Creates a clamped 8-bit RGBA color.
@paramr - Red channel (0-255, defaults to 255).@paramg - Green channel (0-255, defaults to 255).@paramb - Blue channel (0-255, defaults to 255).@parama - Alpha channel (0-255, defaults to 255 = opaque).
Color32
(255, 150, 100)); // Orange wave.
this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const C_WAVE_3: 7C_WAVE_3, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32
Creates a clamped 8-bit RGBA color.
@paramr - Red channel (0-255, defaults to 255).@paramg - Green channel (0-255, defaults to 255).@paramb - Blue channel (0-255, defaults to 255).@parama - Alpha channel (0-255, defaults to 255 = opaque).
Color32
(150, 255, 150)); // Green interference wave.
// Circle segments: 32 colors spread across the rainbow. // Because these hues do not depend on animTime, we set them here once. for (let let i: numberi = 0; let i: numberi < const CIRCLE_SEGMENTS: 32CIRCLE_SEGMENTS; let i: numberi++) { // Spread 32 evenly-spaced hues across the 0-360 degree color wheel. const const hue: numberhue = (let i: numberi / const CIRCLE_SEGMENTS: 32CIRCLE_SEGMENTS) * 360; this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const C_CIRCLE_BASE: 8C_CIRCLE_BASE + let i: numberi, class Color32
Mutable 32-bit RGBA color value with 8-bit channels.
@since0.1.0
Color32
.Color32.fromHSL(h: number, s: number, l: number, a?: number): Color32
Creates a color from HSL values.
@paramh - Hue in degrees (0-360).@params - Saturation as percentage (0-100).@paraml - Lightness as percentage (0-100).@parama - Alpha channel (0-255, defaults to 255).@returnsNew color converted from HSL values.
fromHSL
(const hue: numberhue, 100, 50));
} // Radial rays: 12 colors, one per ray. for (let let i: numberi = 0; let i: numberi < const RADIAL_LINES: 12RADIAL_LINES; let i: numberi++) { // Slightly desaturated and brightened compared to the circle colors. const const hue: numberhue = (let i: numberi / const RADIAL_LINES: 12RADIAL_LINES) * 360; this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const C_RADIAL_BASE: 40C_RADIAL_BASE + let i: numberi, class Color32
Mutable 32-bit RGBA color value with 8-bit channels.
@since0.1.0
Color32
.Color32.fromHSL(h: number, s: number, l: number, a?: number): Color32
Creates a color from HSL values.
@paramh - Hue in degrees (0-360).@params - Saturation as percentage (0-100).@paraml - Lightness as percentage (0-100).@parama - Alpha channel (0-255, defaults to 255).@returnsNew color converted from HSL values.
fromHSL
(const hue: numberhue, 80, 60));
} // Spiral, Lissajous, and tunnel slots are left empty here. // They will be filled in by update() before the first frame renders. // Install the shared UI theme the kit draws the pattern captions with. // It writes 12 colors into slots 240-251, above every range this demo touches // (the highest animated range is the tunnel at 192-211), so the per-tick palette // animation never collides with the UI colors. Must run before BT.paletteSet(). this.Demo.theme: nulltheme = import applyThemeapplyTheme(this.Demo.palette: Palette
@type{Palette | null}
palette
);
// Step 3: Activate the palette // This tells the engine "use this palette for all drawing from now on".
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.paletteSet: (palette: Palette) => void
Stores the active engine palette. Use this to swap the **entire palette** (e.g. switch between a day and night theme). After this call the renderer uploads the new palette uniform on the next frame. **Palette-value swap (change what a slot looks like):** mutate the live {@link BT.palette } in place with `palette.set(slot, newColor)`. The renderer uploads dirty slots on the next frame; no `paletteSet()` or {@link BT.spritesRefresh } needed. **Palette-layout swap (same colors, different slot positions):** build a new palette with the same colors at new indices, call `paletteSet()`, then call {@link BT.spritesRefresh } so every sprite sheet re-maps its original RGBA pixels against the new slot layout.
@since1.0.3@parampalette - Palette to make active.
paletteSet
(this.Demo.palette: Palette
@type{Palette | null}
palette
);
return true; } /** * Runs at a fixed rate (60 times per second) to: * 1. Advance the animation timer. * 2. Recalculate dynamic palette entries for spiral, Lissajous, and tunnel. * * All color computation happens here. render() only uses palette index numbers. */ Demo.update(): void
Runs at a fixed rate (60 times per second) to: 1. Advance the animation timer. 2. Recalculate dynamic palette entries for spiral, Lissajous, and tunnel. All color computation happens here. render() only uses palette index numbers.
update
() {
// deltaSeconds is one fixed update step in seconds (usually 1/60). 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: number
Fixed-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.
@since1.0.4@returnsSeconds advanced by one fixed update tick.
deltaSeconds
;
// Update spiral colors (100 animated dots) // Each dot gets a hue that depends on its position AND the current time. // As animTime grows, the hue offset grows too, making colors scroll outward. for (let let i: numberi = 0; let i: numberi < const SPIRAL_POINTS: 100SPIRAL_POINTS; let i: numberi++) { // Spread the base hue from 0 to 360 across the 100 dots. // animTime * 50 makes the colors scroll: 50 degrees per second. const const hue: numberhue = (let i: numberi / const SPIRAL_POINTS: 100SPIRAL_POINTS) * 360 + this.Demo.animTime: numberanimTime * 50; // fromHSL takes hue (0-360), saturation (0-100), lightness (0-100). // Fully saturated (100) and mid-lightness (50) gives vivid rainbow colors. this.Demo.palette: Palette | null
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const C_SPIRAL_BASE: 60C_SPIRAL_BASE + let i: numberi, class Color32
Mutable 32-bit RGBA color value with 8-bit channels.
@since0.1.0
Color32
.Color32.fromHSL(h: number, s: number, l: number, a?: number): Color32
Creates a color from HSL values.
@paramh - Hue in degrees (0-360).@params - Saturation as percentage (0-100).@paraml - Lightness as percentage (0-100).@parama - Alpha channel (0-255, defaults to 255).@returnsNew color converted from HSL values.
fromHSL
(const hue: numberhue % 360, 100, 50));
} // Update Lissajous color bands (32 bands for the 200-point curve) // We divide the 200 curve points into 32 color groups to save palette slots. for (let let i: numberi = 0; let i: numberi < const LISSAJOUS_BANDS: 32LISSAJOUS_BANDS; let i: numberi++) { // animTime * 30 rotates the color cycle: 30 degrees per second. const const hue: numberhue = (let i: numberi / const LISSAJOUS_BANDS: 32LISSAJOUS_BANDS) * 360 + this.Demo.animTime: numberanimTime * 30; this.Demo.palette: Palette | null
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const C_LISSAJOUS_BASE: 160C_LISSAJOUS_BASE + let i: numberi, class Color32
Mutable 32-bit RGBA color value with 8-bit channels.
@since0.1.0
Color32
.Color32.fromHSL(h: number, s: number, l: number, a?: number): Color32
Creates a color from HSL values.
@paramh - Hue in degrees (0-360).@params - Saturation as percentage (0-100).@paraml - Lightness as percentage (0-100).@parama - Alpha channel (0-255, defaults to 255).@returnsNew color converted from HSL values.
fromHSL
(const hue: numberhue % 360, 100, 50));
} // Update tunnel rectangle colors (20 rectangles) // Outer rectangles (high i, near viewer) get brighter hues. // Inner rectangles (low i, far away) get darker hues to suggest depth. for (let let i: numberi = 0; let i: numberi < const TUNNEL_RECTS: 20TUNNEL_RECTS; let i: numberi++) { // t goes from 0 (innermost/farthest) to 1 (outermost/nearest). const const t: numbert = let i: numberi / const TUNNEL_RECTS: 20TUNNEL_RECTS; // The hue rotates at 50 degrees per second. Each rect offsets by t*360 // so the colors spread across the rainbow from inner to outer. const const hue: numberhue = (const t: numbert * 360 + this.Demo.animTime: numberanimTime * 50) % 360; // Lightness goes from 30 (dark, far) to 70 (bright, near). const const lightness: numberlightness = 30 + const t: numbert * 40; this.Demo.palette: Palette | null
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const C_TUNNEL_BASE: 192C_TUNNEL_BASE + let i: numberi, class Color32
Mutable 32-bit RGBA color value with 8-bit channels.
@since0.1.0
Color32
.Color32.fromHSL(h: number, s: number, l: number, a?: number): Color32
Creates a color from HSL values.
@paramh - Hue in degrees (0-360).@params - Saturation as percentage (0-100).@paraml - Lightness as percentage (0-100).@parama - Alpha channel (0-255, defaults to 255).@returnsNew color converted from HSL values.
fromHSL
(const hue: numberhue, 100, const lightness: numberlightness));
} } /** * Runs once per screen refresh to draw all six pattern demonstrations. * Patterns are arranged in a 2x3 grid across the screen. * * Notice: there are NO Color32 objects here. Every draw call uses a palette index. */ Demo.render(): void
Runs once per screen refresh to draw all six pattern demonstrations. Patterns are arranged in a 2x3 grid across the screen. Notice: there are NO Color32 objects here. Every draw call uses a palette index.
render
() {
// Fill the whole screen with the background color (very dark blue-black).
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.clear: (paletteIndex: number) => void
Sets the frame clear color using a palette index. The renderer uses this color when clearing the full display at the start of the next frame.
@since0.1.0@parampaletteIndex - Palette index for the full-screen clear pass.
clear
(const C_BG: 2C_BG);
// Top row: three patterns centered at y=80. this.Demo.drawSpiral(center: Vector2i): void
Draws an Archimedean spiral: a coil of colored dots that expands outward while rotating. The inner dots are near the center, the outer ones are far. Colors are animated - they scroll along the spiral over time. The actual color values are computed in update() and stored in palette slots C_SPIRAL_BASE+i.
@paramcenter - The center point to spiral around.
drawSpiral
(new new Vector2i(x?: number, y?: number): Vector2i
Creates an integer 2D vector, truncating inputs toward zero.
@paramx - Horizontal component (defaults to 0).@paramy - Vertical component (defaults to 0).
Vector2i
(60, 80));
this.Demo.drawRadialLines(center: Vector2i): void
Draws lines radiating from a center point like sun rays. Each ray has a different color (set once in init) and its length pulses.
@paramcenter - The center point to draw rays from.
drawRadialLines
(new new Vector2i(x?: number, y?: number): Vector2i
Creates an integer 2D vector, truncating inputs toward zero.
@paramx - Horizontal component (defaults to 0).@paramy - Vertical component (defaults to 0).
Vector2i
(160, 80));
this.Demo.drawWavePattern(center: Vector2i): void
Draws three wave curves overlapping each other. Two separate waves plus a combined "interference" pattern that shows what happens when the two waves add together. All three wave colors are static - they never change.
@paramcenter - The center point to draw the waves around.
drawWavePattern
(new new Vector2i(x?: number, y?: number): Vector2i
Creates an integer 2D vector, truncating inputs toward zero.
@paramx - Horizontal component (defaults to 0).@paramy - Vertical component (defaults to 0).
Vector2i
(260, 80));
// Bottom row: three more patterns centered at y=150. this.Demo.drawCircleApproximation(center: Vector2i): void
Draws a circle by connecting many short line segments around its edge. This shows how circles can be approximated with only line-drawing primitives. The radius pulses and the whole circle slowly rotates. Segment colors are static (set once in init based on hue position).
@paramcenter - The center of the circle.
drawCircleApproximation
(new new Vector2i(x?: number, y?: number): Vector2i
Creates an integer 2D vector, truncating inputs toward zero.
@paramx - Horizontal component (defaults to 0).@paramy - Vertical component (defaults to 0).
Vector2i
(60, 150));
this.Demo.drawLissajous(center: Vector2i): void
Draws a Lissajous curve - a parametric figure where the x and y axes oscillate at different frequencies. The ratio 3:4 here creates an interlocking looping curve (not a simple figure-eight; a classic figure-eight shape often comes from a 1:2 frequency ratio instead). Lissajous figures are used in physics and electronics to visualize frequency ratios. Colors are animated - 200 curve points are mapped to 32 color bands, and those bands rotate through the rainbow over time.
@paramcenter - The center of the curve.
drawLissajous
(new new Vector2i(x?: number, y?: number): Vector2i
Creates an integer 2D vector, truncating inputs toward zero.
@paramx - Horizontal component (defaults to 0).@paramy - Vertical component (defaults to 0).
Vector2i
(160, 150));
this.Demo.drawTunnel(center: Vector2i): void
Draws a tunnel effect by stacking concentric rectangles of decreasing size. The rectangles slowly rotate and wobble, creating an illusion of depth. Colors are animated - each rectangle's hue and brightness are updated in update().
@paramcenter - The vanishing point (center) of the tunnel.
drawTunnel
(new new Vector2i(x?: number, y?: number): Vector2i
Creates an integer 2D vector, truncating inputs toward zero.
@paramx - Horizontal component (defaults to 0).@paramy - Vertical component (defaults to 0).
Vector2i
(260, 150));
// Label each grid cell so viewers know which pattern they are looking at. // ui.caption() is the shared UI kit's pinned one-line caption - the same widget // every demo in the series uses, so all captions look identical everywhere. // Text draws left-aligned, so we nudge x until short names sit under each center. import uiui.caption(42, 100, 'Spiral'); import uiui.caption(120, 100, 'Radial'); import uiui.caption(250, 100, 'Wave'); import uiui.caption(42, 180, 'Circle'); import uiui.caption(133, 180, 'Lissajous'); import uiui.caption(243, 180, 'Tunnel'); } /** * Draws an Archimedean spiral: a coil of colored dots that expands outward * while rotating. The inner dots are near the center, the outer ones are far. * * Colors are animated - they scroll along the spiral over time. The actual * color values are computed in update() and stored in palette slots C_SPIRAL_BASE+i. * * @param {Vector2i} center - The center point to spiral around. */ Demo.drawSpiral(center: Vector2i): void
Draws an Archimedean spiral: a coil of colored dots that expands outward while rotating. The inner dots are near the center, the outer ones are far. Colors are animated - they scroll along the spiral over time. The actual color values are computed in update() and stored in palette slots C_SPIRAL_BASE+i.
@paramcenter - The center point to spiral around.
drawSpiral
(center: Vector2i
- The center point to spiral around.
@paramcenter - The center point to spiral around.
center
) {
const const maxRadius: 35maxRadius = 35; for (let let i: numberi = 0; let i: numberi < const SPIRAL_POINTS: 100SPIRAL_POINTS; let i: numberi++) { // t goes from animTime (inner) to animTime + 2*PI (outer). // 2*PI is one full rotation, so the spiral wraps around once. const const t: numbert = (let i: numberi / const SPIRAL_POINTS: 100SPIRAL_POINTS) * Math.PI * 2 + this.Demo.animTime: numberanimTime; // How far from the center this dot is. // Point 0 is at the center, point 99 is at maxRadius pixels away. const const radius: numberradius = (let i: numberi / const SPIRAL_POINTS: 100SPIRAL_POINTS) * const maxRadius: 35maxRadius; // Convert polar coordinates (angle t, distance radius) to x,y screen positions. // // Math.cos and Math.sin turn an angle into how far to move on each axis. // Think of a clock hand: cos is how far right/left the tip is, sin is how far up/down. // Math.PI is half a full turn (180 degrees); 2*PI is one full turn (360 degrees). // Multiplying radius scales that direction vector to the dot's distance from center. const const x: numberx = center: Vector2i
- The center point to spiral around.
@paramcenter - The center point to spiral around.
center
.Vector2i.x: number
Horizontal component (defaults to 0).
x
+ Math.cos(const t: numbert) * const radius: numberradius;
const const y: numbery = center: Vector2i
- The center point to spiral around.
@paramcenter - The center point to spiral around.
center
.Vector2i.y: number
Vertical component (defaults to 0).
y
+ Math.sin(const t: numbert) * const radius: numberradius;
// Use the animated color for dot i - already updated in update(). this.Demo.tempVec1: Vector2itempVec1.Vector2i.set(x: number, y: number): Vector2i
Sets both components of this vector. Modifies this vector directly for maximum performance. WARNING: Mutates this vector. Don't use on frozen/cached singletons.
@paramx - New x component.@paramy - New y component.@returnsThis vector for chaining.
set
(Math.floor(const x: numberx), Math.floor(const y: numbery));
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
.drawPixel: (posOrX: Vector2i | number, yOrColor: number, maybeColor?: number) => void
Draws a single pixel. Accepts either: - `(posOrX: Vector2i, yOrColor: number)` where `yOrColor` is the palette index. - `(posOrX: number, yOrColor: number, maybeColor: number)` for `(x, y, paletteIndex)`.
@since0.1.0@paramposOrX - Pixel position as `Vector2i`, or x coordinate when using numeric overload.@paramyOrColor - Palette index for vector overload, or y coordinate for numeric overload.@parammaybeColor - Palette index when using numeric overload.
drawPixel
(this.Demo.tempVec1: Vector2itempVec1, const C_SPIRAL_BASE: 60C_SPIRAL_BASE + let i: numberi);
} } /** * Draws lines radiating from a center point like sun rays. * Each ray has a different color (set once in init) and its length pulses. * * @param {Vector2i} center - The center point to draw rays from. */ Demo.drawRadialLines(center: Vector2i): void
Draws lines radiating from a center point like sun rays. Each ray has a different color (set once in init) and its length pulses.
@paramcenter - The center point to draw rays from.
drawRadialLines
(center: Vector2i
- The center point to draw rays from.
@paramcenter - The center point to draw rays from.
center
) {
const const radius: 35radius = 35; // Maximum length of each ray. for (let let i: numberi = 0; let i: numberi < const RADIAL_LINES: 12RADIAL_LINES; let i: numberi++) { // Space the rays evenly around the circle. The full circle is 2*PI radians. // Adding animTime rotates the whole pattern over time. const const angle: numberangle = (let i: numberi / const RADIAL_LINES: 12RADIAL_LINES) * Math.PI * 2 + this.Demo.animTime: numberanimTime; // Each ray has a different phase offset (i) so they pulse at different times. // The length oscillates between radius*0 and radius*1. const const length: numberlength = const radius: 35radius * (0.5 + 0.5 * Math.sin(this.Demo.animTime: numberanimTime * 2 + let i: numberi)); // Calculate where the tip of this ray is. const const x: numberx = center: Vector2i
- The center point to draw rays from.
@paramcenter - The center point to draw rays from.
center
.Vector2i.x: number
Horizontal component (defaults to 0).
x
+ Math.cos(const angle: numberangle) * const length: numberlength;
const const y: numbery = center: Vector2i
- The center point to draw rays from.
@paramcenter - The center point to draw rays from.
center
.Vector2i.y: number
Vertical component (defaults to 0).
y
+ Math.sin(const angle: numberangle) * const length: numberlength;
// Use the static color for ray i (set in init, never changes). this.Demo.tempVec1: Vector2itempVec1.Vector2i.set(x: number, y: number): Vector2i
Sets both components of this vector. Modifies this vector directly for maximum performance. WARNING: Mutates this vector. Don't use on frozen/cached singletons.
@paramx - New x component.@paramy - New y component.@returnsThis vector for chaining.
set
(Math.floor(const x: numberx), Math.floor(const y: numbery));
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
.drawLine: (p0: Vector2i, p1: Vector2i, paletteIndex: number) => void
Draws a pixel-perfect line between two points. Uses rasterized line drawing without antialiasing.
@since0.1.0@paramp0 - Start position in display coordinates.@paramp1 - End position in display coordinates.@parampaletteIndex - Palette color index.
drawLine
(center: Vector2i
- The center point to draw rays from.
@paramcenter - The center point to draw rays from.
center
, this.Demo.tempVec1: Vector2itempVec1, const C_RADIAL_BASE: 40C_RADIAL_BASE + let i: numberi);
} } /** * Draws three wave curves overlapping each other. * Two separate waves plus a combined "interference" pattern that shows * what happens when the two waves add together. * * All three wave colors are static - they never change. * * @param {Vector2i} center - The center point to draw the waves around. */ Demo.drawWavePattern(center: Vector2i): void
Draws three wave curves overlapping each other. Two separate waves plus a combined "interference" pattern that shows what happens when the two waves add together. All three wave colors are static - they never change.
@paramcenter - The center point to draw the waves around.
drawWavePattern
(center: Vector2i
- The center point to draw the waves around.
@paramcenter - The center point to draw the waves around.
center
) {
const const width: 60width = 60; // The wave spans 60 pixels wide. for (let let x: numberx = 0; let x: numberx < const width: 60width; let x: numberx++) { // Map x from 0..width to a screen position centered around center.x. const const baseX: numberbaseX = center: Vector2i
- The center point to draw the waves around.
@paramcenter - The center point to draw the waves around.
center
.Vector2i.x: number
Horizontal component (defaults to 0).
x
- const width: 60width / 2 + let x: numberx;
// Primary wave: a sine wave. Adding animTime*20 makes it scroll to the left. // Multiplying by 0.2 controls the frequency (how many waves fit in the space). // Multiplying by 15 controls the amplitude (how tall the wave is in pixels). const const y1: numbery1 = Math.sin((let x: numberx + this.Demo.animTime: numberanimTime * 20) * 0.2) * 15; this.Demo.tempVec1: Vector2itempVec1.Vector2i.set(x: number, y: number): Vector2i
Sets both components of this vector. Modifies this vector directly for maximum performance. WARNING: Mutates this vector. Don't use on frozen/cached singletons.
@paramx - New x component.@paramy - New y component.@returnsThis vector for chaining.
set
(const baseX: numberbaseX, center: Vector2i
- The center point to draw the waves around.
@paramcenter - The center point to draw the waves around.
center
.Vector2i.y: number
Vertical component (defaults to 0).
y
+ Math.floor(const y1: numbery1));
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
.drawPixel: (posOrX: Vector2i | number, yOrColor: number, maybeColor?: number) => void
Draws a single pixel. Accepts either: - `(posOrX: Vector2i, yOrColor: number)` where `yOrColor` is the palette index. - `(posOrX: number, yOrColor: number, maybeColor: number)` for `(x, y, paletteIndex)`.
@since0.1.0@paramposOrX - Pixel position as `Vector2i`, or x coordinate when using numeric overload.@paramyOrColor - Palette index for vector overload, or y coordinate for numeric overload.@parammaybeColor - Palette index when using numeric overload.
drawPixel
(this.Demo.tempVec1: Vector2itempVec1, const C_WAVE_1: 5C_WAVE_1);
// Secondary wave: a cosine wave with different speed and size. const const y2: numbery2 = Math.cos((let x: numberx + this.Demo.animTime: numberanimTime * 15) * 0.15) * 10; this.Demo.tempVec1: Vector2itempVec1.Vector2i.set(x: number, y: number): Vector2i
Sets both components of this vector. Modifies this vector directly for maximum performance. WARNING: Mutates this vector. Don't use on frozen/cached singletons.
@paramx - New x component.@paramy - New y component.@returnsThis vector for chaining.
set
(const baseX: numberbaseX, center: Vector2i
- The center point to draw the waves around.
@paramcenter - The center point to draw the waves around.
center
.Vector2i.y: number
Vertical component (defaults to 0).
y
+ Math.floor(const y2: numbery2));
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
.drawPixel: (posOrX: Vector2i | number, yOrColor: number, maybeColor?: number) => void
Draws a single pixel. Accepts either: - `(posOrX: Vector2i, yOrColor: number)` where `yOrColor` is the palette index. - `(posOrX: number, yOrColor: number, maybeColor: number)` for `(x, y, paletteIndex)`.
@since0.1.0@paramposOrX - Pixel position as `Vector2i`, or x coordinate when using numeric overload.@paramyOrColor - Palette index for vector overload, or y coordinate for numeric overload.@parammaybeColor - Palette index when using numeric overload.
drawPixel
(this.Demo.tempVec1: Vector2itempVec1, const C_WAVE_2: 6C_WAVE_2);
// Interference: add both waves together. Divide by 2 to keep it in range. // When waves meet in-phase they reinforce; out-of-phase they cancel. const const y3: numbery3 = Math.sin((let x: numberx + this.Demo.animTime: numberanimTime * 20) * 0.2) * 15 + Math.cos((let x: numberx + this.Demo.animTime: numberanimTime * 15) * 0.15) * 10; this.Demo.tempVec1: Vector2itempVec1.Vector2i.set(x: number, y: number): Vector2i
Sets both components of this vector. Modifies this vector directly for maximum performance. WARNING: Mutates this vector. Don't use on frozen/cached singletons.
@paramx - New x component.@paramy - New y component.@returnsThis vector for chaining.
set
(const baseX: numberbaseX, center: Vector2i
- The center point to draw the waves around.
@paramcenter - The center point to draw the waves around.
center
.Vector2i.y: number
Vertical component (defaults to 0).
y
+ Math.floor(const y3: numbery3 / 2));
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
.drawPixel: (posOrX: Vector2i | number, yOrColor: number, maybeColor?: number) => void
Draws a single pixel. Accepts either: - `(posOrX: Vector2i, yOrColor: number)` where `yOrColor` is the palette index. - `(posOrX: number, yOrColor: number, maybeColor: number)` for `(x, y, paletteIndex)`.
@since0.1.0@paramposOrX - Pixel position as `Vector2i`, or x coordinate when using numeric overload.@paramyOrColor - Palette index for vector overload, or y coordinate for numeric overload.@parammaybeColor - Palette index when using numeric overload.
drawPixel
(this.Demo.tempVec1: Vector2itempVec1, const C_WAVE_3: 7C_WAVE_3);
} } /** * Draws a circle by connecting many short line segments around its edge. * This shows how circles can be approximated with only line-drawing primitives. * The radius pulses and the whole circle slowly rotates. * * Segment colors are static (set once in init based on hue position). * * @param {Vector2i} center - The center of the circle. */ Demo.drawCircleApproximation(center: Vector2i): void
Draws a circle by connecting many short line segments around its edge. This shows how circles can be approximated with only line-drawing primitives. The radius pulses and the whole circle slowly rotates. Segment colors are static (set once in init based on hue position).
@paramcenter - The center of the circle.
drawCircleApproximation
(center: Vector2i
- The center of the circle.
@paramcenter - The center of the circle.
center
) {
// The radius pulses between 2 and 30 pixels (16 ± 14) using Math.sin. const const radius: numberradius = 16 + Math.sin(this.Demo.animTime: numberanimTime) * 14; for (let let i: numberi = 0; let i: numberi < const CIRCLE_SEGMENTS: 32CIRCLE_SEGMENTS; let i: numberi++) { // Calculate the start and end angle of this segment. // Together, all 32 segments cover the full circle (2*PI radians). const const angle1: numberangle1 = (let i: numberi / const CIRCLE_SEGMENTS: 32CIRCLE_SEGMENTS) * Math.PI * 2; const const angle2: numberangle2 = ((let i: numberi + 1) / const CIRCLE_SEGMENTS: 32CIRCLE_SEGMENTS) * Math.PI * 2; // Adding animTime to the angles rotates the whole circle over time. const const x1: numberx1 = center: Vector2i
- The center of the circle.
@paramcenter - The center of the circle.
center
.Vector2i.x: number
Horizontal component (defaults to 0).
x
+ Math.cos(const angle1: numberangle1 + this.Demo.animTime: numberanimTime) * const radius: numberradius;
const const y1: numbery1 = center: Vector2i
- The center of the circle.
@paramcenter - The center of the circle.
center
.Vector2i.y: number
Vertical component (defaults to 0).
y
+ Math.sin(const angle1: numberangle1 + this.Demo.animTime: numberanimTime) * const radius: numberradius;
const const x2: numberx2 = center: Vector2i
- The center of the circle.
@paramcenter - The center of the circle.
center
.Vector2i.x: number
Horizontal component (defaults to 0).
x
+ Math.cos(const angle2: numberangle2 + this.Demo.animTime: numberanimTime) * const radius: numberradius;
const const y2: numbery2 = center: Vector2i
- The center of the circle.
@paramcenter - The center of the circle.
center
.Vector2i.y: number
Vertical component (defaults to 0).
y
+ Math.sin(const angle2: numberangle2 + this.Demo.animTime: numberanimTime) * const radius: numberradius;
// Each segment uses its own static color from the palette. this.Demo.tempVec1: Vector2itempVec1.Vector2i.set(x: number, y: number): Vector2i
Sets both components of this vector. Modifies this vector directly for maximum performance. WARNING: Mutates this vector. Don't use on frozen/cached singletons.
@paramx - New x component.@paramy - New y component.@returnsThis vector for chaining.
set
(Math.floor(const x1: numberx1), Math.floor(const y1: numbery1));
this.Demo.tempVec2: Vector2itempVec2.Vector2i.set(x: number, y: number): Vector2i
Sets both components of this vector. Modifies this vector directly for maximum performance. WARNING: Mutates this vector. Don't use on frozen/cached singletons.
@paramx - New x component.@paramy - New y component.@returnsThis vector for chaining.
set
(Math.floor(const x2: numberx2), Math.floor(const y2: numbery2));
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
.drawLine: (p0: Vector2i, p1: Vector2i, paletteIndex: number) => void
Draws a pixel-perfect line between two points. Uses rasterized line drawing without antialiasing.
@since0.1.0@paramp0 - Start position in display coordinates.@paramp1 - End position in display coordinates.@parampaletteIndex - Palette color index.
drawLine
(this.Demo.tempVec1: Vector2itempVec1, this.Demo.tempVec2: Vector2itempVec2, const C_CIRCLE_BASE: 8C_CIRCLE_BASE + let i: numberi);
} } /** * Draws a Lissajous curve - a parametric figure where the x and y axes * oscillate at different frequencies. The ratio 3:4 here creates an interlocking * looping curve (not a simple figure-eight; a classic figure-eight shape often * comes from a 1:2 frequency ratio instead). * * Lissajous figures are used in physics and electronics to visualize frequency ratios. * * Colors are animated - 200 curve points are mapped to 32 color bands, * and those bands rotate through the rainbow over time. * * @param {Vector2i} center - The center of the curve. */ Demo.drawLissajous(center: Vector2i): void
Draws a Lissajous curve - a parametric figure where the x and y axes oscillate at different frequencies. The ratio 3:4 here creates an interlocking looping curve (not a simple figure-eight; a classic figure-eight shape often comes from a 1:2 frequency ratio instead). Lissajous figures are used in physics and electronics to visualize frequency ratios. Colors are animated - 200 curve points are mapped to 32 color bands, and those bands rotate through the rainbow over time.
@paramcenter - The center of the curve.
drawLissajous
(center: Vector2i
- The center of the curve.
@paramcenter - The center of the curve.
center
) {
const const points: 200points = 200; // More points = smoother curve. const const a: 3a = 3; // Frequency of the horizontal oscillation. const const b: 4b = 4; // Frequency of the vertical oscillation. const const radius: 30radius = 30; // Track the previous point so we can draw a line from previous to current. let let prevX: numberprevX = 0; let let prevY: numberprevY = 0; for (let let i: numberi = 0; let i: numberi <= const points: 200points; let i: numberi++) { // t goes from 0 to 2*PI, tracing out the full curve. const const t: numbert = (let i: numberi / const points: 200points) * Math.PI * 2; // x oscillates at frequency a, y oscillates at frequency b. // Adding animTime to the x calculation makes the figure rotate over time. const const x: numberx = center: Vector2i
- The center of the curve.
@paramcenter - The center of the curve.
center
.Vector2i.x: number
Horizontal component (defaults to 0).
x
+ Math.sin(const a: 3a * const t: numbert + this.Demo.animTime: numberanimTime) * const radius: 30radius;
const const y: numbery = center: Vector2i
- The center of the curve.
@paramcenter - The center of the curve.
center
.Vector2i.y: number
Vertical component (defaults to 0).
y
+ Math.sin(const b: 4b * const t: numbert) * const radius: 30radius;
if (let i: numberi > 0) { // Map this point to one of the 32 color bands. // Math.floor((i / points) * LISSAJOUS_BANDS) gives 0..31. // We cap at LISSAJOUS_BANDS-1 to avoid going out of range. const const band: anyband = Math.min(Math.floor((let i: numberi / const points: 200points) * const LISSAJOUS_BANDS: 32LISSAJOUS_BANDS), const LISSAJOUS_BANDS: 32LISSAJOUS_BANDS - 1); this.Demo.tempVec1: Vector2itempVec1.Vector2i.set(x: number, y: number): Vector2i
Sets both components of this vector. Modifies this vector directly for maximum performance. WARNING: Mutates this vector. Don't use on frozen/cached singletons.
@paramx - New x component.@paramy - New y component.@returnsThis vector for chaining.
set
(Math.floor(let prevX: numberprevX), Math.floor(let prevY: numberprevY));
this.Demo.tempVec2: Vector2itempVec2.Vector2i.set(x: number, y: number): Vector2i
Sets both components of this vector. Modifies this vector directly for maximum performance. WARNING: Mutates this vector. Don't use on frozen/cached singletons.
@paramx - New x component.@paramy - New y component.@returnsThis vector for chaining.
set
(Math.floor(const x: numberx), Math.floor(const y: numbery));
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
.drawLine: (p0: Vector2i, p1: Vector2i, paletteIndex: number) => void
Draws a pixel-perfect line between two points. Uses rasterized line drawing without antialiasing.
@since0.1.0@paramp0 - Start position in display coordinates.@paramp1 - End position in display coordinates.@parampaletteIndex - Palette color index.
drawLine
(this.Demo.tempVec1: Vector2itempVec1, this.Demo.tempVec2: Vector2itempVec2, const C_LISSAJOUS_BASE: 160C_LISSAJOUS_BASE + const band: anyband);
} // Save this point to use as "previous" in the next iteration. let prevX: numberprevX = const x: numberx; let prevY: numberprevY = const y: numbery; } } /** * Draws a tunnel effect by stacking concentric rectangles of decreasing size. * The rectangles slowly rotate and wobble, creating an illusion of depth. * * Colors are animated - each rectangle's hue and brightness are updated in update(). * * @param {Vector2i} center - The vanishing point (center) of the tunnel. */ Demo.drawTunnel(center: Vector2i): void
Draws a tunnel effect by stacking concentric rectangles of decreasing size. The rectangles slowly rotate and wobble, creating an illusion of depth. Colors are animated - each rectangle's hue and brightness are updated in update().
@paramcenter - The vanishing point (center) of the tunnel.
drawTunnel
(center: Vector2i
- The vanishing point (center) of the tunnel.
@paramcenter - The vanishing point (center) of the tunnel.
center
) {
for (let let i: numberi = 0; let i: numberi < const TUNNEL_RECTS: 20TUNNEL_RECTS; let i: numberi++) { // t = i / TUNNEL_RECTS: i = 0 is the outer/near largest rectangle; // the final i is the inner/far smallest (size uses 1 - t below). const const t: numbert = let i: numberi / const TUNNEL_RECTS: 20TUNNEL_RECTS; // Outer rectangles are large, inner ones are small. // The sine term adds a gentle pulsing wobble to the size. const const size: numbersize = (1 - const t: numbert) * 60 + Math.sin(this.Demo.animTime: numberanimTime * 2 + let i: numberi * 0.3) * 5; // Each rectangle orbits slightly around the center at different speeds. const const angle: numberangle = this.Demo.animTime: numberanimTime + let i: numberi * 0.2; const const offsetX: numberoffsetX = Math.cos(const angle: numberangle) * let i: numberi; const const offsetY: numberoffsetY = Math.sin(const angle: numberangle) * let i: numberi; // Position the rectangle centered on the offset point. const const x: numberx = center: Vector2i
- The vanishing point (center) of the tunnel.
@paramcenter - The vanishing point (center) of the tunnel.
center
.Vector2i.x: number
Horizontal component (defaults to 0).
x
- const size: numbersize / 2 + const offsetX: numberoffsetX;
const const y: numbery = center: Vector2i
- The vanishing point (center) of the tunnel.
@paramcenter - The vanishing point (center) of the tunnel.
center
.Vector2i.y: number
Vertical component (defaults to 0).
y
- const size: numbersize / 2 + const offsetY: numberoffsetY;
// Use the animated color for this rectangle (updated in update()). this.Demo.tempRect: Rect2itempRect.Rect2i.set(x: number, y: number, width: number, height: number): Rect2i
Sets all components of this rectangle. Modifies this rectangle directly for maximum performance. WARNING: Mutates this rectangle. Don't use on frozen/cached singletons.
@paramx - New x position.@paramy - New y position.@paramwidth - New width.@paramheight - New height.@returnsThis rectangle for chaining.
set
(Math.floor(const x: numberx), Math.floor(const y: numbery), Math.floor(const size: numbersize), Math.floor(const size: numbersize));
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) => void
Draws an unfilled rectangle outline.
@since0.1.0@paramrect - Rectangle bounds in display coordinates.@parampaletteIndex - Palette color index.
drawRect
(this.Demo.tempRect: Rect2itempRect, const C_TUNNEL_BASE: 192C_TUNNEL_BASE + let i: numberi);
} } } // Hand the Demo class to BLIT386 to start the demo loop. function bootstrap(DemoClass: DemoConstructor, options?: BootstrapOptions): Promise<boolean>
One-liner bootstrap function for BLIT386 demos. Handles canvas retrieval and engine initialization. Backend selection (WebGPU or software fallback) is managed internally by BTAPI. This function provides a streamlined way to start a demo with sensible defaults while allowing customization through options.
@since0.2.0@changed1.4.0 Calling `bootstrap()` again while already initialized now routes to a hot swap (via {@link registerHotReload}) when a Vite HMR context is registered, or logs a double-bootstrap guard and returns `false` otherwise - previously it silently started a second, unstoppable `GameLoop`.@changed1.7.0 Exposes `BT` on `window.BT` after bootstrap finishes, gated by {@link BootstrapOptions.exposeGlobal} (default: {@link BT.isDevMode}).@paramDemoClass - Demo class constructor implementing `IBTDemo` (optional `configure()` for hardware settings).@paramoptions - Optional configuration for IDs and callbacks.@returns`true` when the demo boots successfully; otherwise `false`.@example// Simplest usage - uses default IDs. bootstrap(MyDemo);@example// With custom options. bootstrap(MyDemo, { canvasID: 'custom-canvas', containerID: 'custom-container', onSuccess: () => console.log('Demo started!'), onError: (err) => analytics.trackError(err), });@example// Await the result. const success = await bootstrap(MyDemo); if (success) { console.log('Demo is running'); }
bootstrap
(class Demo
Demonstrates animated mathematical patterns using primitive drawing. Each section shows a different algorithmic visual effect arranged in a 2x3 grid.
@implementsIBTDemo
Demo
);