/*

Game Scene: one small world that uses almost everything from the series.
@description The capstone: tilemap ground, patterns, sprites, camera, animation, and looping music in one scene.

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
  - Patterns: https://demos.blit386.dev/patterns
  - Camera: https://demos.blit386.dev/camera
  - Sprites: https://demos.blit386.dev/sprites
  - Animation: https://demos.blit386.dev/animation
  - Sprite Effects: https://demos.blit386.dev/sprite-effects
  - Starfield: https://demos.blit386.dev/starfield
  - Tilemap: https://demos.blit386.dev/tilemap
  - Image Output: https://demos.blit386.dev/image-output
  - Music: https://demos.blit386.dev/music
  - Random Basics: https://demos.blit386.dev/random-basics

Live version: https://demos.blit386.dev/game-scene

Guides:
  - Palette: https://blit386.dev/docs/guides/palette#per-draw-palette-offsets-zero-cost-color-variants
  - Game Loop: https://blit386.dev/docs/guides/game-loop#smoothing-motion-between-updates
  - Audio: https://blit386.dev/docs/guides/audio#playing-music
  - Random: https://blit386.dev/docs/guides/random#deterministic-by-default
  - Overlay: https://blit386.dev/docs/guides/overlay#custom-rows

Reference:
  - Palette: https://blit386.dev/docs/api/palette#per-draw-offset
  - Camera: https://blit386.dev/docs/api/camera
  - Game Loop: https://blit386.dev/docs/api/game-loop#timer
  - Audio: https://blit386.dev/docs/api/audio#loop-points
  - Rendering: https://blit386.dev/docs/api/rendering#frame-capture
  - Easing: https://blit386.dev/docs/api/easing#applyeasing
  - Overlay: https://blit386.dev/docs/api/overlay#custom-rows

---

WHAT YOU WILL SEE
-----------------

A 640x240 world seen through a 320x240 window. From back to front:

- A sky of 20 horizontal bands, eased from a deep blue at the top to a pale blue at the
  horizon, with five two-rectangle clouds that scroll at 22 percent of the camera speed
  (the parallax idea from Starfield).
- Four buildings filled with a 4x4 checker pattern (Patterns) and framed with an outline,
  standing on the far edge of the walkway.
- A grass strip from y = 188 down, a 3-pixel dark line on top of it, and above that the
  walkway: one row of 16x16 tiles alternating two grass, two brown dirt, picked by tile
  ID from an array (Tilemap).
- The 44x44 rock sprite from Sprites walking left and right along the walkway at one
  pixel per tick, bobbing one pixel every 8 ticks, with a thin shadow under its feet. The
  camera eases after it (Camera) and is clamped to the world.
- Three sparkles every 30 ticks around the rock: 3x3 squares that hue-shift and fade out
  over 72 ticks (the particle idea from Animation).
- A 20-second day/night cycle: every world color dims toward a cool blue at night and
  back (the palette tinting idea from Sprite Effects).
- Looping background music with a real intro-then-loop point (Music), a chime on every
  day-phase change, and a blip after a successful PNG capture.
- Two small UI kit panels (src/shared/ui.js) along the top: Capstone in the top-left
  corner with a Save PNG button (Image Output), and Status in the top-right corner with
  the score and the time of day. Until sound is unlocked, the standard "enable sound"
  hint sits on the grass in the bottom-left corner.
- Score, rock position, and day phase as engine overlay rows above the FPS bar.

WHAT YOU WILL LEARN
-------------------

- Palette-first lighting: every ambient-lit color is written into a dedicated palette
  slot in update(); render() passes palette indices only (see below).
- Per-draw palette offsets: BT.drawSprite(sheet, rect, pos, spriteColorCount) shifts the
  rock's pixel indices into a second "ambient" block of slots that update() re-lights
  every tick.
- Color32.multiply(), Color32.lerp(), Color32.fromHSL(), and applyEasing(t, 'ease-in-out')
  for the sky gradient.
- Fixed-step timing with Timer.fireIfElapsed(tick) for the walk bob, the score, and the
  sparkle spawns.
- Smoothing the rock and the camera between ticks with BT.renderAlpha, with zero
  per-frame allocation (reused Vector2i and Rect2i instances).
- BT.cameraSet(), BT.cameraReset(), and BT.cameraClamp(camera, worldSize, viewSize),
  plus a second, slower camera for the sky layer.
- BT.musicPlay(clip, { loop, loopStart, loopEnd }), AudioClip.synth(),
  BT.synthPreset.blip(), and BT.soundPlay(); music requested before the audio unlock
  starts on the first click or key press.
- BT.downloadFrame(filename) from a UI kit button bound to Space, with a busy flag while
  the promise is pending.
- BT.random.int(min, max) for the sparkle scatter.
- Overlay configuration: overlayRows(), overlayStyle, overlayTimingChartStyle, the palette
  grid (isOverlayPaletteEnabled, 32 columns, 3 visible rows), and hiding the toggle hint
  with isOverlayToggleHintVisible: false.

HOW THE DAY/NIGHT PALETTE WORKS
-------------------------------

Instead of computing base.multiply(ambient) in render() and passing a Color32 to draw
calls, update() pre-computes those multiplied colors and stores them in dedicated palette
slots. render() only ever uses palette index numbers.

Color32.multiply() scales each channel of the base color by the matching channel of the
ambient color and returns a new Color32 - like shining a colored light on a surface: a
blue light on a red wall gives a darker, purplish result.

Example: the grass fill has a base color (50, 140, 70) and a reserved slot C_GRASS. Every
tick update() computes grassBaseColor.multiply(ambient) and writes it to C_GRASS.
render() calls BT.drawRectFill(rect, C_GRASS) - no Color32 needed there.

The ambient color itself comes from a cosine over the 1200-tick cycle: bright and warm
(255, 255, 255) at the start, cool and dark (70, 75, 120) halfway through.

TRY THIS
--------

- Click or tap "Save PNG (Space)", or press Space, to download blit386-scene.png. The
  label reads "Capturing..." while the browser writes the file, then "Saved: ..." for
  3 seconds, and a blip plays.
- Click anywhere or press any key once to unlock audio; the music starts from its intro
  and then loops between 1.5 s and 7.9 s. Listen for the chime every 5 seconds as the day
  phase changes.
- Press Backquote (`) to open the engine overlay. The three custom rows show the score,
  the rock's world position, and the day phase; the palette grid at the bottom shows the
  scene slots re-lighting as the day turns.

*/

