/*
Patterns: animated mathematical art using only primitive drawing.
@description Animated mathematical art from primitives alone: spirals, Lissajous curves, waves, and a tunnel.
Part of the BLIT386 demo series.
Prerequisites:
- Basics: https://demos.blit386.dev/basics
- Primitives: https://demos.blit386.dev/primitives
- Colors: https://demos.blit386.dev/colors
Live version: https://demos.blit386.dev/patterns
Guides:
- Palette: https://blit386.dev/docs/guides/palette#draw-using-absolute-palette-indices
- Overlay: https://blit386.dev/docs/guides/overlay#optional-bands
Reference:
- Rendering: https://blit386.dev/docs/api/rendering#primitives
- Core Types: https://blit386.dev/docs/api/core-types#color32
- Overlay: https://blit386.dev/docs/api/overlay#timing-chart-optional
---
WHAT YOU WILL SEE
-----------------
Six small animated patterns on a near-black screen, arranged in two rows of three, each
with an amber caption under it:
- Spiral: 100 dots coiling out from the center, spinning, with colors that slide along
the coil.
- Radial: 12 rays shooting out from a center point like sun rays, each pulsing in length
while the whole set slowly turns.
- Wave: a blue sine wave, an orange cosine wave, and a green "interference" wave that is
the two added together, all scrolling sideways.
- Circle: a ring of 32 short line segments in rainbow colors that slowly spins while its
radius breathes in and out.
- Lissajous: a smooth looping curve (the kind physics and electronics people draw on
oscilloscopes) that slowly changes shape, with 32 color bands rotating around it.
- Tunnel: 20 nested squares that shrink toward the center, wobble, and orbit, like
looking down a pipe.
The engine overlay is open from the start with the full 256-slot palette grid and a
timing chart in "rich" diagnostics mode, so you can watch the animated palette ranges
change color every tick.
WHAT YOU WILL LEARN
-------------------
- Drawing with just three primitives: BT.drawPixel(), BT.drawLine(), and BT.drawRect().
No images, no sprites.
- Turning an angle and a distance into x and y with Math.cos() and Math.sin().
- Palette-first animation: update() recomputes colors with Color32.fromHSL() and writes
them into reserved palette ranges; render() only passes slot numbers.
- Reusing a few Vector2i and Rect2i objects (tempVec1, tempVec2, tempRect) with .set()
instead of creating new ones every frame.
- Styling the overlay from configure() with overlayStyle and overlayTimingChartStyle,
and turning on the renderer diagnostics bar.
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", and so on).
Some colors never change (white, the background, the three wave colors, the 32 circle
hues, the 12 ray hues). Those are set once in init(). Other colors animate (the 100
spiral dots, the 32 Lissajous bands, the 20 tunnel squares). Those are recalculated every
tick in update() and stored back in the palette. render() only ever uses palette index
numbers, never Color32 objects.
TRY THIS
--------
- Watch the overlay's palette grid: slots 60-159 (spiral), 160-191 (Lissajous), and
192-211 (tunnel) keep changing, while 8-39 (circle) and 40-51 (rays) stay still.
- Press the backquote key (`) to hide the overlay and see the patterns alone.
*/
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, class Color32Mutable 32-bit RGBA color value with 8-bit channels.Color32, class Rect2iInteger rectangle for pixel-perfect bounds and regions.
Used throughout the engine for sprite regions, display-space bounds, and
zero-allocation geometry helpers. Both convenience getters and allocation-free
`*To()` helpers are provided so callers can choose between readability and
hot-path efficiency.Rect2i, class Vector2iInteger 2D vector for pixel-perfect positioning.
Used for points, sizes, directions, and camera offsets throughout the engine.
The API includes both allocation-free `*To()` / `*InPlace()` variants and
convenience methods that return new vectors.Vector2i } from 'blit386';
import { import applyThemeapplyTheme, import uiui } from './shared/ui.js';
/** @typedef {import('blit386').Palette} Palette */
// These numbers are the "addresses" in the palette table. Slot 0 is always reserved for
// transparent, so we never use it. Each color gets a readable name so the code is easier
// to follow.
// Static colors: set once in init(), never changed.
const const C_WHITE: 1C_WHITE = 1; // pure white: the overlay bar 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
const const C_WAVE_1: 5C_WAVE_1 = 5; // blue: the sine wave
const const C_WAVE_2: 6C_WAVE_2 = 6; // orange: the cosine wave
const const C_WAVE_3: 7C_WAVE_3 = 7; // green: the interference wave (both added together)
// Circle segments: 32 slots at 8-39. Each segment gets its own hue. The hues never
// change, so these are static too.
const const C_CIRCLE_BASE: 8C_CIRCLE_BASE = 8;
const const CIRCLE_SEGMENTS: 32CIRCLE_SEGMENTS = 32;
// Radial rays: 12 slots at 40-51, one fixed hue per ray. Static.
const const C_RADIAL_BASE: 40C_RADIAL_BASE = 40;
const const RADIAL_LINES: 12RADIAL_LINES = 12;
// The dynamic colors start here: everything below is repainted every tick in update().
// Spiral dots: 100 slots at 60-159. Each dot's hue moves forward as animTime grows, so
// the colors slide along the coil.
const const C_SPIRAL_BASE: 60C_SPIRAL_BASE = 60;
const const SPIRAL_POINTS: 100SPIRAL_POINTS = 100;
// Lissajous curve: 32 color bands at 160-191. The 200-point curve is split into 32 color
// groups whose hues rotate over time.
const const C_LISSAJOUS_BASE: 160C_LISSAJOUS_BASE = 160;
const const LISSAJOUS_BANDS: 32LISSAJOUS_BANDS = 32;
// Tunnel squares: 20 slots at 192-211. The large outer square is darkest; the squares get
// brighter as they shrink toward the center.
const const C_TUNNEL_BASE: 192C_TUNNEL_BASE = 192;
const const TUNNEL_RECTS: 20TUNNEL_RECTS = 20;
// Animated mathematical patterns drawn with primitives only, six of them in a 2x3 grid.
class class DemoDemo {
// A clock that counts up in seconds. It makes the patterns move.
Demo.animTime: numberanimTime = 0;
// The palette holds every color we are allowed to use: a box of 256 numbered paints.
/** @type {Palette | null} */
Demo.palette: Palette | nullpalette = null;
// Where the shared UI theme colors landed in the palette, filled by applyTheme() in
// init(). The UI kit draws the six captions with these slots.
Demo.theme: nulltheme = null;
// These Vector2i and Rect2i objects are created once and reused every frame. Making
// new objects inside a loop every frame can slow things down, because the browser has
// to clean up the old ones. Reusing them avoids that.
Demo.tempVec1: Vector2itempVec1 = new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(0, 0);
Demo.tempVec2: Vector2itempVec2 = new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(0, 0);
Demo.tempRect: Rect2itempRect = new new Rect2i(x?: number, y?: number, width?: number, height?: number): Rect2iCreates an integer rectangle, truncating all inputs toward zero.Rect2i(0, 0, 0, 0);
// Optional engine settings. We keep the default 320x240 screen, open the overlay from
// the start with the full 256-slot palette grid (default column count), and turn on
// the timing chart in "rich" mode plus the renderer diagnostics bar, so GPU pipeline
// pressure is visible while the patterns run.
Demo.configure(): {
isOverlayPaletteEnabled: boolean;
isOverlayVisibleAtStart: boolean;
overlayStyle: {
barPaletteIndex: number;
textPaletteIndex: number;
gapPaletteIndex: number;
};
isOverlayTimingChartEnabled: boolean;
overlayTimingChartDiagnostics: string;
isOverlayRendererDiagnosticsBarEnabled: boolean;
overlayTimingChartStyle: {
updateBarPaletteIndex: number;
renderBarPaletteIndex: number;
warningPaletteIndex: number;
errorPaletteIndex: number;
tagPaletteIndex: number;
};
}
configure() {
return {
isOverlayPaletteEnabled: booleanisOverlayPaletteEnabled: true,
isOverlayVisibleAtStart: booleanisOverlayVisibleAtStart: true,
// Overlay bar: white with dark text.
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,
// The timing chart bars borrow the wave colors: green for update(), blue for
// render(), orange for warnings and errors.
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 and sets up the palette. The order matters:
//
// 1. Create the palette: the 256-slot color table.
// 2. Fill in the static colors: the ones that never change.
// 3. BT.paletteSet(): tell the engine to use this palette.
//
// Returns true when ready to run.
async Demo.init(): unknowninit() {
// 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 | 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: register the static colors. First the basic ones.
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)); // overlay bar white
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(15, 15, 25)); // very dark blue-black
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_TAG: 3C_TAG, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(200, 200, 200)); // dim white for chart tags
// Wave pattern: three fixed colors.
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_WAVE_1: 5C_WAVE_1, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(100, 200, 255)); // blue wave
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_WAVE_2: 6C_WAVE_2, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(255, 150, 100)); // orange wave
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_WAVE_3: 7C_WAVE_3, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(150, 255, 150)); // green interference
// Circle segments: 32 colors spread across the rainbow. These hues do not depend
// on animTime, so 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: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_CIRCLE_BASE: 8C_CIRCLE_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));
}
// Radial rays: 12 colors, one per ray.
for (let let i: numberi = 0; let i: numberi < const RADIAL_LINES: 12RADIAL_LINES; let i: numberi++) {
// A little less saturated and a little brighter than the circle colors.
const const hue: numberhue = (let i: numberi / const RADIAL_LINES: 12RADIAL_LINES) * 360;
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_RADIAL_BASE: 40C_RADIAL_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, 80, 60));
}
// The spiral, Lissajous, and tunnel slots are left empty here. update() fills
// them in before the first frame renders.
// Install the shared UI theme the captions draw 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: Palettepalette);
// 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;
... 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;
}
// Runs at a fixed rate (60 times per second by default). Advances the animation clock
// and recomputes the dynamic palette entries for the spiral, Lissajous, and tunnel.
// All color math happens here; render() only uses palette index numbers.
Demo.update(): voidupdate() {
// deltaSeconds is the length of 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;
... 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;
// Spiral colors: 100 animated dots. Each dot's hue depends on its place along the
// coil and on the current time, so the colors slide along the coil as animTime
// grows.
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
// scrolls the colors by 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), and lightness (0-100).
// Full saturation and mid lightness give vivid rainbow colors. `% 360`
// wraps the hue back onto the color wheel once it passes 360.
this.Demo.palette: Palette | nullpalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_SPIRAL_BASE: 60C_SPIRAL_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 % 360, 100, 50));
}
// Lissajous color bands: 32 bands for the 200-point curve. Splitting the points
// into 32 groups saves 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 by 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 | nullpalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_LISSAJOUS_BASE: 160C_LISSAJOUS_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 % 360, 100, 50));
}
// Tunnel colors: 20 squares. Low i is the big outer square, high i the small
// inner one (drawTunnel() sizes them with 1 - t), so the lightness ramp below
// makes the tunnel brighter toward the center.
for (let let i: numberi = 0; let i: numberi < const TUNNEL_RECTS: 20TUNNEL_RECTS; let i: numberi++) {
// t goes from 0 (outer, largest) to just under 1 (inner, smallest).
const const t: numbert = let i: numberi / const TUNNEL_RECTS: 20TUNNEL_RECTS;
// The hue rotates at 50 degrees per second, and each square is offset by
// t * 360 so the colors spread across the rainbow from outer to inner.
const const hue: numberhue = (const t: numbert * 360 + this.Demo.animTime: numberanimTime * 50) % 360;
// Lightness goes from 30 (dark, outer) to nearly 70 (bright, inner).
const const lightness: numberlightness = 30 + const t: numbert * 40;
this.Demo.palette: Palette | nullpalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_TUNNEL_BASE: 192C_TUNNEL_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, const lightness: numberlightness));
}
}
// Runs once per screen refresh and draws all six patterns in a 2x3 grid. Notice there
// are no Color32 objects here: every draw call uses a palette index.
Demo.render(): voidrender() {
// Fill the whole screen with the very dark blue-black background.
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 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);
// Top row: three patterns centered at y = 80.
this.Demo.drawSpiral(center: any): voiddrawSpiral(new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(60, 80));
this.Demo.drawRadialLines(center: any): voiddrawRadialLines(new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(160, 80));
this.Demo.drawWavePattern(center: any): voiddrawWavePattern(new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(260, 80));
// Bottom row: three more centered at y = 150.
this.Demo.drawCircleApproximation(center: any): voiddrawCircleApproximation(new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(60, 150));
this.Demo.drawLissajous(center: any): voiddrawLissajous(new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(160, 150));
this.Demo.drawTunnel(center: any): voiddrawTunnel(new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(260, 150));
// Caption 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. Text draws left-aligned, so each x is nudged
// until the name sits under its pattern's 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 from
// `center` while rotating. Inner dots sit near the center, outer ones far away. The
// colors are animated: update() computes them and stores them in the slots
// C_SPIRAL_BASE + i.
Demo.drawSpiral(center: any): voiddrawSpiral(center: anycenter) {
const const maxRadius: 35maxRadius = 35;
for (let let i: numberi = 0; let i: numberi < const SPIRAL_POINTS: 100SPIRAL_POINTS; let i: numberi++) {
// The angle runs from animTime (inner) to animTime + 2 * PI (outer). 2 * PI
// is one full turn, so the coil wraps around once, and adding animTime spins
// it.
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: dot 0 sits at the center, dot 99 is
// almost 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 and y on screen.
// Math.cos and Math.sin turn an angle into how far to move along each axis.
// Think of a clock hand: cos is how far right or left the tip is, sin how far
// up or down. Math.PI is half a turn (180 degrees); 2 * PI is a full turn
// (360 degrees). Multiplying by radius stretches that direction to the dot's
// distance from the center.
const const x: anyx = center: anycenter.x + Math.cos(const t: numbert) * const radius: numberradius;
const const y: anyy = center: anycenter.y + Math.sin(const t: numbert) * const radius: numberradius;
// Draw the dot with its animated color, already updated in update().
this.Demo.tempVec1: Vector2itempVec1.Vector2i.set(x: number, y: number): Vector2iSets both components of this vector.
Modifies this vector directly for maximum performance.
WARNING: Mutates this vector. Don't use on frozen/cached singletons.set(Math.floor(const x: anyx), Math.floor(const y: anyy));
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.drawPixel: (posOrX: Vector2i | number, yOrColor: number, maybeColor?: number) => voidDraws 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)`.drawPixel(this.Demo.tempVec1: Vector2itempVec1, const C_SPIRAL_BASE: 60C_SPIRAL_BASE + let i: numberi);
}
}
// Draws lines radiating from `center` like sun rays. Each ray has its own fixed color
// (set once in init()) and its length pulses.
Demo.drawRadialLines(center: any): voiddrawRadialLines(center: anycenter) {
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 gets a different phase offset (i) so they pulse at different
// times. The length swings between 0 and the full radius.
const const length: numberlength = const radius: 35radius * (0.5 + 0.5 * Math.sin(this.Demo.animTime: numberanimTime * 2 + let i: numberi));
// Where the tip of this ray is.
const const x: anyx = center: anycenter.x + Math.cos(const angle: numberangle) * const length: numberlength;
const const y: anyy = center: anycenter.y + Math.sin(const angle: numberangle) * const length: numberlength;
// Draw the ray from the center to the tip in its fixed color.
this.Demo.tempVec1: Vector2itempVec1.Vector2i.set(x: number, y: number): Vector2iSets both components of this vector.
Modifies this vector directly for maximum performance.
WARNING: Mutates this vector. Don't use on frozen/cached singletons.set(Math.floor(const x: anyx), Math.floor(const y: anyy));
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.drawLine: (p0: Vector2i, p1: Vector2i, paletteIndex: number) => voidDraws a pixel-perfect line between two points.
Uses rasterized line drawing without antialiasing.drawLine(center: anycenter, this.Demo.tempVec1: Vector2itempVec1, const C_RADIAL_BASE: 40C_RADIAL_BASE + let i: numberi);
}
}
// Draws three wave curves on top of each other around `center`: a sine wave, a cosine
// wave, and an "interference" wave that shows what happens when the two are added.
// All three wave colors are static.
Demo.drawWavePattern(center: any): voiddrawWavePattern(center: anycenter) {
const const width: 60width = 60; // the waves span 60 pixels
for (let let x: numberx = 0; let x: numberx < const width: 60width; let x: numberx++) {
// Map x from 0..width to a screen column centered on center.x.
const const baseX: numberbaseX = center: anycenter.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 sets the frequency (how many waves fit in the
// space) and multiplying by 15 sets 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): Vector2iSets both components of this vector.
Modifies this vector directly for maximum performance.
WARNING: Mutates this vector. Don't use on frozen/cached singletons.set(const baseX: numberbaseX, center: anycenter.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;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.drawPixel: (posOrX: Vector2i | number, yOrColor: number, maybeColor?: number) => voidDraws 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)`.drawPixel(this.Demo.tempVec1: Vector2itempVec1, const C_WAVE_1: 5C_WAVE_1);
// Secondary wave: a cosine wave with a 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): Vector2iSets both components of this vector.
Modifies this vector directly for maximum performance.
WARNING: Mutates this vector. Don't use on frozen/cached singletons.set(const baseX: numberbaseX, center: anycenter.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;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.drawPixel: (posOrX: Vector2i | number, yOrColor: number, maybeColor?: number) => voidDraws 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)`.drawPixel(this.Demo.tempVec1: Vector2itempVec1, const C_WAVE_2: 6C_WAVE_2);
// Interference: both waves added together, divided by 2 to keep it in range.
// Where the waves line up they reinforce each other; where they are opposite
// they cancel out.
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): Vector2iSets both components of this vector.
Modifies this vector directly for maximum performance.
WARNING: Mutates this vector. Don't use on frozen/cached singletons.set(const baseX: numberbaseX, center: anycenter.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;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.drawPixel: (posOrX: Vector2i | number, yOrColor: number, maybeColor?: number) => voidDraws 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)`.drawPixel(this.Demo.tempVec1: Vector2itempVec1, const C_WAVE_3: 7C_WAVE_3);
}
}
// Draws a circle by joining many short line segments around its edge, showing how a
// circle can be approximated with nothing but lines. The radius pulses and the whole
// ring slowly rotates. Segment colors are static (set once in init() by hue).
Demo.drawCircleApproximation(center: any): voiddrawCircleApproximation(center: anycenter) {
// The radius pulses between 2 and 30 pixels (16 plus or minus 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++) {
// 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 both angles rotates the whole circle over time.
const const x1: anyx1 = center: anycenter.x + Math.cos(const angle1: numberangle1 + this.Demo.animTime: numberanimTime) * const radius: numberradius;
const const y1: anyy1 = center: anycenter.y + Math.sin(const angle1: numberangle1 + this.Demo.animTime: numberanimTime) * const radius: numberradius;
const const x2: anyx2 = center: anycenter.x + Math.cos(const angle2: numberangle2 + this.Demo.animTime: numberanimTime) * const radius: numberradius;
const const y2: anyy2 = center: anycenter.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): Vector2iSets both components of this vector.
Modifies this vector directly for maximum performance.
WARNING: Mutates this vector. Don't use on frozen/cached singletons.set(Math.floor(const x1: anyx1), Math.floor(const y1: anyy1));
this.Demo.tempVec2: Vector2itempVec2.Vector2i.set(x: number, y: number): Vector2iSets both components of this vector.
Modifies this vector directly for maximum performance.
WARNING: Mutates this vector. Don't use on frozen/cached singletons.set(Math.floor(const x2: anyx2), Math.floor(const y2: anyy2));
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.drawLine: (p0: Vector2i, p1: Vector2i, paletteIndex: number) => voidDraws a pixel-perfect line between two points.
Uses rasterized line drawing without antialiasing.drawLine(this.Demo.tempVec1: Vector2itempVec1, this.Demo.tempVec2: Vector2itempVec2, const C_CIRCLE_BASE: 8C_CIRCLE_BASE + let i: numberi);
}
}
// Draws a Lissajous curve: a figure where x and y swing back and forth at different
// frequencies. The 3:4 ratio here gives an interlocking looping curve (a classic
// figure-eight usually comes from a 1:2 ratio). Lissajous figures are used in physics
// and electronics to visualize frequency ratios. The colors are animated: 200 curve
// points map to 32 color bands, and those bands rotate through the rainbow over time.
Demo.drawLissajous(center: any): voiddrawLissajous(center: anycenter) {
const const points: 200points = 200; // more points, smoother curve
const const a: 3a = 3; // frequency of the horizontal swing
const const b: 4b = 4; // frequency of the vertical swing
const const radius: 30radius = 30;
// Remember the previous point so each step draws a line from it to the new one.
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 runs from 0 to 2 * PI, tracing the full curve.
const const t: numbert = (let i: numberi / const points: 200points) * Math.PI * 2;
// x swings at frequency a, y at frequency b. Adding animTime to the x phase
// makes the figure slowly change shape over time.
const const x: anyx = center: anycenter.x + Math.sin(const a: 3a * const t: numbert + this.Demo.animTime: numberanimTime) * const radius: 30radius;
const const y: anyy = center: anycenter.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..32, so Math.min caps it at 31 to stay in
// 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): Vector2iSets both components of this vector.
Modifies this vector directly for maximum performance.
WARNING: Mutates this vector. Don't use on frozen/cached singletons.set(Math.floor(let prevX: numberprevX), Math.floor(let prevY: numberprevY));
this.Demo.tempVec2: Vector2itempVec2.Vector2i.set(x: number, y: number): Vector2iSets both components of this vector.
Modifies this vector directly for maximum performance.
WARNING: Mutates this vector. Don't use on frozen/cached singletons.set(Math.floor(const x: anyx), Math.floor(const y: anyy));
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.drawLine: (p0: Vector2i, p1: Vector2i, paletteIndex: number) => voidDraws a pixel-perfect line between two points.
Uses rasterized line drawing without antialiasing.drawLine(this.Demo.tempVec1: Vector2itempVec1, this.Demo.tempVec2: Vector2itempVec2, const C_LISSAJOUS_BASE: 160C_LISSAJOUS_BASE + const band: anyband);
}
// Save this point as "previous" for the next step.
let prevX: numberprevX = const x: anyx;
let prevY: numberprevY = const y: anyy;
}
}
// Draws a tunnel by stacking 20 nested squares of shrinking size. They slowly orbit
// and wobble, which gives the illusion of depth. Colors are animated: each square's
// hue and brightness come from update().
Demo.drawTunnel(center: any): voiddrawTunnel(center: anycenter) {
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 big outer square, the last i the small
// inner one (the size uses 1 - t below).
const const t: numbert = let i: numberi / const TUNNEL_RECTS: 20TUNNEL_RECTS;
// Outer squares are large, inner ones 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 square orbits a little around the center. The orbit radius is i, so
// the inner squares swing farther, and i * 0.2 gives each one a different
// phase.
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 square so it is centered on the offset point.
const const x: numberx = center: anycenter.x - const size: numbersize / 2 + const offsetX: numberoffsetX;
const const y: numbery = center: anycenter.y - const size: numbersize / 2 + const offsetY: numberoffsetY;
// Draw the outline in its animated color (updated in update()).
this.Demo.tempRect: Rect2itempRect.Rect2i.set(x: number, y: number, width: number, height: number): Rect2iSets all components of this rectangle.
Modifies this rectangle directly for maximum performance.
WARNING: Mutates this rectangle. Don't use on frozen/cached singletons.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;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.drawRect: (rect: Rect2i, paletteIndex: number) => voidDraws an unfilled rectangle outline.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.bootstrap(class DemoDemo);