import { function applyEasing(t: number, easing: EasingFunction): number
Applies an easing curve to a normalized time value.
@since1.0.3@changed1.5.0 Added sine, cubic, quartic, quintic, expo, circ, back, elastic, and bounce families.@paramt - Normalized time in [0, 1]. Values outside this range are not clamped.@parameasing - Easing curve to apply.@returnsEased value. Guaranteed to return 0 for `t = 0` and 1 for `t = 1`.
applyEasing
, class AudioClip
Decoded audio asset with its winning source URL and buffer-derived metadata. Construct instances with {@link AudioClip.load } , {@link AudioClip.loadAll } , or {@link AudioClip.synth } ; there is no public constructor.
@since1.3.0
AudioClip
, 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;
    ... 124 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
,
type Rect2i = Rect2i
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
,
type SpriteSheet = SpriteSheet
class SpriteSheet
Sprite-sheet wrapper around a loaded image asset. The class keeps the original image available for CPU-side inspection while lazily creating and caching a GPU texture for rendering. When possible, `load()` also pre-decodes the source into an `ImageBitmap` so texture uploads preserve pixel-art alpha and color values more reliably. After calling `indexize()`, the sheet stores palette indices rather than RGBA data. The GPU texture becomes an `r8uint` format uploaded via `writeTexture`. The original RGBA bytes are retained so `reindexize()` can re-convert without reloading the image.
@since0.1.0
SpriteSheet
, class Timer
Fixed-tick interval helper for {@link IBTDemo.update } loops. Counts engine ticks ( {@link BT.ticks } ), which advance once per fixed update at {@link HardwareSettings.targetFPS } , not once per {@link IBTDemo.render } frame. Convert ticks to seconds with `intervalTicks / BT.targetFPS`. Tracks a "last fired" tick and reports when a configured interval has elapsed. Useful for periodic events such as particle spawning, score ticks, or palette swaps.
@since1.0.3
Timer
, 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, import UI_ANCHORSUI_ANCHORS } from './shared/ui.js'; /** @typedef {import('blit386').Palette} Palette */ /** @typedef {import('blit386').SpriteSheet} SpriteSheet */ /** @typedef {import('blit386').Rect2i} Rect2i */ // Internal game resolution width. const const DISPLAY_W: 320DISPLAY_W = 320; // The level is wider than the screen so the camera can scroll (Camera). const const WORLD_W: 640WORLD_W = 640; const const WORLD_H: 240WORLD_H = 240; // Where the walkway ends and the grass starts. const const GROUND_Y: 188GROUND_Y = 188; // One row of 16 px tiles along the walkway (Tilemap: small tile IDs in an array). const const GROUND_TILE_SIZE: 16GROUND_TILE_SIZE = 16; const const TILE_GRASS_ID: 1TILE_GRASS_ID = 1; const const TILE_DIRT_ID: 2TILE_DIRT_ID = 2; // The buildings stand on the far edge of the walkway, and the rock walks along it 4 // pixels in from its near edge, so the scene reads front to back: grass, walkway with the // rock, buildings, sky. const const WALKWAY_Y: numberWALKWAY_Y = const GROUND_Y: 188GROUND_Y - const GROUND_TILE_SIZE: 16GROUND_TILE_SIZE; const const WALK_LINE_Y: numberWALK_LINE_Y = const GROUND_Y: 188GROUND_Y - 4; // test.png is 44x44, but its bottom 8 rows are transparent: the rock's lowest visible // pixels are on row 35. Standing the rock on the walk line uses this, not the frame // height, so its feet touch the ground instead of hovering 8 pixels above it. const const HERO_ART_BOTTOM: 36HERO_ART_BOTTOM = 36; // Checker squares inside buildings (Patterns: repeating blocks, no images). const const BUILDING_PATTERN_CELL: 4BUILDING_PATTERN_CELL = 4; // How far the rock moves along x each update tick. const const HERO_SPEED: 1HERO_SPEED = 1; // Walk bob timer: advance the step counter every 8 ticks. const const WALK_FRAME_TICKS: 8WALK_FRAME_TICKS = 8; // Score +1 every 60 ticks (one second at the default 60 ticks per second). const const SCORE_INTERVAL_TICKS: 60SCORE_INTERVAL_TICKS = 60; // A batch of sparkles every 30 ticks. const const PARTICLE_SPAWN_INTERVAL: 30PARTICLE_SPAWN_INTERVAL = 30; // Full day/night loop: 1200 ticks, 20 seconds at 60 ticks per second. const const DAY_NIGHT_CYCLE_TICKS: 1200DAY_NIGHT_CYCLE_TICKS = 1200; // Camera smoothing: each tick the camera steps 14 percent closer to its target. const const CAMERA_LERP: 0.14CAMERA_LERP = 0.14; // These two numbers come straight out of public/audio/music-intro-loop.loop.json, // generated by scripts/generate-audio-loops.mjs. Music plays the same track in // isolation. const const MUSIC_LOOP_START_SECONDS: 1.5MUSIC_LOOP_START_SECONDS = 1.5; const const MUSIC_LOOP_END_SECONDS: 7.9MUSIC_LOOP_END_SECONDS = 7.9; // Parallax: clouds move at 22 percent of the real camera speed. const const SKY_PARALLAX: 0.22SKY_PARALLAX = 0.22; // How many sky bands cover the sky from y = 0 to GROUND_Y. const const SKY_BANDS: 20SKY_BANDS = 20; // Size of the rotating pool of particle palette slots. const const MAX_PARTICLES: 20MAX_PARTICLES = 20; // Static scene slot (never changes after init). UI text and panel colors come from the // shared UI kit theme (slots 240 and up); the slots below belong to the scene itself. const const C_BLACK: 2C_BLACK = 2; // black for BT.clear // Dynamic world slots, rewritten every tick in update(). Sky bands: 10..10+SKY_BANDS-1 // (20 slots). const const C_SKY_BASE: 10C_SKY_BASE = 10; // Ground: 30..31, and the walkway's dirt tiles at 47. const const C_GRASS: 30C_GRASS = 30; const const C_DIRTLINE: 31C_DIRTLINE = 31; const const C_DIRT: 47C_DIRT = 47; // Buildings: 4 buildings x 2 slots (fill + outline) = 8 slots at 32..39. const const C_BUILDING_BASE: 32C_BUILDING_BASE = 32; // Clouds: one slot at 40. const const C_CLOUD: 40C_CLOUD = 40; // Overlay text and timing-chart colors: 41..44, dimmed with the scene at night. const const C_HUD_TITLE: 41C_HUD_TITLE = 41; // timing-chart error markers only const const C_HUD_SCORE: 42C_HUD_SCORE = 42; const const C_HUD_POS: 43C_HUD_POS = 43; const const C_HUD_PHASE: 44C_HUD_PHASE = 44; // colors the day-phase overlay row (Day / Night / ...) // Hero shadow: 45. const const C_HERO_SHADOW: 45C_HERO_SHADOW = 45; // Overlay bar fill (text slots reuse C_HUD_SCORE / C_HUD_POS / C_HUD_PHASE). const const C_OVERLAY_BAR: 46C_OVERLAY_BAR = 46; // Particle slots: 50..69 (MAX_PARTICLES = 20). const const PARTICLE_SLOT_START: 50PARTICLE_SLOT_START = 50; // Sprite base colors extracted from test.png: 70..70+N-1. The ambient (lit) version of // the same colors: 70+N..70+2N-1. const const SPRITE_BASE: 70SPRITE_BASE = 70; // One self-running mini scene: walking rock, following camera, overlay rows, day/night, // sparkles. All color computation happens in update(); render() uses palette indices // only. class class DemoDemo { // The palette holds every color this demo uses. /** @type {Palette | null} */ Demo.palette: Palette | null
@type{Palette | null}
palette
= null;
// Sprite sheet for the rock hero, loaded from /sprites/test.png. /** @type {SpriteSheet | null} */ Demo.heroSheet: SpriteSheet | null
@type{SpriteSheet | null}
heroSheet
= null;
// Looping background music with a distinct intro section. /** @type {AudioClip | null} */ Demo.musicClip: AudioClip | null
@type{AudioClip | null}
musicClip
= null;
// Short chime played on every day/night phase change. /** @type {AudioClip | null} */ Demo.dayPhaseChimeClip: AudioClip | null
@type{AudioClip | null}
dayPhaseChimeClip
= null;
// Confirmation blip played after a successful PNG capture. /** @type {AudioClip | null} */ Demo.captureBlipClip: AudioClip | null
@type{AudioClip | null}
captureBlipClip
= null;
// Day phase label as of the previous tick, used to detect changes. /** @type {string | null} */ Demo.lastDayPhaseLabel: string | null
@type{string | null}
lastDayPhaseLabel
= null;
// The full source rectangle for the hero sprite. /** @type {Rect2i | null} */ Demo.heroSprite: Rect2i | null
@type{Rect2i | null}
heroSprite
= null;
// Hero sprite size in world pixels, read from the loaded sheet in init() (test.png is // 44x44). Deriving it from the real image instead of a guessed number keeps movement // bounds, camera centering, and particle spawns matching what is drawn. /** @type {Vector2i} */ Demo.heroSize: Vector2i
@type{Vector2i}
heroSize
= 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);
// How many unique colors the sprite has (N). Demo.spriteColorCount: numberspriteColorCount = 0; // Original Color32 objects for the sprite's colors, for the ambient multiplication. Demo.spriteBaseColors: {}spriteBaseColors = []; // Rock position in world pixels. The y here is a placeholder: init() sets the real y // once the sprite has loaded and its height is known, so the rock stands exactly on // the ground line. Demo.heroPos: Vector2iheroPos = 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
(120, 0);
// Rock position at the start of the most recent update() tick, before this tick's // walk step moved it. render() blends between heroPrevPos and heroPos using // BT.renderAlpha so the rock glides between ticks instead of jumping (Game Loop // guide, "Smoothing motion between updates"). init() snaps this to heroPos so the // very first frame does not blend in from a stale position. Demo.heroPrevPos: Vector2iheroPrevPos = 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
(120, 0);
// +1 = moving right, -1 = moving left. Demo.heroFacing: numberheroFacing = 1; // Walk step counter: not a frame index, it only bobs the rock one pixel. Demo.walkStep: numberwalkStep = 0; Demo.walkFrameTimer: TimerwalkFrameTimer = new new Timer(intervalTicks: number): Timer
Creates a timer that fires once per fixed-tick interval.
@paramintervalTicks - Number of ticks required between firings; must be a positive integer.
Timer
(const WALK_FRAME_TICKS: 8WALK_FRAME_TICKS);
// Camera top-left in world coordinates. Demo.cameraPos: Vector2icameraPos = 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);
// Camera position at the start of the most recent update() tick, before this tick's // follow-lerp moved it. render() blends between this and cameraPos using // BT.renderAlpha for the same reason as heroPrevPos above. Demo.cameraPrevPos: Vector2icameraPrevPos = 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);
// Reused every render() call for the interpolated camera and hero positions, so no // new Vector2i is allocated per frame. Demo.cameraRenderPos: Vector2icameraRenderPos = 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.heroRenderPos: Vector2iheroRenderPos = 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);
// Float version of the camera x for a smooth lerp without pixel jitter. Demo.cameraXFloat: numbercameraXFloat = 0; // Simple score counter and its timer. Demo.score: numberscore = 0; Demo.scoreTimer: TimerscoreTimer = new new Timer(intervalTicks: number): Timer
Creates a timer that fires once per fixed-tick interval.
@paramintervalTicks - Number of ticks required between firings; must be a positive integer.
Timer
(const SCORE_INTERVAL_TICKS: 60SCORE_INTERVAL_TICKS);
// Particle spawn timer. Demo.particleSpawnTimer: TimerparticleSpawnTimer = new new Timer(intervalTicks: number): Timer
Creates a timer that fires once per fixed-tick interval.
@paramintervalTicks - Number of ticks required between firings; must be a positive integer.
Timer
(const PARTICLE_SPAWN_INTERVAL: 30PARTICLE_SPAWN_INTERVAL);
// Active particle objects: { pos, spawnTick, paletteSlot }. Demo.particles: {}particles = []; // Rotating pool index for particle palette slots. Demo.nextParticleSlot: numbernextParticleSlot = 0; // World decoration: buildings and clouds, built once in init(). Demo.buildings: {}buildings = []; Demo.clouds: {}clouds = []; // Tile IDs for the walkway row (Tilemap): each entry is TILE_GRASS_ID or // TILE_DIRT_ID. Demo.groundTileIds: {}groundTileIds = []; // PNG capture state (Image Output): the Capstone panel's Save button (click, tap, or // Space) triggers BT.downloadFrame() once per press. Demo.capturing: booleancapturing = false; Demo.lastCaptureMessage: stringlastCaptureMessage = ''; Demo.messageTimer: numbermessageTimer = 0; // Base colors for buildings, clouds, and ground, multiplied by the ambient in // update(). Demo.buildingFills: {}buildingFills = []; Demo.buildingOutlines: {}buildingOutlines = []; Demo.cloudBaseColor: Color32cloudBaseColor = 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
(230, 240, 255, 200);
Demo.grassBaseColor: Color32grassBaseColor = 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
(50, 140, 70);
Demo.dirtBaseColor: Color32dirtBaseColor = 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
(40, 100, 55);
Demo.walkwayDirtBaseColor: Color32walkwayDirtBaseColor = 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
(130, 100, 70);
// Reused rectangle and vectors to avoid creating new objects every frame. 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);
Demo.tempVec: Vector2itempVec = 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.worldSize: Vector2iworldSize = 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
(const WORLD_W: 640WORLD_W, const WORLD_H: 240WORLD_H); // pre-allocated for cameraClamp calls
// Sky gradient: base colors at the top and at the horizon. Demo.skyTop: Color32skyTop = 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
(40, 70, 140);
Demo.skyHorizon: Color32skyHorizon = 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
(120, 170, 220);
// Overlay text base colors, before the ambient is applied. Demo.hudTitleBase: Color32hudTitleBase = 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, 230, 180);
Demo.hudScoreBase: Color32hudScoreBase = 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, 220, 255);
Demo.hudPosBase: Color32hudPosBase = 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
(180, 200, 180);
Demo.hudFpsBase: Color32hudFpsBase = 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, 150, 160);
// Reused every frame for the overlay rows: score, rock position, day phase. Demo.overlayRowData: {}overlayRowData = [ { leftText: stringleftText: 'Score 0', textPaletteIndex: numbertextPaletteIndex: const C_HUD_SCORE: 42C_HUD_SCORE }, { leftText: stringleftText: 'Rock (0, 0)', textPaletteIndex: numbertextPaletteIndex: const C_HUD_POS: 43C_HUD_POS }, { leftText: stringleftText: 'Dawn/Day', textPaletteIndex: numbertextPaletteIndex: const C_HUD_PHASE: 44C_HUD_PHASE }, ]; // Palette slots for the engine overlay bars (the FPS strip keeps the engine // defaults). The live palette grid at the bottom of the overlay shows which slots // this frame's draw calls use, which is handy for the day/night tinting and the // sprite palette blocks: 32 swatches per row, 3 visible rows, scroll to browse all // 256 slots.
Demo.configure(): {
    isOverlayToggleHintVisible: boolean;
    isCapturingKeyboardScroll: boolean;
    isOverlayPaletteEnabled: boolean;
    overlayPaletteColumns: number;
    overlayPaletteRowsVisible: number;
    overlayStyle: {
        barPaletteIndex: number;
        textPaletteIndex: number;
        gapPaletteIndex: number;
    };
    isOverlayTimingChartEnabled: boolean;
    overlayTimingChartStyle: {
        updateBarPaletteIndex: number;
        renderBarPaletteIndex: number;
        warningPaletteIndex: number;
        errorPaletteIndex: number;
        tagPaletteIndex: number;
    };
}
configure
() {
return { // The engine normally draws a small toggle hint in the bottom-left corner so // people know they can press Backquote (`) to open the stats overlay. This is // an immersive game scene, so the hint is hidden to keep the picture clean. // The overlay still works: ` reveals the full dev HUD (timing chart and // palette grid) on demand, ` again hides it. Teaching demos leave the hint // visible (the default) so newcomers can find it. isOverlayToggleHintVisible: booleanisOverlayToggleHintVisible: false, // Space is the Save PNG shortcut; keep the page from scrolling when it is // pressed. isCapturingKeyboardScroll: booleanisCapturingKeyboardScroll: true, isOverlayPaletteEnabled: booleanisOverlayPaletteEnabled: true, overlayPaletteColumns: numberoverlayPaletteColumns: 32, overlayPaletteRowsVisible: numberoverlayPaletteRowsVisible: 3,
overlayStyle: {
    barPaletteIndex: number;
    textPaletteIndex: number;
    gapPaletteIndex: number;
}
overlayStyle
: {
barPaletteIndex: numberbarPaletteIndex: const C_OVERLAY_BAR: 46C_OVERLAY_BAR, textPaletteIndex: numbertextPaletteIndex: const C_HUD_SCORE: 42C_HUD_SCORE, gapPaletteIndex: numbergapPaletteIndex: const C_OVERLAY_BAR: 46C_OVERLAY_BAR, }, isOverlayTimingChartEnabled: booleanisOverlayTimingChartEnabled: true,
overlayTimingChartStyle: {
    updateBarPaletteIndex: number;
    renderBarPaletteIndex: number;
    warningPaletteIndex: number;
    errorPaletteIndex: number;
    tagPaletteIndex: number;
}
overlayTimingChartStyle
: {
updateBarPaletteIndex: numberupdateBarPaletteIndex: const C_HUD_POS: 43C_HUD_POS, renderBarPaletteIndex: numberrenderBarPaletteIndex: const C_HUD_SCORE: 42C_HUD_SCORE, warningPaletteIndex: numberwarningPaletteIndex: const C_HUD_PHASE: 44C_HUD_PHASE, errorPaletteIndex: numbererrorPaletteIndex: const C_HUD_TITLE: 41C_HUD_TITLE, tagPaletteIndex: numbertagPaletteIndex: const C_HUD_POS: 43C_HUD_POS, }, }; } // Loads the hero sprite sheet, builds the palette, places buildings and clouds, and // starts the audio. async Demo.init(): unknowninit() { console.log('[GameSceneDemo] Initializing...'); // Create the palette and set the static slots. The shared UI kit needs its // twelve theme colors in the palette before any widget draws. applyTheme() writes // them into high slots (240 and up), far above this demo's scene slots, which end // at SPRITE_BASE + 2N - 1 (below slot 100). The returned slot map is not needed // here, so the call is side effect only. 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;
    ... 124 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);
import applyThemeapplyTheme(this.Demo.palette: Palette
@type{Palette | null}
palette
);
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_BLACK: 2C_BLACK, 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
(0, 0, 0));
// Overlay row backgrounds. 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_OVERLAY_BAR: 46C_OVERLAY_BAR, 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
(0, 0, 0, 180));
// Pre-fill the particle slots as transparent. for (let let i: numberi = 0; let i: numberi < const MAX_PARTICLES: 20MAX_PARTICLES; let i: numberi++) { 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 PARTICLE_SLOT_START: 50PARTICLE_SLOT_START + let i: numberi, 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
(0, 0, 0, 0));
} // Build the world decoration and the walkway tile-ID row (Tilemap). this.Demo.buildWorldDecor(): voidbuildWorldDecor(); this.Demo.buildGroundTileStrip(): voidbuildGroundTileStrip(); // Extract the sprite colors and register them in the palette: the engine scans // the PNG and adds every unique color it finds, starting at SPRITE_BASE. The // returned array holds the same colors in palette-write order (sorted darkest // first by luminance). They are kept so updateWorldPalette() can multiply each // base color by the current ambient tint and write the lit version into the // higher "ambient block" (SPRITE_BASE+N..SPRITE_BASE+2N-1). this.Demo.spriteBaseColors: {}spriteBaseColors = await class SpriteSheet
Sprite-sheet wrapper around a loaded image asset. The class keeps the original image available for CPU-side inspection while lazily creating and caching a GPU texture for rendering. When possible, `load()` also pre-decodes the source into an `ImageBitmap` so texture uploads preserve pixel-art alpha and color values more reliably. After calling `indexize()`, the sheet stores palette indices rather than RGBA data. The GPU texture becomes an `r8uint` format uploaded via `writeTexture`. The original RGBA bytes are retained so `reindexize()` can re-convert without reloading the image.
@since0.1.0
SpriteSheet
.
SpriteSheet.loadColorsIntoPalette(url: string, palette: Palette, startSlot: number, options?: {
    sort?: "luminance" | "none";
}): Promise<Color32[]>
Walks a PNG's pixels and registers every unique opaque color into the supplied palette starting at `startSlot`. Pixels with alpha 0 are skipped - they map to the engine's transparent sentinel slot 0 at draw time. Opaque pixels are deduplicated on RGB and stored with alpha forced to 255, matching the lookup performed by `indexize()` so a subsequent `sheet.indexize(palette)` call resolves without throwing on missing colors. By default colors are sorted darkest-first by perceived luminance ( {@link Color32.luminance } ); pass `{ sort: 'none' }` to keep the row-major scan order of the source image. Image loading goes through {@link AssetLoader.loadImage } , so the call shares cache and in-flight deduplication with {@link SpriteSheet.load } . The destination range is validated before any write, so the palette is never left partially mutated: if the collected colors would not fit (`startSlot < 1` or `startSlot + count > palette.size`), the method throws without touching any slot.
@paramurl - Path or URL to the PNG file.@parampalette - Target palette to populate.@paramstartSlot - First palette slot to write into.@paramoptions - Optional configuration.@paramoptions.sort - Color ordering. Defaults to `'luminance'`.@returnsRegistered colors in palette-write order.@throwsError if the image cannot be loaded.@throwsRangeError if the discovered colors do not fit in the palette starting at `startSlot`.
loadColorsIntoPalette
('/sprites/test.png', this.Demo.palette: Palette
@type{Palette | null}
palette
, const SPRITE_BASE: 70SPRITE_BASE);
const const colorCount: anycolorCount = this.Demo.spriteBaseColors: {}spriteBaseColors.length; this.Demo.spriteColorCount: numberspriteColorCount = const colorCount: anycolorCount; // Pre-fill the ambient sprite block (SPRITE_BASE+N..SPRITE_BASE+2N-1). update() // recalculates these every tick from the current ambient light. for (let let i: numberi = 0; let i: numberi < const colorCount: anycolorCount; let i: numberi++) { 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 SPRITE_BASE: 70SPRITE_BASE + const colorCount: anycolorCount + let i: numberi, this.Demo.spriteBaseColors: {}spriteBaseColors[let i: numberi]);
} // Load the hero sprite against the slots registered above. const const indexed: Promise<IndexedSpriteLoadResult>indexed = await class SpriteSheet
Sprite-sheet wrapper around a loaded image asset. The class keeps the original image available for CPU-side inspection while lazily creating and caching a GPU texture for rendering. When possible, `load()` also pre-decodes the source into an `ImageBitmap` so texture uploads preserve pixel-art alpha and color values more reliably. After calling `indexize()`, the sheet stores palette indices rather than RGBA data. The GPU texture becomes an `r8uint` format uploaded via `writeTexture`. The original RGBA bytes are retained so `reindexize()` can re-convert without reloading the image.
@since0.1.0
SpriteSheet
.
SpriteSheet.loadIndexed(url: string, palette: Palette, startSlot: number, options?: {
    sort?: "luminance" | "none";
    tileSize?: number | Vector2i;
}): Promise<IndexedSpriteLoadResult>
Convenience one-call path for palette-indexed sprite setup. This combines: 1) {@link SpriteSheet.loadColorsIntoPalette } 2) {@link SpriteSheet.load } 3) {@link SpriteSheet.indexize } It returns the indexized sheet plus a full-frame source rectangle and the colors that were written into the palette. Callers still control when to activate the palette via `BT.paletteSet(palette)`.
@paramurl - Path or URL to the PNG file.@parampalette - Target palette used for both registration and indexization.@paramstartSlot - First palette slot to write discovered colors into.@changed1.8.0 Added `options.tileSize`.@paramoptions - Optional color-sort behavior for registration and tile grid size.@paramoptions.sort - Color ordering for palette registration.@paramoptions.tileSize - Tile grid cell size (see {@link SpriteSheet.tileSize}).@returnsObject with `sheet`, `srcRect`, and registered `colors`.@throwsRangeError if `options.tileSize` is not a positive integer on both axes.
loadIndexed
('/sprites/test.png', this.Demo.palette: Palette
@type{Palette | null}
palette
, const SPRITE_BASE: 70SPRITE_BASE, {
sort?: "luminance" | "none" | undefinedsort: 'none', }); this.Demo.heroSheet: SpriteSheet | null
@type{SpriteSheet | null}
heroSheet
= const indexed: Promise<IndexedSpriteLoadResult>indexed.sheet;
this.Demo.heroSprite: Rect2i | null
@type{Rect2i | null}
heroSprite
= this.Demo.heroSheet: SpriteSheet | null
@type{SpriteSheet | null}
heroSheet
.SpriteSheet.fullRect(): Rect2i
Returns a source rectangle that covers the entire sprite sheet.
@returnsFull-sheet source rectangle.
fullRect
();
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) => 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
);
console.log(`[GameSceneDemo] Loaded sprite: ${this.Demo.heroSprite: Rect2i
@type{Rect2i | null}
heroSprite
.Rect2i.width: number
Width in pixels (defaults to 0).
width
}x${this.Demo.heroSprite: Rect2i
@type{Rect2i | null}
heroSprite
.Rect2i.height: number
Height in pixels (defaults to 0).
height
}px`);
// Read the hero's real size from the loaded sheet (44x44 for test.png), the same // way Basics Enhanced and Logo Low-Res do. Every bit of math below - movement // bounds, camera centering, particle spawns - uses this size, so the logic always // matches the picture on screen. this.Demo.heroSize: Vector2i
@type{Vector2i}
heroSize
.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
(this.Demo.heroSheet: SpriteSheet | null
@type{SpriteSheet | null}
heroSheet
.SpriteSheet.size: Vector2i
Gets the sprite-sheet dimensions in pixels.
@returnsSheet dimensions. Changes after a hot-replace image swap with different dimensions - any `srcRect` a demo holds onto is the demo's own responsibility to reconcile.
size
.Vector2i.x: number
Horizontal component (defaults to 0).
x
, this.Demo.heroSheet: SpriteSheet | null
@type{SpriteSheet | null}
heroSheet
.SpriteSheet.size: Vector2i
Gets the sprite-sheet dimensions in pixels.
@returnsSheet dimensions. Changes after a hot-replace image swap with different dimensions - any `srcRect` a demo holds onto is the demo's own responsibility to reconcile.
size
.Vector2i.y: number
Vertical component (defaults to 0).
y
);
// Stand the rock on the walk line: the sprite's top-left y is the walk line minus // the height of its visible pixels (HERO_ART_BOTTOM), so its lowest pixels sit // right on WALK_LINE_Y. this.Demo.heroPos: Vector2iheroPos.Vector2i.y: number
Vertical component (defaults to 0).
y
= const WALK_LINE_Y: numberWALK_LINE_Y - const HERO_ART_BOTTOM: 36HERO_ART_BOTTOM;
// Snap heroPrevPos to match so the very first render does not blend in from the // placeholder position the class fields started with. this.Demo.heroPrevPos: Vector2iheroPrevPos.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
(this.Demo.heroPos: Vector2iheroPos.Vector2i.x: number
Horizontal component (defaults to 0).
x
, this.Demo.heroPos: Vector2iheroPos.Vector2i.y: number
Vertical component (defaults to 0).
y
);
// Place the camera on the hero to start. this.Demo.cameraXFloat: numbercameraXFloat = this.Demo.heroPos: Vector2iheroPos.Vector2i.x: number
Horizontal component (defaults to 0).
x
- const DISPLAY_W: 320DISPLAY_W / 2 + this.Demo.heroSize: Vector2i
@type{Vector2i}
heroSize
.Vector2i.x: number
Horizontal component (defaults to 0).
x
/ 2;
this.Demo.cameraPos: Vector2icameraPos.Vector2i.x: number
Horizontal component (defaults to 0).
x
= Math.floor(this.Demo.cameraXFloat: numbercameraXFloat);
this.Demo.cameraPos: Vector2icameraPos.Vector2i.y: number
Vertical component (defaults to 0).
y
= 0;
this.Demo.clampCamera(): voidclampCamera(); // Snap cameraPrevPos to match the starting camera position so the very first // render does not blend in from (0, 0). this.Demo.cameraPrevPos: Vector2icameraPrevPos.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
(this.Demo.cameraPos: Vector2icameraPos.Vector2i.x: number
Horizontal component (defaults to 0).
x
, this.Demo.cameraPos: Vector2icameraPos.Vector2i.y: number
Vertical component (defaults to 0).
y
);
// Write every ambient-lit color once now. update() rewrites them each tick, but // the first frame can be drawn before the first update() runs, and without this // it would show a black world. this.Demo.updateWorldPalette(): voidupdateWorldPalette(); // Load and start all sound: music, the day/night chime, and the capture blip. await this.Demo.initAudio(): anyinitAudio(); console.log('[GameSceneDemo] Ready.'); return true; } // Loads the music track, synthesizes the two sound effects, and starts the music. // Called once from init(); split out so the audio setup reads as one step. async Demo.initAudio(): anyinitAudio() { // Background music: a real intro-then-loop track, the same one Music plays in // isolation. BT.musicPlay() called before the page is unlocked is remembered and // starts for real the instant the player clicks or presses a key. // // Load and play are wrapped like in Snake Game: a missing or undecodable file // must not abort the whole scene - the capstone still renders with SFX only. try { this.Demo.musicClip: AudioClip | null
@type{AudioClip | null}
musicClip
= await class AudioClip
Decoded audio asset with its winning source URL and buffer-derived metadata. Construct instances with {@link AudioClip.load } , {@link AudioClip.loadAll } , or {@link AudioClip.synth } ; there is no public constructor.
@since1.3.0
AudioClip
.AudioClip.load(url: string | string[], options?: AudioClipLoadOptions): Promise<AudioClip>
Loads an audio clip from a single URL, or from an ordered list of candidate URLs. A single URL runs the download+decode pipeline directly, sharing the per-URL cache and in-flight dedup described on {@link AudioClip } . A URL array tries each candidate in order and resolves with the first one that downloads and decodes successfully - useful for offering a browser-friendly fallback (for example `['music.ogg', 'music.mp3']`) when a container or codec isn't universally supported. If every candidate fails, the error from the last candidate is thrown.
@paramurl - Single audio URL, or an ordered list of fallback URLs.@paramoptions - Optional load options (progress reporting).@returnsLoaded clip, cached under its winning source URL.@throwsError if the URL (or every URL in the list) fails to load or decode.
load
('/audio/music-intro-loop.wav');
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
.musicPlay: (clip: AudioClip, options?: MusicPlayOptions) => void
Plays a loaded audio clip through the music player, crossfading out whatever is currently playing. Silently does nothing when the clip hasn't finished loading yet (or was already unloaded with `clip.unload()`), or before the engine has initialized. While the audio context is still locked (before the first unlock gesture), the request is remembered instead of dropped - it starts automatically the instant the context unlocks, unlike {@link BT.soundPlay } .
@since1.3.0@paramclip - Loaded audio clip to play.@paramoptions - Crossfade, volume, and loop options; see {@link MusicPlayOptions}.
musicPlay
(this.Demo.musicClip: AudioClip | null
@type{AudioClip | null}
musicClip
, {
MusicPlayOptions.loop?: boolean | undefined
Whether the whole track loops. Ignored when `loopStart`/`loopEnd` are given. Defaults to `true`.
loop
: true,
MusicPlayOptions.loopStart?: number | undefined
Loop region start in seconds. Requires `loopEnd`; see {@link MusicPlayer.play } .
loopStart
: const MUSIC_LOOP_START_SECONDS: 1.5MUSIC_LOOP_START_SECONDS,
MusicPlayOptions.loopEnd?: number | undefined
Loop region end in seconds. Requires `loopStart`; see {@link MusicPlayer.play } .
loopEnd
: const MUSIC_LOOP_END_SECONDS: 7.9MUSIC_LOOP_END_SECONDS,
}); } catch (function (local var) error: unknownerror) { console.warn('[GameSceneDemo] Failed to load background music, continuing without it.', function (local var) error: unknownerror); this.Demo.musicClip: AudioClip | null
@type{AudioClip | null}
musicClip
= null;
} // A soft sine chime for day/night transitions, and BT.synthPreset.blip() (the // same UI blip Synth Toy uses) for a successful capture. this.Demo.dayPhaseChimeClip: AudioClip | null
@type{AudioClip | null}
dayPhaseChimeClip
= await class AudioClip
Decoded audio asset with its winning source URL and buffer-derived metadata. Construct instances with {@link AudioClip.load } , {@link AudioClip.loadAll } , or {@link AudioClip.synth } ; there is no public constructor.
@since1.3.0
AudioClip
.AudioClip.synth(params: SynthParams): Promise<AudioClip>
Synthesizes a clip from deterministic procedural parameters - no source file, no `OfflineAudioContext`, and no audio graph involved. Rendering happens entirely on the CPU via the pure {@link renderSynthSamples } function against an `AudioBuffer` allocated from the registered decode context. The returned clip flows through the same {@link buffer } getter and playback path as a loaded clip, but uses a synthetic, non-cached identifier (`synth:<waveform>`) - it is never added to the URL-keyed resolved cache and never deduplicated, so identical `params` still render a fresh, independent `AudioBuffer` on every call. See {@link SynthParams } for the full parameter set.
@paramparams - Deterministic synthesis parameters.@returnsA new clip wrapping the synthesized buffer.@throwsError if `params` fails validation, or the engine has not registered a decode context yet (see {@link audioClipNotReadyError }).
synth
({
SynthParams.waveform: any
Oscillator waveform shape.
waveform
: 'sine',
SynthParams.frequency: number
Base carrier frequency in Hz at the start of the clip (before any pitch sweep or vibrato).
frequency
: 660,
SynthParams.duration: number
Total clip duration in seconds. Must be greater than 0 and no more than {@link MAX_SYNTH_DURATION_SECONDS } .
duration
: 0.6,
SynthParams.volume?: number | undefined
Overall output amplitude in [0, 1] (unclamped on the high end; final output is always clamped to avoid clipping). Defaults to {@link DEFAULT_VOLUME } .
volume
: 0.5,
SynthParams.envelope?: SynthEnvelope | undefined
Optional attack/decay/sustain/release envelope. Defaults to a full ADSR envelope; see {@link SynthEnvelope } .
envelope
: { SynthEnvelope.attack?: number | undefined
Time in seconds to ramp from silence to full amplitude. Defaults to {@link DEFAULT_ATTACK } .
attack
: 0.02, SynthEnvelope.decay?: number | undefined
Time in seconds to fall from full amplitude to the `sustain` level. Defaults to {@link DEFAULT_DECAY } .
decay
: 0.15, SynthEnvelope.sustain?: number | undefined
Gain level in [0, 1] held between the decay and release phases. Defaults to {@link DEFAULT_SUSTAIN } .
sustain
: 0.3, SynthEnvelope.release?: number | undefined
Time in seconds to fall from the sustain level to silence at the end of the clip. Defaults to {@link DEFAULT_RELEASE } .
release
: 0.4 },
SynthParams.seed: number
Seed for the deterministic PRNG driving noise generation - identical seeds render identical output.
seed
: 1,
}); this.Demo.captureBlipClip: AudioClip | null
@type{AudioClip | null}
captureBlipClip
= await class AudioClip
Decoded audio asset with its winning source URL and buffer-derived metadata. Construct instances with {@link AudioClip.load } , {@link AudioClip.loadAll } , or {@link AudioClip.synth } ; there is no public constructor.
@since1.3.0
AudioClip
.AudioClip.synth(params: SynthParams): Promise<AudioClip>
Synthesizes a clip from deterministic procedural parameters - no source file, no `OfflineAudioContext`, and no audio graph involved. Rendering happens entirely on the CPU via the pure {@link renderSynthSamples } function against an `AudioBuffer` allocated from the registered decode context. The returned clip flows through the same {@link buffer } getter and playback path as a loaded clip, but uses a synthetic, non-cached identifier (`synth:<waveform>`) - it is never added to the URL-keyed resolved cache and never deduplicated, so identical `params` still render a fresh, independent `AudioBuffer` on every call. See {@link SynthParams } for the full parameter set.
@paramparams - Deterministic synthesis parameters.@returnsA new clip wrapping the synthesized buffer.@throwsError if `params` fails validation, or the engine has not registered a decode context yet (see {@link audioClipNotReadyError }).
synth
(
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
.
synthPreset: {
    jump: (seed?: number) => SynthParams;
    pickup: (seed?: number) => SynthParams;
    explosion: (seed?: number) => SynthParams;
    laser: (seed?: number) => SynthParams;
    hit: (seed?: number) => SynthParams;
    blip: (seed?: number) => SynthParams;
}
Pre-configured `SynthParams` presets for common sound effects ("jump", "pickup", "explosion", "laser", "hit", "blip"). Each function returns a fresh {@link SynthParams } object; pass it to {@link AudioClip.synth } to render a clip, then play the result via {@link BT.soundPlay } . An optional `seed` argument applies small, bounded, deterministic jitter to a few hand-picked fields per preset, so repeated plays vary without losing reproducibility - the same seed always renders the exact same variant.
@since1.3.0@exampleconst jumpClip = await AudioClip.synth(BT.synthPreset.jump()); BT.soundPlay(jumpClip);
synthPreset
.blip: (seed?: number) => SynthParams
UI blip / menu select: a very short, clean sine tone. `seed` jitters only the base frequency, and only slightly (+/-3%) - UI feedback should stay recognizably consistent rather than vary as much as a gameplay sound effect. Omit `seed` for a fixed baseline variant.
@paramseed - Seed for deterministic jitter. Defaults to {@link DEFAULT_PRESET_SEED }.@returnsA fresh `SynthParams` for a UI blip sound effect.
blip
());
// Remember the starting day phase so updateDayPhaseSound() only chimes when the // phase actually changes, not on the very first tick. this.Demo.lastDayPhaseLabel: string | null
@type{string | null}
lastDayPhaseLabel
= this.Demo.getDayPhaseLabel(tick?: number): "Day" | "Toward dusk" | "Night" | "Toward dawn"getDayPhaseLabel();
} // Fixed-step logic: moves the rock, camera, score, and particles, then recalculates // all ambient-lit palette colors. Demo.update(): voidupdate() { // Let the shared UI kit latch keyboard shortcuts and touch contacts for this // tick. This must run before anything else so the Save button's Space binding // works. import uiui.tick(); const const tick: numbertick =
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.ticks: number
Current fixed-update tick counter. Increments once per engine update. Reset via {@link BT.ticksReset } .
@since1.0.4@returnsCurrent tick count since initialization or last reset.
ticks
;
this.Demo.updateDayPhaseSound(tick: any): voidupdateDayPhaseSound(const tick: numbertick); // The PNG capture itself starts from the kit's Save button in renderHud(). // Here only the "Saved: ..." message is counted down so it disappears after a // while. if (this.Demo.messageTimer: numbermessageTimer > 0) { this.Demo.messageTimer: numbermessageTimer--; } this.Demo.updateHeroMovement(): voidupdateHeroMovement(); this.Demo.updateWalkStep(tick: any): voidupdateWalkStep(const tick: numbertick); this.Demo.updateCameraFollow(): voidupdateCameraFollow(); this.Demo.updateScore(tick: any): voidupdateScore(const tick: numbertick); this.Demo.updateParticlesSpawn(tick: any): voidupdateParticlesSpawn(const tick: numbertick); this.Demo.cleanupParticles(tick: any): voidcleanupParticles(const tick: numbertick); this.Demo.updateParticleColors(tick: any): voidupdateParticleColors(const tick: numbertick); // Recalculate all ambient-lit palette colors: the core of the day/night system. this.Demo.updateWorldPalette(): voidupdateWorldPalette(); } // Plays a short chime the instant the day/night phase label changes (Day -> Toward // dusk -> Night -> Toward dawn -> Day ...). Comparing this tick's label against last // tick's label is the same edge-detection idea Keyboard Input uses for key presses: // only the moment something changes matters, not every tick it stays the same. Demo.updateDayPhaseSound(tick: any): voidupdateDayPhaseSound(tick: anytick) { const const currentPhase: "Day" | "Toward dusk" | "Night" | "Toward dawn"currentPhase = this.Demo.getDayPhaseLabel(tick?: number): "Day" | "Toward dusk" | "Night" | "Toward dawn"getDayPhaseLabel(tick: anytick); if (this.Demo.lastDayPhaseLabel: string | null
@type{string | null}
lastDayPhaseLabel
!== null && const currentPhase: "Day" | "Toward dusk" | "Night" | "Toward dawn"currentPhase !== this.Demo.lastDayPhaseLabel: string
@type{string | null}
lastDayPhaseLabel
) {
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
.soundPlay: (clip: AudioClip, options?: SoundPlayOptions) => SoundRef
Plays a loaded audio clip through the SFX voice pool. Returns an inert {@link SoundRef } without allocating a voice when the clip hasn't finished loading yet (or was already unloaded), when the pool has no free or stealable voice at this priority, or before the engine has unlocked audio playback.
@since1.3.0@paramclip - Loaded audio clip to play.@paramoptions - Playback options.@returnsA handle identifying the new voice; pass it to {@link BT.soundStop} and the other per-sound controls. Safe to use even when playback was silently dropped - every accessor on an inert handle is a no-op.
soundPlay
(this.Demo.dayPhaseChimeClip: AudioClip | null
@type{AudioClip | null}
dayPhaseChimeClip
);
} this.Demo.lastDayPhaseLabel: string | null
@type{string | null}
lastDayPhaseLabel
= const currentPhase: "Day" | "Toward dusk" | "Night" | "Toward dawn"currentPhase;
} // Draws the world layers back to front. Score, position, and phase text are handled // by the engine overlay. Every draw call uses palette index numbers only, no Color32. Demo.render(): voidrender() {
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) => 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_BLACK: 2C_BLACK);
// Blend cameraPrevPos toward cameraPos by BT.renderAlpha - a fraction from 0 (a // tick just finished) to just under 1 (the next tick is about to happen) - so the // camera's on-screen position matches this exact render moment instead of only // its last-tick position. Both the sky parallax and the full camera offset use // this same smoothed value, so the layers never drift apart. this.Demo.cameraRenderPos: Vector2icameraRenderPos.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(this.Demo.cameraPrevPos: Vector2icameraPrevPos.Vector2i.x: number
Horizontal component (defaults to 0).
x
+ (this.Demo.cameraPos: Vector2icameraPos.Vector2i.x: number
Horizontal component (defaults to 0).
x
- this.Demo.cameraPrevPos: Vector2icameraPrevPos.Vector2i.x: number
Horizontal component (defaults to 0).
x
) *
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
.renderAlpha: number
Fractional progress between the last completed fixed update and the next. Intended for interpolating render state between fixed-update steps.
@since1.3.0@returnsInterpolation alpha in `[0, 1)`.
renderAlpha
),
Math.floor(this.Demo.cameraPrevPos: Vector2icameraPrevPos.Vector2i.y: number
Vertical component (defaults to 0).
y
+ (this.Demo.cameraPos: Vector2icameraPos.Vector2i.y: number
Vertical component (defaults to 0).
y
- this.Demo.cameraPrevPos: Vector2icameraPrevPos.Vector2i.y: number
Vertical component (defaults to 0).
y
) *
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
.renderAlpha: number
Fractional progress between the last completed fixed update and the next. Intended for interpolating render state between fixed-update steps.
@since1.3.0@returnsInterpolation alpha in `[0, 1)`.
renderAlpha
),
); // Layer 1: sky and clouds with parallax. this.Demo.renderSkyLayer(): voidrenderSkyLayer(); // Layer 2: world with the full camera offset.
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
.cameraSet: (offset: Vector2i) => void
Sets the global camera offset applied to subsequent draw calls.
@since0.1.0@paramoffset - Camera translation in display pixels.
cameraSet
(this.Demo.cameraRenderPos: Vector2icameraRenderPos);
this.Demo.renderGroundAndBuildings(): voidrenderGroundAndBuildings(); this.Demo.renderParticles(): voidrenderParticles(); this.Demo.renderHero(): voidrenderHero();
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
.cameraReset: () => void
Resets the global camera offset to `(0, 0)`.
@since0.1.0@changed1.7.1 Now persists once nothing sets the camera again: previously, the offset the engine re-applies at frame start stayed at its pre-reset value forever, so a later frame that never calls `cameraSet` again still saw the old offset leak back in.
cameraReset
();
// HUD panels and capture controls, in screen space (not scrolled with the // world). this.Demo.renderHud(): voidrenderHud(); // Score, rock position, and day phase are drawn in overlayRows() above the FPS // bar. } // Score, rock position, and day/night phase for the engine overlay. The text colors // are updated each tick in updateWorldPalette() so they dim at night. Demo.overlayRows(): {}overlayRows() { this.Demo.overlayRowData: {}overlayRowData[0].leftText = `Score ${this.Demo.score: numberscore}`; this.Demo.overlayRowData: {}overlayRowData[1].leftText = `Rock (${this.Demo.heroPos: Vector2iheroPos.Vector2i.x: number
Horizontal component (defaults to 0).
x
}, ${this.Demo.heroPos: Vector2iheroPos.Vector2i.y: number
Vertical component (defaults to 0).
y
})`;
this.Demo.overlayRowData: {}overlayRowData[2].leftText = this.Demo.getDayPhaseLabel(tick?: number): "Day" | "Toward dusk" | "Night" | "Toward dawn"getDayPhaseLabel(); return this.Demo.overlayRowData: {}overlayRowData; } // Fills groundTileIds with alternating grass/dirt tile IDs for one 16 px row // (Tilemap). Demo.buildGroundTileStrip(): voidbuildGroundTileStrip() { const const cols: anycols = Math.ceil(const WORLD_W: 640WORLD_W / const GROUND_TILE_SIZE: 16GROUND_TILE_SIZE); this.Demo.groundTileIds: {}groundTileIds = []; for (let let col: numbercol = 0; let col: numbercol < const cols: anycols; let col: numbercol++) { // Simple pattern: two grass tiles, then two dirt tiles, repeat. this.Demo.groundTileIds: {}groundTileIds.push(let col: numbercol % 4 < 2 ? const TILE_GRASS_ID: 1TILE_GRASS_ID : const TILE_DIRT_ID: 2TILE_DIRT_ID); } } // Places buildings and clouds once at startup, and stores the building base colors in // separate arrays so update() can apply the ambient. Demo.buildWorldDecor(): voidbuildWorldDecor() { const const rawBuildings: {}rawBuildings = [ { x: numberx: 40, y: numbery: const WALKWAY_Y: numberWALKWAY_Y - 52, w: numberw: 36, h: numberh: 52, fill: Color32fill: 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
(140, 90, 70),
outline: Color32outline: 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
(80, 50, 40),
}, { x: numberx: 200, y: numbery: const WALKWAY_Y: numberWALKWAY_Y - 70, w: numberw: 44, h: numberh: 70, fill: Color32fill: 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
(110, 120, 140),
outline: Color32outline: 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
(60, 70, 90),
}, { x: numberx: 380, y: numbery: const WALKWAY_Y: numberWALKWAY_Y - 46, w: numberw: 32, h: numberh: 46, fill: Color32fill: 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
(160, 100, 100),
outline: Color32outline: 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, 50, 50),
}, { x: numberx: 500, y: numbery: const WALKWAY_Y: numberWALKWAY_Y - 60, w: numberw: 40, h: numberh: 60, fill: Color32fill: 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
(120, 130, 100),
outline: Color32outline: 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
(70, 80, 60),
}, ]; // Fresh arrays every call, never push() onto the old ones: a second call would // leave 8 buildings whose palette slots spill past slot 39 into the cloud, // overlay, and walkway colors. Array.map returns a new array with one result per // entry. this.Demo.buildings: {}buildings = const rawBuildings: {}rawBuildings.map((b: anyb) => ({ x: anyx: b: anyb.x, y: anyy: b: anyb.y, w: anyw: b: anyb.w, h: anyh: b: anyb.h })); this.Demo.buildingFills: {}buildingFills = const rawBuildings: {}rawBuildings.map((b: anyb) => b: anyb.fill); this.Demo.buildingOutlines: {}buildingOutlines = const rawBuildings: {}rawBuildings.map((b: anyb) => b: anyb.outline); this.Demo.clouds: {}clouds = [ { x: numberx: 30, y: numbery: 24, w: numberw: 42, h: numberh: 14 }, { x: numberx: 160, y: numbery: 40, w: numberw: 56, h: numberh: 18 }, { x: numberx: 320, y: numbery: 18, w: numberw: 48, h: numberh: 16 }, { x: numberx: 480, y: numbery: 36, w: numberw: 50, h: numberh: 15 }, { x: numberx: 600, y: numbery: 22, w: numberw: 44, h: numberh: 17 }, ]; } // Computes the current ambient tint from the day/night cycle and returns it as a // Color32, the "color of the light": bright white at midday, dark blue at midnight. // Called from update() to drive the palette, never from render(). Demo.getAmbientTint(): Color32getAmbientTint() { const const tick: numbertick =
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.ticks: number
Current fixed-update tick counter. Increments once per engine update. Reset via {@link BT.ticksReset } .
@since1.0.4@returnsCurrent tick count since initialization or last reset.
ticks
;
const const cycle: numbercycle = (const tick: numbertick % const DAY_NIGHT_CYCLE_TICKS: 1200DAY_NIGHT_CYCLE_TICKS) / const DAY_NIGHT_CYCLE_TICKS: 1200DAY_NIGHT_CYCLE_TICKS; // Math.cos returns -1..1; (cos + 1) / 2 gives 0..1 for "how bright is the sun". const const dayAmount: numberdayAmount = (Math.cos(const cycle: numbercycle * Math.PI * 2) + 1) * 0.5; // Interpolate from a cool night color to a warm day color. const const r: anyr = Math.floor(70 + const dayAmount: numberdayAmount * 185); const const g: anyg = Math.floor(75 + const dayAmount: numberdayAmount * 180); const const b: anyb = Math.floor(120 + const dayAmount: numberdayAmount * 135); return 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
(const r: anyr, const g: anyg, const b: anyb);
} // Recalculates every ambient-dependent palette entry. Called every tick in update() // so render() always has up-to-date slot values. Demo.updateWorldPalette(): voidupdateWorldPalette() { const const ambient: Color32ambient = this.Demo.getAmbientTint(): Color32getAmbientTint(); // Sky gradient bands. for (let let band: numberband = 0; let band: numberband < const SKY_BANDS: 20SKY_BANDS; let band: numberband++) { // t goes from 0 at the top to 1 near the horizon. const const t: numbert = let band: numberband / const SKY_BANDS: 20SKY_BANDS; // applyEasing(t, 'ease-in-out') squashes the transition so it moves slowly // near the top, speeds up through the middle, then slows again near the // horizon. Real skies work the same way: a deep uniform color at the zenith, // a quick shift through the middle bands, then a flatter wash near the // horizon. lerp(other, t) blends between two Color32 values: t = 0 is all // skyTop, t = 1 is all skyHorizon. const const bandBase: Color32bandBase = this.Demo.skyTop: Color32skyTop.Color32.lerp(other: Color32, t: number): Color32
Linearly interpolates between this color and another. Useful for color transitions and gradients.
@paramother - Target color to blend toward.@paramt - Interpolation factor (0.0 = this color, 1.0 = other color).@returnsNew color blended between this and other.
lerp
(this.Demo.skyHorizon: Color32skyHorizon, function applyEasing(t: number, easing: EasingFunction): number
Applies an easing curve to a normalized time value.
@since1.0.3@changed1.5.0 Added sine, cubic, quartic, quintic, expo, circ, back, elastic, and bounce families.@paramt - Normalized time in [0, 1]. Values outside this range are not clamped.@parameasing - Easing curve to apply.@returnsEased value. Guaranteed to return 0 for `t = 0` and 1 for `t = 1`.
applyEasing
(const t: numbert, 'ease-in-out'));
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_SKY_BASE: 10C_SKY_BASE + let band: numberband, const bandBase: Color32bandBase.Color32.multiply(other: Color32): Color32
Multiplies this color by another color component-wise. Useful for tinting and color modulation.
@paramother - Color to multiply with.@returnsNew color with each channel multiplied and normalized.
multiply
(const ambient: Color32ambient));
} // Ground. 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_GRASS: 30C_GRASS, this.Demo.grassBaseColor: Color32grassBaseColor.Color32.multiply(other: Color32): Color32
Multiplies this color by another color component-wise. Useful for tinting and color modulation.
@paramother - Color to multiply with.@returnsNew color with each channel multiplied and normalized.
multiply
(const ambient: Color32ambient));
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_DIRTLINE: 31C_DIRTLINE, this.Demo.dirtBaseColor: Color32dirtBaseColor.Color32.multiply(other: Color32): Color32
Multiplies this color by another color component-wise. Useful for tinting and color modulation.
@paramother - Color to multiply with.@returnsNew color with each channel multiplied and normalized.
multiply
(const ambient: Color32ambient));
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_DIRT: 47C_DIRT, this.Demo.walkwayDirtBaseColor: Color32walkwayDirtBaseColor.Color32.multiply(other: Color32): Color32
Multiplies this color by another color component-wise. Useful for tinting and color modulation.
@paramother - Color to multiply with.@returnsNew color with each channel multiplied and normalized.
multiply
(const ambient: Color32ambient));
// Buildings. for (let let i: numberi = 0; let i: numberi < this.Demo.buildings: {}buildings.length; let i: numberi++) { 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_BUILDING_BASE: 32C_BUILDING_BASE + let i: numberi * 2, this.Demo.buildingFills: {}buildingFills[let i: numberi].multiply(const ambient: Color32ambient));
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_BUILDING_BASE: 32C_BUILDING_BASE + let i: numberi * 2 + 1, this.Demo.buildingOutlines: {}buildingOutlines[let i: numberi].multiply(const ambient: Color32ambient));
} // Clouds. 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_CLOUD: 40C_CLOUD, this.Demo.cloudBaseColor: Color32cloudBaseColor.Color32.multiply(other: Color32): Color32
Multiplies this color by another color component-wise. Useful for tinting and color modulation.
@paramother - Color to multiply with.@returnsNew color with each channel multiplied and normalized.
multiply
(const ambient: Color32ambient));
// Engine overlay text: a subtle night tint on the score / position / phase rows. // The kit HUD panels keep their fixed theme colors and does not dim at night. 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_HUD_TITLE: 41C_HUD_TITLE, this.Demo.hudTitleBase: Color32hudTitleBase.Color32.multiply(other: Color32): Color32
Multiplies this color by another color component-wise. Useful for tinting and color modulation.
@paramother - Color to multiply with.@returnsNew color with each channel multiplied and normalized.
multiply
(const ambient: Color32ambient));
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_HUD_SCORE: 42C_HUD_SCORE, this.Demo.hudScoreBase: Color32hudScoreBase.Color32.multiply(other: Color32): Color32
Multiplies this color by another color component-wise. Useful for tinting and color modulation.
@paramother - Color to multiply with.@returnsNew color with each channel multiplied and normalized.
multiply
(const ambient: Color32ambient));
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_HUD_POS: 43C_HUD_POS, this.Demo.hudPosBase: Color32hudPosBase.Color32.multiply(other: Color32): Color32
Multiplies this color by another color component-wise. Useful for tinting and color modulation.
@paramother - Color to multiply with.@returnsNew color with each channel multiplied and normalized.
multiply
(const ambient: Color32ambient));
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_HUD_PHASE: 44C_HUD_PHASE, this.Demo.hudFpsBase: Color32hudFpsBase.Color32.multiply(other: Color32): Color32
Multiplies this color by another color component-wise. Useful for tinting and color modulation.
@paramother - Color to multiply with.@returnsNew color with each channel multiplied and normalized.
multiply
(const ambient: Color32ambient));
// Hero shadow, softer at night. const const shadowAlpha: anyshadowAlpha = Math.floor(60 + (const ambient: Color32ambient.Color32.r: number
Red channel (0-255).
r
/ 255) * 60);
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_HERO_SHADOW: 45C_HERO_SHADOW, 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
(0, 0, 0, const shadowAlpha: anyshadowAlpha));
// Sprite ambient block: each base stone color is multiplied by the current // ambient to get the lit version. drawSprite uses offset = spriteColorCount so it // reads from this block. for (let let i: numberi = 0; let i: numberi < this.Demo.spriteColorCount: numberspriteColorCount; let i: numberi++) { const const base: anybase = this.Demo.spriteBaseColors: {}spriteBaseColors[let i: numberi]; 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 SPRITE_BASE: 70SPRITE_BASE + this.Demo.spriteColorCount: numberspriteColorCount + let i: numberi, const base: anybase.multiply(const ambient: Color32ambient));
} } // Draws the sky gradient and clouds using a slower second camera for parallax depth. Demo.renderSkyLayer(): voidrenderSkyLayer() { // The sky camera x is only a fraction of the real one, so clouds drift slower // than the ground. It uses the same interpolated camera position as the ground // layer, so the two never drift apart from each other frame to frame. const const paraX: anyparaX = Math.floor(this.Demo.cameraRenderPos: Vector2icameraRenderPos.Vector2i.x: number
Horizontal component (defaults to 0).
x
* const SKY_PARALLAX: 0.22SKY_PARALLAX);
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
.cameraSet: (offset: Vector2i) => void
Sets the global camera offset applied to subsequent draw calls.
@since0.1.0@paramoffset - Camera translation in display pixels.
cameraSet
(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
(const paraX: anyparaX, 0));
// Each band is GROUND_Y / SKY_BANDS pixels tall. const const bandH: anybandH = Math.ceil(const GROUND_Y: 188GROUND_Y / const SKY_BANDS: 20SKY_BANDS); for (let let band: numberband = 0; let band: numberband < const SKY_BANDS: 20SKY_BANDS; let band: numberband++) { 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
(0, let band: numberband * const bandH: anybandH, const WORLD_W: 640WORLD_W, const bandH: anybandH);
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.drawRectFill: (rect: Rect2i, paletteIndex: number) => void
Draws a filled rectangle.
@since0.1.0@paramrect - Rectangle bounds in display coordinates.@parampaletteIndex - Palette color index.
drawRectFill
(this.Demo.tempRect: Rect2itempRect, const C_SKY_BASE: 10C_SKY_BASE + let band: numberband);
} // Clouds: two overlapping rectangles for a puffy look. for (const const c: anyc of this.Demo.clouds: {}clouds) { 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
(const c: anyc.x, const c: anyc.y, const c: anyc.w, const c: anyc.h);
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.drawRectFill: (rect: Rect2i, paletteIndex: number) => void
Draws a filled rectangle.
@since0.1.0@paramrect - Rectangle bounds in display coordinates.@parampaletteIndex - Palette color index.
drawRectFill
(this.Demo.tempRect: Rect2itempRect, const C_CLOUD: 40C_CLOUD);
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
(const c: anyc.x + 8, const c: anyc.y - 6, const c: anyc.w - 16, const c: anyc.h - 4);
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.drawRectFill: (rect: Rect2i, paletteIndex: number) => void
Draws a filled rectangle.
@since0.1.0@paramrect - Rectangle bounds in display coordinates.@parampaletteIndex - Palette color index.
drawRectFill
(this.Demo.tempRect: Rect2itempRect, const C_CLOUD: 40C_CLOUD);
}
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
.cameraReset: () => void
Resets the global camera offset to `(0, 0)`.
@since0.1.0@changed1.7.1 Now persists once nothing sets the camera again: previously, the offset the engine re-applies at frame start stayed at its pre-reset value forever, so a later frame that never calls `cameraSet` again still saw the old offset leak back in.
cameraReset
();
} // Draws the grass strip, the tile row, and the building blocks in world space. Demo.renderGroundAndBuildings(): voidrenderGroundAndBuildings() { // Grass. 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
(0, const GROUND_Y: 188GROUND_Y, const WORLD_W: 640WORLD_W, const WORLD_H: 240WORLD_H - const GROUND_Y: 188GROUND_Y);
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.drawRectFill: (rect: Rect2i, paletteIndex: number) => void
Draws a filled rectangle.
@since0.1.0@paramrect - Rectangle bounds in display coordinates.@parampaletteIndex - Palette color index.
drawRectFill
(this.Demo.tempRect: Rect2itempRect, const C_GRASS: 30C_GRASS);
// Thin dark line at the top of the grass for depth. 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
(0, const GROUND_Y: 188GROUND_Y, const WORLD_W: 640WORLD_W, 3);
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.drawRectFill: (rect: Rect2i, paletteIndex: number) => void
Draws a filled rectangle.
@since0.1.0@paramrect - Rectangle bounds in display coordinates.@parampaletteIndex - Palette color index.
drawRectFill
(this.Demo.tempRect: Rect2itempRect, const C_DIRTLINE: 31C_DIRTLINE);
// Walkway tile row: each cell is a tile ID mapped to a palette color (Tilemap). this.Demo.renderGroundTileStrip(): voidrenderGroundTileStrip(); // Buildings: checker fill (Patterns) plus an outline frame. for (let let i: numberi = 0; let i: numberi < this.Demo.buildings: {}buildings.length; let i: numberi++) { const const b: anyb = this.Demo.buildings: {}buildings[let i: numberi]; const const fillIdx: numberfillIdx = const C_BUILDING_BASE: 32C_BUILDING_BASE + let i: numberi * 2; const const outlineIdx: numberoutlineIdx = const fillIdx: numberfillIdx + 1; this.Demo.renderBuildingChecker(building: any, fillIdx: any, outlineIdx: any): voidrenderBuildingChecker(const b: anyb, const fillIdx: numberfillIdx, const outlineIdx: numberoutlineIdx); 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
(const b: anyb.x, const b: anyb.y, const b: anyb.w, const b: anyb.h);
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 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 outlineIdx: numberoutlineIdx);
} } // Draws one 16 px tall row of tiles above the grass using the IDs in groundTileIds. Demo.renderGroundTileStrip(): voidrenderGroundTileStrip() { const const rowY: numberrowY = const GROUND_Y: 188GROUND_Y - const GROUND_TILE_SIZE: 16GROUND_TILE_SIZE; for (let let col: numbercol = 0; let col: numbercol < this.Demo.groundTileIds: {}groundTileIds.length; let col: numbercol++) { const const tileId: anytileId = this.Demo.groundTileIds: {}groundTileIds[let col: numbercol]; const const colorIndex: 30 | 47colorIndex = const tileId: anytileId === const TILE_GRASS_ID: 1TILE_GRASS_ID ? const C_GRASS: 30C_GRASS : const C_DIRT: 47C_DIRT; const const worldX: numberworldX = let col: numbercol * const GROUND_TILE_SIZE: 16GROUND_TILE_SIZE; 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
(const worldX: numberworldX, const rowY: numberrowY, const GROUND_TILE_SIZE: 16GROUND_TILE_SIZE, const GROUND_TILE_SIZE: 16GROUND_TILE_SIZE);
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.drawRectFill: (rect: Rect2i, paletteIndex: number) => void
Draws a filled rectangle.
@since0.1.0@paramrect - Rectangle bounds in display coordinates.@parampaletteIndex - Palette color index.
drawRectFill
(this.Demo.tempRect: Rect2itempRect, const colorIndex: 30 | 47colorIndex);
} } // Fills a building with alternating 4x4 blocks (the checker pattern from Patterns). // `fillIdx` is the palette index for the light squares, `outlineIdx` for the dark // ones. Demo.renderBuildingChecker(building: any, fillIdx: any, outlineIdx: any): voidrenderBuildingChecker(building: anybuilding, fillIdx: anyfillIdx, outlineIdx: anyoutlineIdx) { const const cell: 4cell = const BUILDING_PATTERN_CELL: 4BUILDING_PATTERN_CELL; for (let let py: anypy = building: anybuilding.y; let py: anypy < building: anybuilding.y + building: anybuilding.h; let py: anypy += const cell: 4cell) { for (let let px: anypx = building: anybuilding.x; let px: anypx < building: anybuilding.x + building: anybuilding.w; let px: anypx += const cell: 4cell) { // `>> 2` divides by 4 (the cell size); an even cell sum picks the fill. const const useFill: booleanuseFill = ((let px: anypx >> 2) + (let py: anypy >> 2)) % 2 === 0; const const colorIndex: anycolorIndex = const useFill: booleanuseFill ? fillIdx: anyfillIdx : outlineIdx: anyoutlineIdx; 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
(let px: anypx, let py: anypy, const cell: 4cell, const cell: 4cell);
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.drawRectFill: (rect: Rect2i, paletteIndex: number) => void
Draws a filled rectangle.
@since0.1.0@paramrect - Rectangle bounds in display coordinates.@parampaletteIndex - Palette color index.
drawRectFill
(this.Demo.tempRect: Rect2itempRect, const colorIndex: anycolorIndex);
} } } // Moves the rock left and right automatically, turning around at the world edges. Demo.updateHeroMovement(): voidupdateHeroMovement() { // Remember where the rock was before this tick moves it, so render() can draw a // smooth in-between position instead of a pop. this.Demo.heroPrevPos: Vector2iheroPrevPos.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
(this.Demo.heroPos: Vector2iheroPos.Vector2i.x: number
Horizontal component (defaults to 0).
x
, this.Demo.heroPos: Vector2iheroPos.Vector2i.y: number
Vertical component (defaults to 0).
y
);
let let nextX: numbernextX = this.Demo.heroPos: Vector2iheroPos.Vector2i.x: number
Horizontal component (defaults to 0).
x
+ const HERO_SPEED: 1HERO_SPEED * this.Demo.heroFacing: numberheroFacing;
const const margin: 2margin = 2; if (let nextX: numbernextX <= const margin: 2margin) { let nextX: numbernextX = const margin: 2margin; this.Demo.heroFacing: numberheroFacing = 1; } else if (let nextX: numbernextX + this.Demo.heroSize: Vector2i
@type{Vector2i}
heroSize
.Vector2i.x: number
Horizontal component (defaults to 0).
x
>= const WORLD_W: 640WORLD_W - const margin: 2margin) {
let nextX: numbernextX = const WORLD_W: 640WORLD_W - this.Demo.heroSize: Vector2i
@type{Vector2i}
heroSize
.Vector2i.x: number
Horizontal component (defaults to 0).
x
- const margin: 2margin;
this.Demo.heroFacing: numberheroFacing = -1; } this.Demo.heroPos: Vector2iheroPos.Vector2i.x: number
Horizontal component (defaults to 0).
x
= let nextX: numbernextX;
} // Advances the step counter every WALK_FRAME_TICKS ticks, for the subtle bob of the // rock as it moves. Demo.updateWalkStep(tick: any): voidupdateWalkStep(tick: anytick) { if (this.Demo.walkFrameTimer: TimerwalkFrameTimer.Timer.fireIfElapsed(currentTick?: number): boolean
Returns true once per interval and advances the internal last-fired tick.
@paramcurrentTick - Tick to evaluate against; defaults to engine tick counter.@returnsTrue when at least `intervalTicks` have elapsed since the last fire/reset.
fireIfElapsed
(tick: anytick)) {
this.Demo.walkStep: numberwalkStep = (this.Demo.walkStep: numberwalkStep + 1) % 4; } } // Draws the rock sprite with a shadow underfoot. The sprite is drawn with // paletteOffset = spriteColorCount, which shifts each pixel index into the ambient // block (SPRITE_BASE+N..SPRITE_BASE+2N-1). Those slots are rewritten every tick in // updateWorldPalette() for the current ambient light, so the rock dims at night. Demo.renderHero(): voidrenderHero() { if (!this.Demo.heroSheet: SpriteSheet | null
@type{SpriteSheet | null}
heroSheet
|| !this.Demo.heroSprite: Rect2i | null
@type{Rect2i | null}
heroSprite
) {
return; } // Blend heroPrevPos toward heroPos by BT.renderAlpha so the rock's drawn position // matches this exact render moment instead of only its last-tick position (same // idea as the camera blend in render() above). this.Demo.heroRenderPos: Vector2iheroRenderPos.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.round(this.Demo.heroPrevPos: Vector2iheroPrevPos.Vector2i.x: number
Horizontal component (defaults to 0).
x
+ (this.Demo.heroPos: Vector2iheroPos.Vector2i.x: number
Horizontal component (defaults to 0).
x
- this.Demo.heroPrevPos: Vector2iheroPrevPos.Vector2i.x: number
Horizontal component (defaults to 0).
x
) *
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
.renderAlpha: number
Fractional progress between the last completed fixed update and the next. Intended for interpolating render state between fixed-update steps.
@since1.3.0@returnsInterpolation alpha in `[0, 1)`.
renderAlpha
),
Math.round(this.Demo.heroPrevPos: Vector2iheroPrevPos.Vector2i.y: number
Vertical component (defaults to 0).
y
+ (this.Demo.heroPos: Vector2iheroPos.Vector2i.y: number
Vertical component (defaults to 0).
y
- this.Demo.heroPrevPos: Vector2iheroPrevPos.Vector2i.y: number
Vertical component (defaults to 0).
y
) *
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
.renderAlpha: number
Fractional progress between the last completed fixed update and the next. Intended for interpolating render state between fixed-update steps.
@since1.3.0@returnsInterpolation alpha in `[0, 1)`.
renderAlpha
),
); // Tiny vertical bob based on walkStep: steps 0 and 2 are up, 1 and 3 at rest. const const bob: 0 | -1bob = this.Demo.walkStep: numberwalkStep % 2 === 0 ? -1 : 0; this.Demo.tempVec: Vector2itempVec.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
(this.Demo.heroRenderPos: Vector2iheroRenderPos.Vector2i.x: number
Horizontal component (defaults to 0).
x
, this.Demo.heroRenderPos: Vector2iheroRenderPos.Vector2i.y: number
Vertical component (defaults to 0).
y
+ const bob: 0 | -1bob);
// The ambient offset shifts all pixel indices into the pre-lit block.
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
.drawSprite: (spriteSheet: SpriteSheet, srcRect: Rect2i, destPos: Vector2i, paletteOffset?: number) => void (+1 overload)drawSprite(this.Demo.heroSheet: SpriteSheet
@type{SpriteSheet | null}
heroSheet
, this.Demo.heroSprite: Rect2i
@type{Rect2i | null}
heroSprite
, this.Demo.tempVec: Vector2itempVec, this.Demo.spriteColorCount: numberspriteColorCount);
// Shadow underfoot: a 2-pixel strip on the walk line, as wide as the rock's // visible pixels (x = 4 to 38 in the frame) minus a little at each end. It uses // heroRenderPos, not tempVec, so it stays on the ground while the rock bobs. // The engine draws rectangles before sprites, so the rock covers its top row. 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
(this.Demo.heroRenderPos: Vector2iheroRenderPos.Vector2i.x: number
Horizontal component (defaults to 0).
x
+ 6, const WALK_LINE_Y: numberWALK_LINE_Y - 1, 30, 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
.drawRectFill: (rect: Rect2i, paletteIndex: number) => void
Draws a filled rectangle.
@since0.1.0@paramrect - Rectangle bounds in display coordinates.@parampaletteIndex - Palette color index.
drawRectFill
(this.Demo.tempRect: Rect2itempRect, const C_HERO_SHADOW: 45C_HERO_SHADOW);
} // Smoothly follows the hero, then clamps so the view never leaves the world. Demo.updateCameraFollow(): voidupdateCameraFollow() { // Remember where the camera was before this tick's follow-lerp moves it. this.Demo.cameraPrevPos: Vector2icameraPrevPos.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
(this.Demo.cameraPos: Vector2icameraPos.Vector2i.x: number
Horizontal component (defaults to 0).
x
, this.Demo.cameraPos: Vector2icameraPos.Vector2i.y: number
Vertical component (defaults to 0).
y
);
const const targetCamX: numbertargetCamX = this.Demo.heroPos: Vector2iheroPos.Vector2i.x: number
Horizontal component (defaults to 0).
x
- const DISPLAY_W: 320DISPLAY_W / 2 + this.Demo.heroSize: Vector2i
@type{Vector2i}
heroSize
.Vector2i.x: number
Horizontal component (defaults to 0).
x
/ 2;
this.Demo.cameraXFloat: numbercameraXFloat += (const targetCamX: numbertargetCamX - this.Demo.cameraXFloat: numbercameraXFloat) * const CAMERA_LERP: 0.14CAMERA_LERP; this.Demo.cameraPos: Vector2icameraPos.Vector2i.x: number
Horizontal component (defaults to 0).
x
= Math.floor(this.Demo.cameraXFloat: numbercameraXFloat);
this.Demo.clampCamera(): voidclampCamera(); } // Keeps cameraPos.x between 0 and WORLD_W - DISPLAY_W. Demo.clampCamera(): voidclampCamera() { const const clamped: Vector2iclamped =
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
.cameraClamp: (camera: Vector2i, worldSize: Vector2i, viewSize?: Vector2i) => Vector2i
Clamps a camera origin so the viewport stays within world bounds. Uses integer clamping per axis: `[0, worldSize - viewSize]`. If `viewSize` is omitted, the active {@link BT.displaySize } is used.
@since1.0.3@paramcamera - Desired camera origin in world coordinates.@paramworldSize - Full world size in pixels.@paramviewSize - Viewport size in pixels (defaults to {@link BT.displaySize}).@returnsClamped camera origin.
cameraClamp
(this.Demo.cameraPos: Vector2icameraPos, this.Demo.worldSize: Vector2iworldSize,
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
.displaySize: Vector2i
Active logical render resolution in pixels. This is the game/simulation coordinate space configured by the demo, not the canvas element's CSS size. Each read returns a clone.
@since1.0.4@returnsConfigured logical size, or `Vector2i.zero()` before initialization.
displaySize
);
this.Demo.cameraPos: Vector2icameraPos.Vector2i.x: number
Horizontal component (defaults to 0).
x
= const clamped: Vector2iclamped.Vector2i.x: number
Horizontal component (defaults to 0).
x
;
this.Demo.cameraPos: Vector2icameraPos.Vector2i.y: number
Vertical component (defaults to 0).
y
= const clamped: Vector2iclamped.Vector2i.y: number
Vertical component (defaults to 0).
y
;
this.Demo.cameraXFloat: numbercameraXFloat = this.Demo.cameraPos: Vector2icameraPos.Vector2i.x: number
Horizontal component (defaults to 0).
x
;
} // +1 score every SCORE_INTERVAL_TICKS. Demo.updateScore(tick: any): voidupdateScore(tick: anytick) { if (this.Demo.scoreTimer: TimerscoreTimer.Timer.fireIfElapsed(currentTick?: number): boolean
Returns true once per interval and advances the internal last-fired tick.
@paramcurrentTick - Tick to evaluate against; defaults to engine tick counter.@returnsTrue when at least `intervalTicks` have elapsed since the last fire/reset.
fireIfElapsed
(tick: anytick)) {
this.Demo.score: numberscore += 1; } } // Spawns three sparkles near the rock on a fixed schedule. Demo.updateParticlesSpawn(tick: any): voidupdateParticlesSpawn(tick: anytick) { if (this.Demo.particleSpawnTimer: TimerparticleSpawnTimer.Timer.fireIfElapsed(currentTick?: number): boolean
Returns true once per interval and advances the internal last-fired tick.
@paramcurrentTick - Tick to evaluate against; defaults to engine tick counter.@returnsTrue when at least `intervalTicks` have elapsed since the last fire/reset.
fireIfElapsed
(tick: anytick)) {
for (let let i: numberi = 0; let i: numberi < 3; let i: numberi++) { const const slot: numberslot = const PARTICLE_SLOT_START: 50PARTICLE_SLOT_START + (this.Demo.nextParticleSlot: numbernextParticleSlot % const MAX_PARTICLES: 20MAX_PARTICLES); this.Demo.nextParticleSlot: numbernextParticleSlot++; // Scatter each sparkle around the rock. BT.random.int(min, max) returns a // whole number from min up to but not including max, so int(-10, 10) // gives -10 to 9. The y range is lopsided on purpose: most offsets are // negative, which on screen means upward, so the sparkles gather above // the rock. const const ox: numberox =
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
.random: Random
Default engine PRNG (live reference - not a copy). Time-seeded when the engine singleton is created. Call {@link BT.randomSeed } for a reproducible run, or open the page with `?seed=N` - applied in `BT.init()` just before the demo's own `init()`, so a `randomSeed` call there still wins. Mutating the instance (for example `BT.random.int(10)`) advances the shared stream.
@since1.5.0@changed1.7.1 `?seed=N` URL parameter seeds the shared generator before the demo's `init()`.@returnsThe shared {@link Random} instance.@exampleBT.randomSeed(42); BT.random.int(150, 420); BT.random.pick(['a', 'b', 'c']);
random
.Random.int(minOrMaxExclusive: number, maxExclusive?: number): number
Returns a pseudo-random integer in [0, maxExclusive) or [min, maxExclusive).
@paramminOrMaxExclusive - When alone, exclusive upper bound from 0; otherwise inclusive min.@parammaxExclusive - Exclusive upper bound when two arguments are passed.@returnsWhole number in the half-open range.@since1.5.0
int
(-10, 10);
const const oy: numberoy =
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
.random: Random
Default engine PRNG (live reference - not a copy). Time-seeded when the engine singleton is created. Call {@link BT.randomSeed } for a reproducible run, or open the page with `?seed=N` - applied in `BT.init()` just before the demo's own `init()`, so a `randomSeed` call there still wins. Mutating the instance (for example `BT.random.int(10)`) advances the shared stream.
@since1.5.0@changed1.7.1 `?seed=N` URL parameter seeds the shared generator before the demo's `init()`.@returnsThe shared {@link Random} instance.@exampleBT.randomSeed(42); BT.random.int(150, 420); BT.random.pick(['a', 'b', 'c']);
random
.Random.int(minOrMaxExclusive: number, maxExclusive?: number): number
Returns a pseudo-random integer in [0, maxExclusive) or [min, maxExclusive).
@paramminOrMaxExclusive - When alone, exclusive upper bound from 0; otherwise inclusive min.@parammaxExclusive - Exclusive upper bound when two arguments are passed.@returnsWhole number in the half-open range.@since1.5.0
int
(-12, 4);
this.Demo.particles: {}particles.push({ // Spawn each sparkle near the center of the rock (half its real width // and height in from the top-left corner), plus the random offset // picked above. pos: Vector2ipos: 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
(
this.Demo.heroPos: Vector2iheroPos.Vector2i.x: number
Horizontal component (defaults to 0).
x
+ this.Demo.heroSize: Vector2i
@type{Vector2i}
heroSize
.Vector2i.x: number
Horizontal component (defaults to 0).
x
/ 2 + const ox: numberox,
this.Demo.heroPos: Vector2iheroPos.Vector2i.y: number
Vertical component (defaults to 0).
y
+ this.Demo.heroSize: Vector2i
@type{Vector2i}
heroSize
.Vector2i.y: number
Vertical component (defaults to 0).
y
/ 2 + const oy: numberoy,
), spawnTick: anyspawnTick: tick: anytick, paletteSlot: numberpaletteSlot: const slot: numberslot, }); } } } // Removes particles older than 72 ticks (1.2 seconds). Demo.cleanupParticles(tick: any): voidcleanupParticles(tick: anytick) { this.Demo.particles: {}particles = this.Demo.particles: {}particles.filter((p: anyp) => tick: anytick - p: anyp.spawnTick < 72); } // Updates each particle's palette slot with its current color. The hue comes from // spawnTick and age; the alpha fades out as the particle ages. The ambient tint is // applied too, so sparkles dim at night. Demo.updateParticleColors(tick: any): voidupdateParticleColors(tick: anytick) { const const ambient: Color32ambient = this.Demo.getAmbientTint(): Color32getAmbientTint(); for (const const p: anyp of this.Demo.particles: {}particles) { const const age: numberage = tick: anytick - const p: anyp.spawnTick; const const fade: numberfade = 1 - const age: numberage / 72; const const alpha: anyalpha = Math.floor(200 * const fade: numberfade); const const hue: numberhue = (const p: anyp.spawnTick * 7 + const age: numberage * 4) % 360; const const base: Color32base = 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, 90, 65);
const const lit: Color32lit = const base: Color32base.Color32.multiply(other: Color32): Color32
Multiplies this color by another color component-wise. Useful for tinting and color modulation.
@paramother - Color to multiply with.@returnsNew color with each channel multiplied and normalized.
multiply
(const ambient: Color32ambient);
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 p: anyp.paletteSlot, 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
(const lit: Color32lit.Color32.r: number
Red channel (0-255).
r
, const lit: Color32lit.Color32.g: number
Green channel (0-255).
g
, const lit: Color32lit.Color32.b: number
Blue channel (0-255).
b
, const alpha: anyalpha));
} } // Draws the active particles as 3x3 colored squares. The colors were computed in // update(); render() only reads the slot. Demo.renderParticles(): voidrenderParticles() { for (const const p: anyp of this.Demo.particles: {}particles) { 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
(const p: anyp.pos.x - 1, const p: anyp.pos.y - 1, 3, 3);
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.drawRectFill: (rect: Rect2i, paletteIndex: number) => void
Draws a filled rectangle.
@since0.1.0@paramrect - Rectangle bounds in display coordinates.@parampaletteIndex - Palette color index.
drawRectFill
(this.Demo.tempRect: Rect2itempRect, const p: anyp.paletteSlot);
} } // The HUD in screen space, built with the shared UI kit: a small Capstone panel with // the Save button in the top-left corner, a Score and time-of-day panel in the // top-right corner, and the "enable sound" hint down on the grass. The two panels // stay small so most of the sky and clouds stay visible. The Save button also listens // for the Space key, so keyboard, mouse, and touch all trigger the same PNG capture // (Image Output). Demo.renderHud(): voidrenderHud() { // Anchor the group to the top-left corner; the kit sizes the panel to fit its // rows. panel() gives the group a background, border, and an amber title line. import uiui.begin(import UI_ANCHORSUI_ANCHORS.TOP_LEFT); import uiui.panel('Capstone'); // The Save button returns true only on the frame it is clicked, tapped, or its // bound key (Space) is pressed. The `capturing` flag stops a second capture from // starting while the browser is still writing the first PNG. if (import uiui.button('Save PNG (Space)', { key: stringkey: 'Space' }) && !this.Demo.capturing: booleancapturing) { this.Demo.startCapture(): voidstartCapture(); } // Progress / result line for the capture, in the kit's accent color. if (this.Demo.capturing: booleancapturing) { import uiui.label('Capturing...', { color: stringcolor: 'accent' }); } else if (this.Demo.messageTimer: numbermessageTimer > 0) { import uiui.label(this.Demo.lastCaptureMessage: stringlastCaptureMessage, { color: stringcolor: 'accent' }); } import uiui.end(); // Score and time of day, the two things a player of this scene would want to // see. kv() rows line their values up in a column; kvCols: 6 makes the key // column wide enough for "Score" plus a space. import uiui.begin(import UI_ANCHORSUI_ANCHORS.TOP_RIGHT, { kvCols: numberkvCols: 6 }); import uiui.panel('Status'); import uiui.kv('Score', this.Demo.score: numberscore); import uiui.kv('Time', this.Demo.getDayPhaseLabel(tick?: number): "Day" | "Toward dusk" | "Night" | "Toward dawn"getDayPhaseLabel()); import uiui.end(); // Browsers keep all sound muted until the player clicks or presses a key. This // kit row shows the standard warm "enable sound" hint only while the audio is // still locked, then disappears on its own. It sits on the plain grass in the // bottom-left corner, out of the way of the sky and the walking rock. import uiui.begin(import UI_ANCHORSUI_ANCHORS.BOTTOM_LEFT); import uiui.audioUnlockHint(); import uiui.end(); } // Starts one PNG download (Image Output). BT.downloadFrame() is asynchronous - it // hands the browser a file and resolves later - so `capturing` flips on now and the // result message (shown for 3 seconds via messageTimer) is set when the promise // settles. Demo.startCapture(): voidstartCapture() { this.Demo.capturing: booleancapturing = true;
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
.downloadFrame: (filename?: string, options?: FrameCaptureOptions) => Promise<void>
Captures the next rendered frame and downloads it from the browser. Convenience wrapper around {@link BT.captureFrame } that creates a temporary object URL and clicks a synthetic anchor element. Takes the same `options` as {@link BT.captureFrame } .
@since1.0.3@changed1.7.1 Accepts `{ size: 'display' }` to download at logical `BT.displaySize`.@paramfilename - Target download filename.@paramoptions - Capture options; `size` defaults to `'output'`.@exampleawait BT.downloadFrame(); await BT.downloadFrame('screenshot-001.png'); await BT.downloadFrame('exact.png', { size: 'display' });
downloadFrame
('blit386-scene.png')
.then(() => { this.Demo.lastCaptureMessage: stringlastCaptureMessage = 'Saved: blit386-scene.png'; this.Demo.messageTimer: numbermessageTimer = 180; this.Demo.capturing: booleancapturing = false;
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
.soundPlay: (clip: AudioClip, options?: SoundPlayOptions) => SoundRef
Plays a loaded audio clip through the SFX voice pool. Returns an inert {@link SoundRef } without allocating a voice when the clip hasn't finished loading yet (or was already unloaded), when the pool has no free or stealable voice at this priority, or before the engine has unlocked audio playback.
@since1.3.0@paramclip - Loaded audio clip to play.@paramoptions - Playback options.@returnsA handle identifying the new voice; pass it to {@link BT.soundStop} and the other per-sound controls. Safe to use even when playback was silently dropped - every accessor on an inert handle is a no-op.
soundPlay
(this.Demo.captureBlipClip: AudioClip | null
@type{AudioClip | null}
captureBlipClip
);
return null; }) .catch((err: anyerr) => { this.Demo.lastCaptureMessage: stringlastCaptureMessage = `Error: ${err: anyerr.message}`; this.Demo.messageTimer: numbermessageTimer = 180; this.Demo.capturing: booleancapturing = false; console.error('[GameSceneDemo] Capture failed:', err: anyerr); }); } // Human-readable label for the current point in the day/night cycle. `tick` defaults // to BT.ticks. Demo.getDayPhaseLabel(tick?: number): "Day" | "Toward dusk" | "Night" | "Toward dawn"getDayPhaseLabel(tick: numbertick =
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.ticks: number
Current fixed-update tick counter. Increments once per engine update. Reset via {@link BT.ticksReset } .
@since1.0.4@returnsCurrent tick count since initialization or last reset.
ticks
) {
const const phaseTick: numberphaseTick = tick: numbertick % const DAY_NIGHT_CYCLE_TICKS: 1200DAY_NIGHT_CYCLE_TICKS; if (const phaseTick: numberphaseTick < const DAY_NIGHT_CYCLE_TICKS: 1200DAY_NIGHT_CYCLE_TICKS * 0.25) { return 'Day'; } if (const phaseTick: numberphaseTick < const DAY_NIGHT_CYCLE_TICKS: 1200DAY_NIGHT_CYCLE_TICKS * 0.5) { return 'Toward dusk'; } if (const phaseTick: numberphaseTick < const DAY_NIGHT_CYCLE_TICKS: 1200DAY_NIGHT_CYCLE_TICKS * 0.75) { return 'Night'; } return 'Toward dawn'; } } 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 DemoDemo);