/*

Snake Game: grid snake with walls, food, three ways to steer, and a PipBoy CRT finish.
@description Grid snake with walls, food, keyboard, D-pad, and swipe steering, plus PipBoy CRT post-processing.

Part of the BLIT386 demo series.

Prerequisites:
  - Basics: https://demos.blit386.dev/basics
  - Primitives: https://demos.blit386.dev/primitives
  - PipBoy CRT: https://demos.blit386.dev/crt-pipboy
  - Keyboard Input: https://demos.blit386.dev/keyboard-input

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

Guides:
  - Input: https://blit386.dev/docs/guides/input#keyboard
  - Post-Process Effects: https://blit386.dev/docs/guides/post-process-effects#display-tier-effects
  - Audio: https://blit386.dev/docs/guides/audio#playing-sfx
  - Random: https://blit386.dev/docs/guides/random#deterministic-by-default
  - Overlay: https://blit386.dev/docs/guides/overlay#colors-and-hud-palette-slots

Reference:
  - Audio: https://blit386.dev/docs/api/audio#playback-sfx
  - Overlay: https://blit386.dev/docs/api/overlay#event-tags

---

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

A small 160x120 playfield, shown at 4x size, framed by a sand-colored wall. A green snake
three cells long starts in the middle heading right, and a red food dot sits somewhere on
the grid. Eating the dot grows the snake by one cell and shortens the wait between steps
by one tick (from 20 ticks down to a floor of 4), so the snake gets faster and faster.
Hitting the wall or your own body ends the round; two seconds later a new one starts.

When WebGPU is active, the whole picture goes through the PipBoy CRT chain from the
PipBoy CRT demo: a pixel-tier PixelGlitch on the index buffer, then palette resolve and
upscale, then display-tier barrel distortion, chromatic aberration, interference, a
rolling scan line, scanlines, an RGB mask, vignette, noise, flicker, and bloom. Every few
seconds a random glitch burst (horizontal tear, chroma split, snow, brightness waver, or
ghosting) sweeps the screen. In software fallback mode the game runs without the
effects, and a short note at the bottom of the playfield says so.

Eating and dying each play a synthesized sound effect, and an upbeat music loop plays in
the background. Each food eaten nudges the loop's playback rate up by 3 percent, so the
beat climbs with the snake's length. On a touchscreen a D-pad appears in the bottom-right
corner after the first touch.

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

- BT.inputMap(0, BT.BTN_UP, 'KeyW', 'ArrowUp') and friends fold WASD and the arrow keys
  onto player 0, after BT.inputMapReset() clears any earlier remapping.
- BT.isPressed(button, 0) as an edge (true once per key press), combined with the shared
  UI kit's ui.dpad.isPressed(dir) and ui.swipe() so one check covers all three inputs.
- A tick-driven game: BT.ticks for the restart timer, a step cooldown that shrinks as the
  snake grows, and a pending direction that blocks 180-degree turns.
- The same CRT effect chain as the PipBoy CRT demo (BT.effectAdd per effect), gated on
  isAvailable() from src/shared/post-process-backend.js so the game still runs in
  software mode.
- A glitch state machine driven by BT.random.int(), BT.random.pick(), and
  BT.random.float(), with the glitch types and tuning shared through
  src/shared/crt-glitch.js.
- BT.synthPreset.pickup() and BT.synthPreset.explosion() rendered once with
  AudioClip.synth(), then played with BT.soundPlay().
- Music through BT.soundPlay(clip, { loop, volume, pitch, priority }) instead of
  BT.musicPlay(), because only the SFX path exposes pitch, changed live with
  BT.soundPitchSet(ref, rate, { fadeMs }).
- configure() flags: isCapturingKeyboardScroll so the arrows do not scroll the page,
  isWakeLockEnabled so phones stay awake, isOverlayToggleHintVisible: false to hide the
  overlay hint, and overlayStyle to color the overlay bars with scene slots.

TRY THIS
--------

- Steer with W, A, S, D or the arrow keys. A key press counts once, so tap, do not hold.
- On a phone or tablet, tap the D-pad keys in the bottom-right corner, or swipe anywhere
  on the playfield in the direction you want to go.
- Eat a few dots and listen: the music speeds up a little with every one, and resets
  when a new round starts.
- Press the backquote key (`) to open the engine overlay (its corner hint is hidden
  here). "Round start" and "Game over" tags land on the timing chart.
- Add ?backend=software to the URL to see the game without the CRT chain.

*/

import {
    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
,
class BarrelDistortion
Barrel distortion that warps UVs outward from the screen center. Display-tier: operates on the upscaled output. Applying this in the pixel tier (logical 320x240) discretizes the curve onto the source texel grid, which CSS upscale then magnifies into visible step artifacts. At output resolution the curve has enough resolution to express smoothly. The math comes from Timothy Lottes's public-domain `crt-lottes.glsl`: `warp(uv) = uv + delta * d2 * curvature` where `delta = uv - 0.5` and `d2 = dot(delta, delta)`.
@since1.0.3
BarrelDistortion
,
class Bloom
Single-pass box-blur bloom. Samples a 5x5 neighborhood (25 taps) around each fragment, averages, then mixes with the original color by {@link glow } . {@link spread } scales the texel offset so the bloom radius can be tuned independently of the source resolution. Display-tier: bloom mixes neighboring pixels into intermediate hues that are not in the active palette. Running it in pixel space would violate the palette-pixel aesthetic; running it on the upscaled output reads as the warm phosphor glow of an old monitor instead. The implementation matches the original PipBoy bloom shader. A future optimization would be a two-pass separable Gaussian (5 + 5 = 10 taps); add it once a GPU perf test demands it.
@since1.0.3
Bloom
,
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 ChromaticAberration
RGB channel offset that simulates lens chromatic aberration: red samples left of the fragment, blue samples right, green stays centered. Display-tier: spreads color along the lens axis. At logical resolution the single-pixel offset is too coarse and reads as a glitch instead of a soft fringe.
@since1.0.3
ChromaticAberration
,
class Color32
Mutable 32-bit RGBA color value with 8-bit channels.
@since0.1.0
Color32
,
class Flicker
Brightness multiplier - the simplest CRT animation knob. Demos drive {@link amount } per frame to simulate flicker (e.g. with `0.95 + sin(t) * 0.05`). The effect is intentionally trivial so the demo controls the pattern; for procedural noise-driven flicker, combine with the {@link Noise } effect. Display-tier.
@since1.0.3
Flicker
,
class Interference
Per-row horizontal jitter that simulates analog signal interference. Each output row gets a deterministic random horizontal offset seeded by row index and time. Row offsets are stable for one frame and re-seed every frame, producing a buzzing-noise feel. Display-tier. Drives jitter from {@link time } ; demos typically pass `BT.ticks / BT.targetFPS`.
@since1.0.3
Interference
,
class Noise
Additive per-pixel pseudo-random noise. Reseeds each frame from {@link time } so the noise pattern animates. Display-tier.
@since1.0.3
Noise
,
class PixelGlitch
Chunky pixel-aligned horizontal glitch: every Nth row of source pixels gets a random horizontal shift. Shifts snap to integer source-pixel offsets so palette indices move whole-texel (no RGB resampling). Pixel-tier: runs on the logical `r8uint` framebuffer (palette indices).
@since1.0.3
PixelGlitch
,
class Rect2i
Integer rectangle for pixel-perfect bounds and regions. Used throughout the engine for sprite regions, display-space bounds, and zero-allocation geometry helpers. Both convenience getters and allocation-free `*To()` helpers are provided so callers can choose between readability and hot-path efficiency.
@since0.1.0
Rect2i
,
class RGBMask
CRT shadow mask: per-pixel R/G/B vertical-stripe pattern with darkened cell borders, simulating the phosphor grille of an aperture-grille CRT. Display-tier: at output resolution there are enough output pixels per mask cell to read as colored stripes. The cell pitch (in output pixels) is the {@link size } parameter. Math is a direct WGSL port of the libretro `crt-lottes.glsl` mask code.
@since1.0.3
RGBMask
,
class RollLine
Slowly scrolling vertical interference band that brightens a horizontal stripe of the image. Combination of three cosines + smoothstep gives the stripe a soft top/bottom edge. Display-tier. Demo drives {@link time } (typically `BT.ticks / BT.targetFPS`).
@since1.0.3
RollLine
,
class Scanlines
CRT scanlines: alternating bright/dark horizontal bands aligned to the source vertical resolution. Display-tier: at output resolution there is enough vertical pixels for scanlines to read as alternating bright/dark bands. At logical 320x240 the Gaussian weight quantizes to one of two values per source row and you lose the smooth fade.
@since1.0.3
Scanlines
,
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
,
class Vignette
Edge-darkening vignette: smooth radial fade from full brightness at the center to black at the corners. Display-tier: applies to the whole simulated screen, not the underlying pixel art.
@since1.0.3
Vignette
,
} from 'blit386'; import { import ABERRATION_BASEABERRATION_BASE, import applyGlitchUniformsapplyGlitchUniforms, import FLICKER_BASEFLICKER_BASE, import GLITCH_ACTIVE_MAXGLITCH_ACTIVE_MAX, import GLITCH_ACTIVE_MINGLITCH_ACTIVE_MIN, import GLITCH_COOLDOWN_MAXGLITCH_COOLDOWN_MAX, import GLITCH_COOLDOWN_MINGLITCH_COOLDOWN_MIN, import GLITCH_INTENSITY_MAXGLITCH_INTENSITY_MAX, import GLITCH_INTENSITY_MINGLITCH_INTENSITY_MIN, import GLITCH_TYPES_CHROMAGLITCH_TYPES_CHROMA, import NOISE_BASENOISE_BASE, import PIXEL_GLITCH_BAND_HEIGHTPIXEL_GLITCH_BAND_HEIGHT, import resetGlitchUniformsresetGlitchUniforms, } from './shared/crt-glitch.js'; import { import isAvailableisAvailable, import SOFTWARE_FALLBACK_NOTESOFTWARE_FALLBACK_NOTE } from './shared/post-process-backend.js'; import { import applyThemeapplyTheme, import uiui, import UI_ANCHORSUI_ANCHORS } from './shared/ui.js'; /** @typedef {import('blit386').Palette} Palette */ // Palette slots for the scene (slot 0 is reserved as transparent). const const C_BG: 1C_BG = 1; const const C_WALL: 2C_WALL = 2; const const C_SNAKE: 3C_SNAKE = 3; const const C_FOOD: 4C_FOOD = 4; const const C_FOOTER_DIM: 5C_FOOTER_DIM = 5; const const C_FOOTER_WHITE: 6C_FOOTER_WHITE = 6; // Logical resolution: a small playfield. const const DISPLAY_W: 160DISPLAY_W = 160; const const DISPLAY_H: 120DISPLAY_H = 120; // Canvas output size: 4x the logical resolution, so the display-tier CRT effects have // enough pixels to work with. const const OUTPUT_W: 640OUTPUT_W = 640; const const OUTPUT_H: 480OUTPUT_H = 480; const const TARGET_FPS: 60TARGET_FPS = 60; // Wall thickness in pixels (also one grid cell tall and wide). const const WALL: 8WALL = 8; // Snake and food are drawn on a coarse grid so movement stays chunky and readable. const const CELL: 8CELL = 8; // Inner playable area after subtracting the wall strip from each side. const const INNER_X0: 8INNER_X0 = const WALL: 8WALL; const const INNER_Y0: 8INNER_Y0 = const WALL: 8WALL; const const INNER_W: numberINNER_W = const DISPLAY_W: 160DISPLAY_W - 2 * const WALL: 8WALL; const const INNER_H: numberINNER_H = const DISPLAY_H: 120DISPLAY_H - 2 * const WALL: 8WALL; // How many cells fit inside the inner rectangle (should divide evenly). const const CELLS_X: numberCELLS_X = const INNER_W: numberINNER_W / const CELL: 8CELL; const const CELLS_Y: numberCELLS_Y = const INNER_H: numberINNER_H / const CELL: 8CELL; // Ticks between snake steps (lower = faster). A new round starts slow, then each food // shortens the wait by MOVE_INTERVAL_STEP until MOVE_INTERVAL_MIN. At 60 ticks per // second, 20 ticks is three steps per second; 4 ticks is fifteen steps per second. const const MOVE_INTERVAL_START: 20MOVE_INTERVAL_START = 20; const const MOVE_INTERVAL_MIN: 4MOVE_INTERVAL_MIN = 4; const const MOVE_INTERVAL_STEP: 1MOVE_INTERVAL_STEP = 1; // Background-loop playback rate (Web Audio pitch). 1.0 is the file's natural tempo; // higher values play faster and higher, like speeding up a cassette. // MUSIC_PITCH_AT_START is the rate on a fresh round; each food multiplies that rate by // MUSIC_PITCH_SCALE_PER_GROWTH (so 0.57, then 0.57 * 1.03, then 0.57 * 1.03 * 1.03, ...). const const MUSIC_PITCH_AT_START: 0.57MUSIC_PITCH_AT_START = 0.57; const const MUSIC_PITCH_SCALE_PER_GROWTH: 1.03MUSIC_PITCH_SCALE_PER_GROWTH = 1.03; // How many growths it takes for moveInterval to reach MOVE_INTERVAL_MIN. Music pitch uses // the same ceiling so the loop stops speeding up once the snake is already at top speed. const const MUSIC_PITCH_GROWTH_CAP: numberMUSIC_PITCH_GROWTH_CAP = (const MOVE_INTERVAL_START: 20MOVE_INTERVAL_START - const MOVE_INTERVAL_MIN: 4MOVE_INTERVAL_MIN) / const MOVE_INTERVAL_STEP: 1MOVE_INTERVAL_STEP; // High enough that short eat and death blips never steal the looping music voice from // the SFX pool (BT.soundPlay uses priority stealing when every voice is busy). const const MUSIC_VOICE_PRIORITY: 100MUSIC_VOICE_PRIORITY = 100; // Soft glide when the loop speeds up or slows down after a food eat or a new round. const const MUSIC_PITCH_FADE_MS: 120MUSIC_PITCH_FADE_MS = 120; // Two seconds at 60 ticks per second before a new round starts. const const RESTART_DELAY_TICKS: 120RESTART_DELAY_TICKS = 120; // Minimal snake with the PipBoy CRT post-processing stack from the PipBoy CRT demo. class class DemoDemo { /** @type {Palette | null} */ Demo.palette: Palette | null
@type{Palette | null}
palette
= null;
// Sound played when the snake eats food. /** @type {AudioClip | null} */ Demo.eatClip: AudioClip | null
@type{AudioClip | null}
eatClip
= null;
// Sound played when the snake dies. /** @type {AudioClip | null} */ Demo.gameOverClip: AudioClip | null
@type{AudioClip | null}
gameOverClip
= null;
// Looping background music. /** @type {AudioClip | null} */ Demo.musicClip: AudioClip | null
@type{AudioClip | null}
musicClip
= null;
// Live handle for the looping music voice, or null before unlock or if the music // failed to load. We use BT.soundPlay (not BT.musicPlay) so we can change pitch each // time the snake speeds up - the music player has volume and crossfade controls, but // no pitch. /** @type {import('blit386').SoundRef | null} */ Demo.musicRef: SoundRef | null
@type{import('blit386').SoundRef | null}
musicRef
= null;
// Head first, tail last (grid coordinates). /** @type {{ x: number; y: number }[]} */ Demo.snake: {}
@type{{ x: number; y: number }[]}
snake
= [];
// Food cell in grid coordinates. /** @type {{ x: number; y: number }} */
Demo.food: {
    x: number;
    y: number;
}
@type{{ x: number; y: number }}
food
= { x: numberx: 0, y: numbery: 0 };
// Current step direction (grid units per move). Demo.dx: numberdx = 1; Demo.dy: numberdy = 0; // Next direction chosen by the player (applied when the snake steps). Demo.pendingDx: numberpendingDx = 1; Demo.pendingDy: numberpendingDy = 0; // Counts ticks until the next snake step while the game is running. Demo.moveCooldown: numbermoveCooldown = 0; // Current ticks between steps. Starts at MOVE_INTERVAL_START each round and drops // toward MOVE_INTERVAL_MIN every time the snake eats food. Demo.moveInterval: numbermoveInterval = const MOVE_INTERVAL_START: 20MOVE_INTERVAL_START; // How many food dots the snake has eaten this round. Music pitch uses this count // capped at MUSIC_PITCH_GROWTH_CAP so tempo plateaus with move speed. Demo.growthCount: numbergrowthCount = 0; // When true, the snake does not move until restart. Demo.gameOver: booleangameOver = false; // Tick index when the snake died (BT.ticks); null while playing. /** @type {number | null} */ Demo.deathTick: number | null
@type{number | null}
deathTick
= null;
// True when the WebGPU backend is active, so the CRT effect chain can run (set in // init()). Demo.effectsAvailable: booleaneffectsAvailable = false; // CRT effects, created in setupCrtEffects(), which init() calls only under WebGPU. /** @type {PixelGlitch} */ Demo.pixelGlitch: PixelGlitch
@type{PixelGlitch}
pixelGlitch
;
/** @type {BarrelDistortion} */ Demo.barrel: BarrelDistortion
@type{BarrelDistortion}
barrel
;
/** @type {ChromaticAberration} */ Demo.aberration: ChromaticAberration
@type{ChromaticAberration}
aberration
;
/** @type {Interference} */ Demo.interference: Interference
@type{Interference}
interference
;
/** @type {RollLine} */ Demo.rollLine: RollLine
@type{RollLine}
rollLine
;
/** @type {Scanlines} */ Demo.scanlines: Scanlines
@type{Scanlines}
scanlines
;
/** @type {RGBMask} */ Demo.mask: RGBMask
@type{RGBMask}
mask
;
/** @type {Vignette} */ Demo.vignette: Vignette
@type{Vignette}
vignette
;
/** @type {Noise} */ Demo.noise: Noise
@type{Noise}
noise
;
/** @type {Flicker} */ Demo.flicker: Flicker
@type{Flicker}
flicker
;
/** @type {Bloom} */ Demo.bloom: Bloom
@type{Bloom}
bloom
;
// Ticks until the next glitch burst may start. Demo.glitchCooldown: numberglitchCooldown = 0; // How many ticks remain in the current glitch burst; 0 means no glitch is running. Demo.glitchTicksLeft: numberglitchTicksLeft = 0; // Total length of the current burst in ticks, used to shape its envelope. Demo.glitchDuration: numberglitchDuration = 0; // Which glitch is running - a key from GLITCH_TYPES_CHROMA, or 'none'. /** @type {string} */ Demo.glitchType: string
@type{string}
glitchType
= 'none';
// Peak intensity of the current burst, from GLITCH_INTENSITY_MIN to MAX. Demo.glitchPeak: numberglitchPeak = 0; // 160x120 framebuffer, 4x upscale for the CRT chain, 60 fixed updates per second.
Demo.configure(): {
    displaySize: Vector2i;
    drawingBufferSize: Vector2i;
    maxCanvasSize: Vector2i;
    outputUpscaleFilter: string;
    targetFPS: number;
    isWakeLockEnabled: boolean;
    isCapturingKeyboardScroll: boolean;
    isOverlayToggleHintVisible: boolean;
    overlayStyle: {
        barPaletteIndex: number;
        textPaletteIndex: number;
        gapPaletteIndex: number;
    };
    isOverlayTimingChartEnabled: boolean;
    overlayTimingChartStyle: {
        updateBarPaletteIndex: number;
        renderBarPaletteIndex: number;
        warningPaletteIndex: number;
        errorPaletteIndex: number;
        tagPaletteIndex: number;
    };
}
configure
() {
return { displaySize: Vector2idisplaySize: 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 DISPLAY_W: 160DISPLAY_W, const DISPLAY_H: 120DISPLAY_H),
drawingBufferSize: Vector2idrawingBufferSize: 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 OUTPUT_W: 640OUTPUT_W, const OUTPUT_H: 480OUTPUT_H),
maxCanvasSize: Vector2imaxCanvasSize: 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 OUTPUT_W: 640OUTPUT_W, const OUTPUT_H: 480OUTPUT_H),
outputUpscaleFilter: stringoutputUpscaleFilter: 'nearest', targetFPS: numbertargetFPS: const TARGET_FPS: 60TARGET_FPS, // Phones and tablets dim, then lock, the screen after 30-60 seconds without a // touch - annoying mid-game when you are only pressing arrow keys or swiping // every few seconds. This asks the browser to keep the screen on while you // are playing. Browsers that do not support the request just ignore it, so // this is safe everywhere. isWakeLockEnabled: booleanisWakeLockEnabled: true, // Arrow keys normally scroll the page. This demo maps them to steer the // snake, so opt in so the browser does not scroll the demo page while you // play. isCapturingKeyboardScroll: booleanisCapturingKeyboardScroll: true, // Hide the small "~" toggle hint in the bottom-left corner so the game board // stays clean. Players who want the stats overlay can still press the // backquote key (`) to show it and press ` again to hide it - hiding the hint // does not turn the overlay off. isOverlayToggleHintVisible: booleanisOverlayToggleHintVisible: false, // The overlay bars and the timing chart borrow the scene's own palette slots // so the overlay matches the game's look.
overlayStyle: {
    barPaletteIndex: number;
    textPaletteIndex: number;
    gapPaletteIndex: number;
}
overlayStyle
: {
barPaletteIndex: numberbarPaletteIndex: const C_BG: 1C_BG, textPaletteIndex: numbertextPaletteIndex: const C_FOOTER_WHITE: 6C_FOOTER_WHITE, gapPaletteIndex: numbergapPaletteIndex: const C_BG: 1C_BG, }, isOverlayTimingChartEnabled: booleanisOverlayTimingChartEnabled: true,
overlayTimingChartStyle: {
    updateBarPaletteIndex: number;
    renderBarPaletteIndex: number;
    warningPaletteIndex: number;
    errorPaletteIndex: number;
    tagPaletteIndex: number;
}
overlayTimingChartStyle
: {
updateBarPaletteIndex: numberupdateBarPaletteIndex: const C_SNAKE: 3C_SNAKE, renderBarPaletteIndex: numberrenderBarPaletteIndex: const C_FOOD: 4C_FOOD, warningPaletteIndex: numberwarningPaletteIndex: const C_FOOTER_DIM: 5C_FOOTER_DIM, errorPaletteIndex: numbererrorPaletteIndex: const C_WALL: 2C_WALL, tagPaletteIndex: numbertagPaletteIndex: const C_FOOTER_WHITE: 6C_FOOTER_WHITE, }, }; } // Key maps, sounds, palette, the PipBoy CRT stack, the glitch machine, then the first // round. async Demo.init(): unknowninit() { // Start from the default keyboard maps so this demo stays independent from the // remapping demos.
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
.inputMapReset: () => void
Restores built-in default keyboard maps for players `0` and `1`. Uses the tables for the active `keyboardLayout` from `configure()`: `BT.DEFAULT_KEYBOARD_PLAYER1` / `BT.DEFAULT_KEYBOARD_PLAYER2` for `'versus'` (the default), `BT.DEFAULT_KEYBOARD_SINGLE_PLAYER1` / `BT.DEFAULT_KEYBOARD_SINGLE_PLAYER2` for `'single'`.
@since1.0.3@changed1.8.0 Restores the defaults of the active `HardwareSettings.keyboardLayout` instead of always the `'versus'` tables.
inputMapReset
();
// Engine defaults put WASD on player 0 and arrows on player 1. This is a // single-player game, so fold both layouts into player 0: either set of keys // steers the same snake. BT.inputMap replaces the whole key list for that button; // listing both codes means either key counts, the same pattern the Input Map // Remapping demo shows with Q and E for LEFT.
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
.inputMap: (player: number, button: number, ...keys: string[]) => void
Assigns one or more `KeyboardEvent.code` values to a face button for a keyboard player. Logical button state is the OR of all listed keys. Only players `0` and `1` support keyboard; other indices no-op. `button` must be one face-button bit flag (`BT.BTN_UP` … `BT.BTN_SELECT`). Pass an empty key list to clear keyboard bindings for that button until remapped again.
@since1.0.3@paramplayer - Zero-based player index (`0` or `1`).@parambutton - Face button constant.@paramkeys - DOM key codes (for example `'Space'`, `'KeyW'`).
inputMap
(0,
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.type BTN_UP: number
Up button bit flag.
@since0.1.0
BTN_UP
, 'KeyW', 'ArrowUp');
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
.inputMap: (player: number, button: number, ...keys: string[]) => void
Assigns one or more `KeyboardEvent.code` values to a face button for a keyboard player. Logical button state is the OR of all listed keys. Only players `0` and `1` support keyboard; other indices no-op. `button` must be one face-button bit flag (`BT.BTN_UP` … `BT.BTN_SELECT`). Pass an empty key list to clear keyboard bindings for that button until remapped again.
@since1.0.3@paramplayer - Zero-based player index (`0` or `1`).@parambutton - Face button constant.@paramkeys - DOM key codes (for example `'Space'`, `'KeyW'`).
inputMap
(0,
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.type BTN_DOWN: number
Down button bit flag.
@since0.1.0
BTN_DOWN
, 'KeyS', 'ArrowDown');
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
.inputMap: (player: number, button: number, ...keys: string[]) => void
Assigns one or more `KeyboardEvent.code` values to a face button for a keyboard player. Logical button state is the OR of all listed keys. Only players `0` and `1` support keyboard; other indices no-op. `button` must be one face-button bit flag (`BT.BTN_UP` … `BT.BTN_SELECT`). Pass an empty key list to clear keyboard bindings for that button until remapped again.
@since1.0.3@paramplayer - Zero-based player index (`0` or `1`).@parambutton - Face button constant.@paramkeys - DOM key codes (for example `'Space'`, `'KeyW'`).
inputMap
(0,
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.type BTN_LEFT: number
Left button bit flag.
@since0.1.0
BTN_LEFT
, 'KeyA', 'ArrowLeft');
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
.inputMap: (player: number, button: number, ...keys: string[]) => void
Assigns one or more `KeyboardEvent.code` values to a face button for a keyboard player. Logical button state is the OR of all listed keys. Only players `0` and `1` support keyboard; other indices no-op. `button` must be one face-button bit flag (`BT.BTN_UP` … `BT.BTN_SELECT`). Pass an empty key list to clear keyboard bindings for that button until remapped again.
@since1.0.3@paramplayer - Zero-based player index (`0` or `1`).@parambutton - Face button constant.@paramkeys - DOM key codes (for example `'Space'`, `'KeyW'`).
inputMap
(0,
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.type BTN_RIGHT: number
Right button bit flag.
@since0.1.0
BTN_RIGHT
, 'KeyD', 'ArrowRight');
// BT.synthPreset bundles ready-tuned sound recipes (Synth Toy explores all six). // Rendering them once here means eating and dying play back with zero delay // later. this.Demo.eatClip: AudioClip | null
@type{AudioClip | null}
eatClip
= 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
.pickup: (seed?: number) => SynthParams
Item pickup / coin: a short, bright square-wave blip that sweeps upward an octave. `seed` jitters the base frequency (+/-8%) and duration (+/-12%). Omit `seed` for a fixed baseline variant.
@paramseed - Seed for deterministic jitter. Defaults to {@link DEFAULT_PRESET_SEED }.@returnsA fresh `SynthParams` for a pickup sound effect.
pickup
());
this.Demo.gameOverClip: AudioClip | null
@type{AudioClip | null}
gameOverClip
= 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
.explosion: (seed?: number) => SynthParams
Explosion: a low sawtooth rumble mixed heavily with noise, with a slow decay and release for a boom that lingers. `seed` jitters the base frequency (+/-8%), duration (+/-12%), and `noiseMix` (+/-10%, clamped to `[0, 1]`) - the mix jitter alone gives every explosion a distinct noisy texture. Omit `seed` for a fixed baseline variant.
@paramseed - Seed for deterministic jitter. Defaults to {@link DEFAULT_PRESET_SEED }.@returnsA fresh `SynthParams` for an explosion sound effect.
explosion
());
// The background music file is loaded separately from the sound effects above, // and wrapped in its own try/catch. Unlike the effects (built on the spot by the // synth engine, so they cannot fail), this loads an actual audio file over the // network - if it is missing or the browser cannot decode it, we do not want that // one file to stop the whole game from starting. Catching the error here means // Snake stays fully playable with sound effects but no music, instead of getting // stuck on a blank screen. // // We only load here - we do not call BT.soundPlay yet. Browsers keep audio locked // until the first click or key press, and unlike BT.musicPlay (which remembers a // request while locked), BT.soundPlay would be dropped. update() starts the loop // the moment BT.isAudioUnlocked flips true. 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-upbeat.wav');
} catch (function (local var) error: unknownerror) { console.warn('Snake Game: failed to load background music, continuing without it.', function (local var) error: unknownerror); } 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);
// The six scene colors: dark blue-gray background, sand wall, green snake, red // food, and two footer text shades. this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const C_BG: 1C_BG, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32
Creates a clamped 8-bit RGBA color.
@paramr - Red channel (0-255, defaults to 255).@paramg - Green channel (0-255, defaults to 255).@paramb - Blue channel (0-255, defaults to 255).@parama - Alpha channel (0-255, defaults to 255 = opaque).
Color32
(25, 35, 45));
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_WALL: 2C_WALL, 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, 170, 140));
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_SNAKE: 3C_SNAKE, 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
(90, 220, 120));
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_FOOD: 4C_FOOD, 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
(240, 90, 70));
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_FOOTER_DIM: 5C_FOOTER_DIM, 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, 150));
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_FOOTER_WHITE: 6C_FOOTER_WHITE, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32
Creates a clamped 8-bit RGBA color.
@paramr - Red channel (0-255, defaults to 255).@paramg - Green channel (0-255, defaults to 255).@paramb - Blue channel (0-255, defaults to 255).@parama - Alpha channel (0-255, defaults to 255 = opaque).
Color32
(220, 230, 240));
// The shared UI kit draws the touch D-pad, and it needs its theme colors in the // palette. applyTheme() writes them into high slots (240 and up), far away from // the six scene colors above, so the game's look does not change at all. import applyThemeapplyTheme(this.Demo.palette: Palette
@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
.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
);
// CRT post-processing needs WebGPU. In software fallback mode we skip the whole // effect chain - the snake game itself still runs fine without it. this.Demo.effectsAvailable: booleaneffectsAvailable = import isAvailableisAvailable(); if (this.Demo.effectsAvailable: booleaneffectsAvailable) { this.Demo.setupCrtEffects(): voidsetupCrtEffects(); } this.Demo.startRound(): voidstartRound(); return true; } // Drives the CRT animation and glitch machine every tick; runs snake logic when // alive. Demo.update(): voidupdate() { // The UI kit's once-per-tick housekeeping: it tracks touch contacts for the D-pad // and watches for swipe gestures. Always the first line of update(). import uiui.tick(); // Start (or restart) the tempo-linked music loop once audio is unlocked. this.Demo.ensureBackgroundMusic(): voidensureBackgroundMusic(); // Did a swipe finish on this tick? ui.swipe() answers with a direction name // ('up', 'down', 'left', 'right') or null. We read it once and hand it to the // steering code below, next to the keyboard and D-pad checks. const const swipe: anyswipe = import uiui.swipe(); if (this.Demo.effectsAvailable: booleaneffectsAvailable) { this.Demo.tickCrtClock(): voidtickCrtClock(); this.Demo.tickGlitchMachine(): voidtickGlitchMachine(); } if (this.Demo.tickRestartAfterDeath(): booleantickRestartAfterDeath()) { return; } this.Demo.pollDirectionInput(swipe: any): voidpollDirectionInput(const swipe: anyswipe); // Count down to the next step; nothing else happens until the cooldown runs out. this.Demo.moveCooldown: numbermoveCooldown -= 1; if (this.Demo.moveCooldown: numbermoveCooldown > 0) { return; } // Use the live interval so food-driven speed-ups take effect on the next step. this.Demo.moveCooldown: numbermoveCooldown = this.Demo.moveInterval: numbermoveInterval; // Apply the player's pending direction, unless it is the exact opposite of the // current one - a snake cannot reverse into its own neck. if (!(this.Demo.pendingDx: numberpendingDx === -this.Demo.dx: numberdx && this.Demo.pendingDy: numberpendingDy === -this.Demo.dy: numberdy)) { this.Demo.dx: numberdx = this.Demo.pendingDx: numberpendingDx; this.Demo.dy: numberdy = this.Demo.pendingDy: numberpendingDy; } this.Demo.step(): voidstep(); } // Clears to the background, then draws the walls, the food, the snake segments, the // software-mode note, the audio hint, and the touch D-pad. 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_BG: 1C_BG);
this.Demo.renderWalls(): voidrenderWalls(); // (-1, -1) is the "board is full" sentinel from placeFood(); skip the dot then. if (this.
Demo.food: {
    x: number;
    y: number;
}
@type{{ x: number; y: number }}
food
.x: numberx >= 0 && this.
Demo.food: {
    x: number;
    y: number;
}
@type{{ x: number; y: number }}
food
.y: numbery >= 0) {
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.gridRect(gx: any, gy: any): Rect2igridRect(this.
Demo.food: {
    x: number;
    y: number;
}
@type{{ x: number; y: number }}
food
.x: numberx, this.
Demo.food: {
    x: number;
    y: number;
}
@type{{ x: number; y: number }}
food
.y: numbery), const C_FOOD: 4C_FOOD);
} // One filled cell per segment, head first. for (let let i: numberi = 0; let i: numberi < this.Demo.snake: {}
@type{{ x: number; y: number }[]}
snake
.length; let i: numberi++) {
const const seg: anyseg = this.Demo.snake: {}
@type{{ x: number; y: number }[]}
snake
[let i: numberi];
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.gridRect(gx: any, gy: any): Rect2igridRect(const seg: anyseg.x, const seg: anyseg.y), const C_SNAKE: 3C_SNAKE);
} if (!this.Demo.effectsAvailable: booleaneffectsAvailable) {
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.systemPrint: (pos: Vector2i, paletteIndex: number, text: string) => void
Draws text using the built-in 6x14 system font. The system font covers printable ASCII (characters 32-126). For custom bitmap fonts with proportional glyphs, use {@link BT.printFont } instead.
@since1.0.3@parampos - Text origin in display coordinates.@parampaletteIndex - Palette color index for the text.@paramtext - String to render.
systemPrint
(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 INNER_X0: 8INNER_X0, const DISPLAY_H: 120DISPLAY_H - 16), const C_FOOTER_DIM: 5C_FOOTER_DIM, import SOFTWARE_FALLBACK_NOTESOFTWARE_FALLBACK_NOTE);
} // Browsers keep all sound muted until the player clicks or presses a key. This // shared row shows a warm "enable sound" hint only while audio is still locked, // then disappears on its own the moment the first key press or tap unlocks it. // The default sentence is too long for this 160-wide playfield, so we pass a // short override. import uiui.begin(import UI_ANCHORSUI_ANCHORS.TOP_LEFT, { margin: numbermargin: 3 }); import uiui.audioUnlockHint({ text: stringtext: 'Click for sound' }); import uiui.end(); // The touch D-pad, drawn over the playfield in the bottom-right corner. It stays // invisible until the first touch contact, so mouse-and-keyboard players never // see it. This playfield is only 160x120 logical pixels, so the keys are scaled // down from the kit's phone-sized default. import uiui.dpadWidget({ size: numbersize: 22, gap: numbergap: 3, margin: numbermargin: 4 }); } // Builds the WebGPU CRT effect chain (same stack and tuning as the PipBoy CRT demo) // and seeds the glitch state machine. Called from init() only when WebGPU is active. Demo.setupCrtEffects(): voidsetupCrtEffects() { // Pixel-tier glitch: shifts horizontal bands of the index buffer before the // palette is resolved. It starts at zero intensity; the glitch machine turns it // up. this.Demo.pixelGlitch: PixelGlitch
@type{PixelGlitch}
pixelGlitch
= new new PixelGlitch(): PixelGlitch
Chunky pixel-aligned horizontal glitch: every Nth row of source pixels gets a random horizontal shift. Shifts snap to integer source-pixel offsets so palette indices move whole-texel (no RGB resampling). Pixel-tier: runs on the logical `r8uint` framebuffer (palette indices).
@since1.0.3
PixelGlitch
();
this.Demo.pixelGlitch: PixelGlitch
@type{PixelGlitch}
pixelGlitch
.PixelGlitch.bandHeight: number
Height of each glitch band in source pixels. Each band gets a single shift value, so larger bands produce chunkier glitches.
bandHeight
= import PIXEL_GLITCH_BAND_HEIGHTPIXEL_GLITCH_BAND_HEIGHT;
this.Demo.pixelGlitch: PixelGlitch
@type{PixelGlitch}
pixelGlitch
.PixelGlitch.intensity: number
Glitch strength in `[0, 1]`. Scales the per-band horizontal shift magnitude. Bands are shifted when their hash exceeds ~0.85 (~15% of bands). `0` disables.
intensity
= 0;
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
.effectAdd: (effect: Effect) => void
Appends a fullscreen post-processing effect to whichever chain matches its declared {@link Effect.tier } . - `tier='pixel'` -> pixel chain (logical resolution). - `tier='display'` -> display chain (output resolution); requires `drawingBufferSize` in effective hardware settings (`configure()` or `defaultConfig()`). Effects run in registration order within each tier. The pixel chain runs first, followed by the upscale pass, followed by the display chain. Each {@link Effect } instance owns its own GPU resources and may be mutated each frame from demo code.
@since1.0.3@parameffect - Effect instance to append. When the engine is not ready, shows a canvas error instead of throwing.
effectAdd
(this.Demo.pixelGlitch: PixelGlitch
@type{PixelGlitch}
pixelGlitch
);
// Display-tier effects, each with its resting value. The glitch machine raises // aberration, interference, noise, and flicker during a burst and resets them // after. this.Demo.barrel: BarrelDistortion
@type{BarrelDistortion}
barrel
= new new BarrelDistortion(): BarrelDistortion
Barrel distortion that warps UVs outward from the screen center. Display-tier: operates on the upscaled output. Applying this in the pixel tier (logical 320x240) discretizes the curve onto the source texel grid, which CSS upscale then magnifies into visible step artifacts. At output resolution the curve has enough resolution to express smoothly. The math comes from Timothy Lottes's public-domain `crt-lottes.glsl`: `warp(uv) = uv + delta * d2 * curvature` where `delta = uv - 0.5` and `d2 = dot(delta, delta)`.
@since1.0.3
BarrelDistortion
();
this.Demo.barrel: BarrelDistortion
@type{BarrelDistortion}
barrel
.BarrelDistortion.curvature: number
Curvature strength. Typical values: - `0.02` - subtle, like a flat-screen monitor. - `0.05` - moderate desktop CRT. - `0.10` - heavy small CRT or pocket TV.
curvature
= 0.2;
this.Demo.aberration: ChromaticAberration
@type{ChromaticAberration}
aberration
= new new ChromaticAberration(): ChromaticAberration
RGB channel offset that simulates lens chromatic aberration: red samples left of the fragment, blue samples right, green stays centered. Display-tier: spreads color along the lens axis. At logical resolution the single-pixel offset is too coarse and reads as a glitch instead of a soft fringe.
@since1.0.3
ChromaticAberration
();
this.Demo.aberration: ChromaticAberration
@type{ChromaticAberration}
aberration
.ChromaticAberration.aberration: number
Channel offset in display-chain (output) pixels. Reasonable values are `0.5` to `3.0`. Set to `0` to disable.
aberration
= import ABERRATION_BASEABERRATION_BASE;
this.Demo.interference: Interference
@type{Interference}
interference
= new new Interference(): Interference
Per-row horizontal jitter that simulates analog signal interference. Each output row gets a deterministic random horizontal offset seeded by row index and time. Row offsets are stable for one frame and re-seed every frame, producing a buzzing-noise feel. Display-tier. Drives jitter from {@link time } ; demos typically pass `BT.ticks / BT.targetFPS`.
@since1.0.3
Interference
();
this.Demo.interference: Interference
@type{Interference}
interference
.Interference.amount: number
Maximum horizontal offset as a UV fraction (e.g. `0.06` shifts the row by up to ~6% of the image width). Set to `0` to disable.
amount
= 0;
this.Demo.rollLine: RollLine
@type{RollLine}
rollLine
= new new RollLine(): RollLine
Slowly scrolling vertical interference band that brightens a horizontal stripe of the image. Combination of three cosines + smoothstep gives the stripe a soft top/bottom edge. Display-tier. Demo drives {@link time } (typically `BT.ticks / BT.targetFPS`).
@since1.0.3
RollLine
();
this.Demo.rollLine: RollLine
@type{RollLine}
rollLine
.RollLine.amount: number
Roll line amplitude (mix factor onto a brightness boost).
amount
= 0.1;
this.Demo.rollLine: RollLine
@type{RollLine}
rollLine
.RollLine.speed: number
Scroll speed multiplier; final scroll velocity = `time * speed`.
speed
= 1.0;
this.Demo.scanlines: Scanlines
@type{Scanlines}
scanlines
= new new Scanlines(): Scanlines
CRT scanlines: alternating bright/dark horizontal bands aligned to the source vertical resolution. Display-tier: at output resolution there is enough vertical pixels for scanlines to read as alternating bright/dark bands. At logical 320x240 the Gaussian weight quantizes to one of two values per source row and you lose the smooth fade.
@since1.0.3
Scanlines
();
this.Demo.scanlines: Scanlines
@type{Scanlines}
scanlines
.Scanlines.amount: number
Scanline mix amount in `[0, 1]`. 0 disables.
amount
= 0.55;
this.Demo.scanlines: Scanlines
@type{Scanlines}
scanlines
.Scanlines.strength: number
Negative gaussian falloff parameter for scanline brightness. More negative values produce sharper dark bands. PipBoy reference: `-8.0`.
strength
= -8;
this.Demo.scanlines: Scanlines
@type{Scanlines}
scanlines
.Scanlines.density: number
Number of scanline cycles vertically. Should match the demo's logical source vertical resolution so each "source pixel row" maps to one scanline cycle. Defaults to `240`, the most common pixel-art height. Set to e.g. `200` for VGA-style 320x200 games or `144` for Game Boy resolution.
density
= const DISPLAY_H: 120DISPLAY_H;
this.Demo.mask: RGBMask
@type{RGBMask}
mask
= new new RGBMask(): RGBMask
CRT shadow mask: per-pixel R/G/B vertical-stripe pattern with darkened cell borders, simulating the phosphor grille of an aperture-grille CRT. Display-tier: at output resolution there are enough output pixels per mask cell to read as colored stripes. The cell pitch (in output pixels) is the {@link size } parameter. Math is a direct WGSL port of the libretro `crt-lottes.glsl` mask code.
@since1.0.3
RGBMask
();
this.Demo.mask: RGBMask
@type{RGBMask}
mask
.RGBMask.intensity: number
Mask brightness mix amount in `[0, 1]`. 0 hides the mask.
intensity
= 0.18;
this.Demo.mask: RGBMask
@type{RGBMask}
mask
.RGBMask.size: number
Mask cell pitch in output (display-chain) pixels. Smaller = denser mask.
size
= 6;
this.Demo.mask: RGBMask
@type{RGBMask}
mask
.RGBMask.border: number
Border darkening within each mask cell. 0 disables, 1 strong.
border
= 0.5;
this.Demo.vignette: Vignette
@type{Vignette}
vignette
= new new Vignette(): Vignette
Edge-darkening vignette: smooth radial fade from full brightness at the center to black at the corners. Display-tier: applies to the whole simulated screen, not the underlying pixel art.
@since1.0.3
Vignette
();
this.Demo.vignette: Vignette
@type{Vignette}
vignette
.Vignette.amount: number
Vignette darkening exponent. Higher values produce a stronger vignette with a sharper falloff. PipBoy reference: `0.2`. Set to `0` to disable.
amount
= 0.35;
this.Demo.noise: Noise
@type{Noise}
noise
= new new Noise(): Noise
Additive per-pixel pseudo-random noise. Reseeds each frame from {@link time } so the noise pattern animates. Display-tier.
@since1.0.3
Noise
();
this.Demo.noise: Noise
@type{Noise}
noise
.Noise.amount: number
Noise amplitude as a `[-amount, +amount]` additive perturbation on each channel. Reasonable values are `0.005` to `0.05`. Set to `0` to disable.
amount
= import NOISE_BASENOISE_BASE;
this.Demo.flicker: Flicker
@type{Flicker}
flicker
= new new Flicker(): Flicker
Brightness multiplier - the simplest CRT animation knob. Demos drive {@link amount } per frame to simulate flicker (e.g. with `0.95 + sin(t) * 0.05`). The effect is intentionally trivial so the demo controls the pattern; for procedural noise-driven flicker, combine with the {@link Noise } effect. Display-tier.
@since1.0.3
Flicker
();
this.Demo.flicker: Flicker
@type{Flicker}
flicker
.Flicker.amount: number
Brightness multiplier. `1` is unmodulated; values below `1` darken the frame. The demo typically updates this each frame from a sin wave or random source.
amount
= import FLICKER_BASEFLICKER_BASE;
this.Demo.bloom: Bloom
@type{Bloom}
bloom
= new new Bloom(): Bloom
Single-pass box-blur bloom. Samples a 5x5 neighborhood (25 taps) around each fragment, averages, then mixes with the original color by {@link glow } . {@link spread } scales the texel offset so the bloom radius can be tuned independently of the source resolution. Display-tier: bloom mixes neighboring pixels into intermediate hues that are not in the active palette. Running it in pixel space would violate the palette-pixel aesthetic; running it on the upscaled output reads as the warm phosphor glow of an old monitor instead. The implementation matches the original PipBoy bloom shader. A future optimization would be a two-pass separable Gaussian (5 + 5 = 10 taps); add it once a GPU perf test demands it.
@since1.0.3
Bloom
();
this.Demo.bloom: Bloom
@type{Bloom}
bloom
.Bloom.spread: number
Texel offset multiplier for the box-blur kernel.
spread
= 3.0;
this.Demo.bloom: Bloom
@type{Bloom}
bloom
.Bloom.glow: number
Mix factor between the original sample and the blurred neighborhood.
glow
= 0.18;
// Register them in this order; the chain runs top to bottom. for (const const fx: anyfx of [ this.Demo.barrel: BarrelDistortion
@type{BarrelDistortion}
barrel
,
this.Demo.aberration: ChromaticAberration
@type{ChromaticAberration}
aberration
,
this.Demo.interference: Interference
@type{Interference}
interference
,
this.Demo.rollLine: RollLine
@type{RollLine}
rollLine
,
this.Demo.scanlines: Scanlines
@type{Scanlines}
scanlines
,
this.Demo.mask: RGBMask
@type{RGBMask}
mask
,
this.Demo.vignette: Vignette
@type{Vignette}
vignette
,
this.Demo.noise: Noise
@type{Noise}
noise
,
this.Demo.flicker: Flicker
@type{Flicker}
flicker
,
this.Demo.bloom: Bloom
@type{Bloom}
bloom
,
]) {
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
.effectAdd: (effect: Effect) => void
Appends a fullscreen post-processing effect to whichever chain matches its declared {@link Effect.tier } . - `tier='pixel'` -> pixel chain (logical resolution). - `tier='display'` -> display chain (output resolution); requires `drawingBufferSize` in effective hardware settings (`configure()` or `defaultConfig()`). Effects run in registration order within each tier. The pixel chain runs first, followed by the upscale pass, followed by the display chain. Each {@link Effect } instance owns its own GPU resources and may be mutated each frame from demo code.
@since1.0.3@parameffect - Effect instance to append. When the engine is not ready, shows a canvas error instead of throwing.
effectAdd
(const fx: anyfx);
} // BT.random is the engine's shared random number generator. Its int() method // returns a whole number from the first value up to (but not including) the // second, so this picks how many ticks to wait before the first glitch burst. this.Demo.glitchCooldown: numberglitchCooldown =
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
(import GLITCH_COOLDOWN_MINGLITCH_COOLDOWN_MIN, import GLITCH_COOLDOWN_MAXGLITCH_COOLDOWN_MAX);
this.Demo.glitchTicksLeft: numberglitchTicksLeft = 0; this.Demo.glitchDuration: numberglitchDuration = 0; this.Demo.glitchType: string
@type{string}
glitchType
= 'none';
this.Demo.glitchPeak: numberglitchPeak = 0; } // While game over, waits for the restart delay and then starts a new round. Returns // true when the snake should not move this tick. Demo.tickRestartAfterDeath(): booleantickRestartAfterDeath() { if (!this.Demo.gameOver: booleangameOver) { return false; } 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
;
if (this.Demo.deathTick: number | null
@type{number | null}
deathTick
!== null && const tick: numbertick - this.Demo.deathTick: number
@type{number | null}
deathTick
>= const RESTART_DELAY_TICKS: 120RESTART_DELAY_TICKS) {
this.Demo.startRound(): voidstartRound(); } return true; } // Reads all three steering inputs and updates the pending direction (no instant // reverse): player 0 face buttons (WASD, arrows, or gamepad), the on-screen touch // D-pad, and swipes. We use BT.isPressed (edge: up -> down this tick), not BT.isDown // (held every tick), and the D-pad's matching ui.dpad.isPressed. That way one tap per // direction per move interval feels like a classic D-pad. The `this.dy !== 1` checks // block a 180-degree turn: you cannot go straight back into your body on the next // step. `swipe` is the swipe finished this tick, if any (from ui.swipe() in // update()). Demo.pollDirectionInput(swipe: any): voidpollDirectionInput(swipe: anyswipe) { // Up: only if we are not currently moving down (would reverse into the tail). if (this.Demo.isSteerPressed(button: any, dir: any, swipe: any): anyisSteerPressed(
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.type BTN_UP: number
Up button bit flag.
@since0.1.0
BTN_UP
, 'up', swipe: anyswipe) && this.Demo.dy: numberdy !== 1) {
this.Demo.pendingDx: numberpendingDx = 0; this.Demo.pendingDy: numberpendingDy = -1; } // Down: only if we are not currently moving up. if (this.Demo.isSteerPressed(button: any, dir: any, swipe: any): anyisSteerPressed(
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.type BTN_DOWN: number
Down button bit flag.
@since0.1.0
BTN_DOWN
, 'down', swipe: anyswipe) && this.Demo.dy: numberdy !== -1) {
this.Demo.pendingDx: numberpendingDx = 0; this.Demo.pendingDy: numberpendingDy = 1; } // Left: only if we are not currently moving right. if (this.Demo.isSteerPressed(button: any, dir: any, swipe: any): anyisSteerPressed(
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.type BTN_LEFT: number
Left button bit flag.
@since0.1.0
BTN_LEFT
, 'left', swipe: anyswipe) && this.Demo.dx: numberdx !== 1) {
this.Demo.pendingDx: numberpendingDx = -1; this.Demo.pendingDy: numberpendingDy = 0; } // Right: only if we are not currently moving left. if (this.Demo.isSteerPressed(button: any, dir: any, swipe: any): anyisSteerPressed(
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.type BTN_RIGHT: number
Right button bit flag.
@since0.1.0
BTN_RIGHT
, 'right', swipe: anyswipe) && this.Demo.dx: numberdx !== -1) {
this.Demo.pendingDx: numberpendingDx = 1; this.Demo.pendingDy: numberpendingDy = 0; } } // Combines the three ways to steer in one direction: the engine face button (keyboard // or gamepad), the on-screen touch D-pad key, and a swipe that way. `button` is the // face button mask (BT.BTN_UP and friends), `dir` the D-pad and swipe direction name. // Returns true when any of the three pressed that direction this tick. Demo.isSteerPressed(button: any, dir: any, swipe: any): anyisSteerPressed(button: anybutton, dir: anydir, swipe: anyswipe) { return
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
.isPressed: (button: number, player?: number, repeatRate?: number) => boolean
Checks whether a button was pressed on the current frame. Same parameter semantics as {@link isDown } ; returns `true` only on the frame the button transitions from up to down. Call from `update()`, not `render()`, for reliable detection: for keyboard-mapped face buttons (players 0 and 1), the press edge clears once per fixed-update tick, which always runs before that frame's `render()`. `BTN_L2`, `BTN_R2` and `BTN_TRIGGER` are gamepad only (no keyboard mapping).
@since1.1.1@changed1.8.0 Also accepts BTN_L2 / BTN_R2 / BTN_TRIGGER (gamepad only).@parambutton - Button constant from the `BTN_*` set.@paramplayer - Zero-based player index for gamepads, or pointer slot (0-3) for `BTN_POINTER_*`.@paramrepeatRate - Optional repeat interval in fixed ticks (`0`/omitted = edge only).@returns`true` on the transition frame.
isPressed
(button: anybutton, 0) || import uiui.dpad.isPressed(dir: anydir) || swipe: anyswipe === dir: anydir;
} // Updates the time-driven values for the roll line, noise, and interference effects. Demo.tickCrtClock(): voidtickCrtClock() { const const seconds: numberseconds =
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 TARGET_FPS: 60TARGET_FPS;
this.Demo.rollLine: RollLine
@type{RollLine}
rollLine
.RollLine.time: number
Seconds; demos drive this each frame. Use BT.timeSeconds so BT.renderAt can seek it.
time
= const seconds: numberseconds;
this.Demo.noise: Noise
@type{Noise}
noise
.Noise.time: number
Seconds; reseeds the noise each frame. Drive it from BT.timeSeconds so BT.renderAt can seek it.
time
= const seconds: numberseconds;
this.Demo.interference: Interference
@type{Interference}
interference
.Interference.time: number
Seconds; reseeds the row offsets each frame. Drive it from BT.timeSeconds so BT.renderAt can seek it.
time
= const seconds: numberseconds;
} // Same state machine as the PipBoy CRT demo: cooldown, burst envelope, random glitch // type. Demo.tickGlitchMachine(): voidtickGlitchMachine() { // A burst is running while there are ticks left on its countdown. if (this.Demo.glitchTicksLeft: numberglitchTicksLeft > 0) { // `t` runs from 0 at the start of the burst to 1 at its end; sin(t * PI) // turns that into a hump that rises, peaks in the middle, and falls back to // zero. const const t: numbert = 1 - this.Demo.glitchTicksLeft: numberglitchTicksLeft / this.Demo.glitchDuration: numberglitchDuration; const const envelope: anyenvelope = Math.sin(const t: numbert * Math.PI); import applyGlitchUniformsapplyGlitchUniforms(this, const envelope: anyenvelope); this.Demo.glitchTicksLeft: numberglitchTicksLeft--; // Countdown just hit zero: the burst is over, so calm the effects down and // roll a fresh cooldown until the next burst. if (this.Demo.glitchTicksLeft: numberglitchTicksLeft === 0) { import resetGlitchUniformsresetGlitchUniforms(this); this.Demo.glitchCooldown: numberglitchCooldown =
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
(import GLITCH_COOLDOWN_MINGLITCH_COOLDOWN_MIN, import GLITCH_COOLDOWN_MAXGLITCH_COOLDOWN_MAX);
} return; } this.Demo.glitchCooldown: numberglitchCooldown--; if (this.Demo.glitchCooldown: numberglitchCooldown <= 0) { // pick() draws one item out of a list, like taking a card off the top of a // shuffled deck. float() is the decimal cousin of int(), for values that are // not whole numbers. this.Demo.glitchType: string
@type{string}
glitchType
=
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.pick<string>(arr: readonly string[]): string
Returns one element chosen uniformly from a non-empty array.
@paramarr - Array to pick from; must contain at least one element.@returnsChosen element.@since1.5.0
pick
(import GLITCH_TYPES_CHROMAGLITCH_TYPES_CHROMA);
this.Demo.glitchDuration: numberglitchDuration =
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
(import GLITCH_ACTIVE_MINGLITCH_ACTIVE_MIN, import GLITCH_ACTIVE_MAXGLITCH_ACTIVE_MAX);
this.Demo.glitchTicksLeft: numberglitchTicksLeft = this.Demo.glitchDuration: numberglitchDuration; this.Demo.glitchPeak: numberglitchPeak =
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.float(min: number, max: number): number
Returns the next pseudo-random float in [min, max).
@parammin - Inclusive lower bound.@parammax - Exclusive upper bound.@returnsFloat in [min, max).@since1.5.0
float
(import GLITCH_INTENSITY_MINGLITCH_INTENSITY_MIN, import GLITCH_INTENSITY_MAXGLITCH_INTENSITY_MAX);
this.Demo.pixelGlitch: PixelGlitch
@type{PixelGlitch}
pixelGlitch
.PixelGlitch.seed: number
Per-glitch random seed. Change between glitches to vary the band noise pattern.
seed
=
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.float(min: number, max: number): number
Returns the next pseudo-random float in [min, max).
@parammin - Inclusive lower bound.@parammax - Exclusive upper bound.@returnsFloat in [min, max).@since1.5.0
float
(0, 1000);
} } // Pixel rectangle for one grid cell at (gx, gy), inside the inner playfield. Demo.gridRect(gx: any, gy: any): Rect2igridRect(gx: anygx, gy: anygy) { const const px: numberpx = const INNER_X0: 8INNER_X0 + gx: anygx * const CELL: 8CELL; const const py: numberpy = const INNER_Y0: 8INNER_Y0 + gy: anygy * const CELL: 8CELL; return 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
(const px: numberpx, const py: numberpy, const CELL: 8CELL, const CELL: 8CELL);
} // Four filled bars form the boundary the snake must not cross. Demo.renderWalls(): voidrenderWalls() {
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
(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, const DISPLAY_W: 160DISPLAY_W, const WALL: 8WALL), const C_WALL: 2C_WALL);
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
(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, const DISPLAY_H: 120DISPLAY_H - const WALL: 8WALL, const DISPLAY_W: 160DISPLAY_W, const WALL: 8WALL), const C_WALL: 2C_WALL);
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
(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, const WALL: 8WALL, const WALL: 8WALL, const DISPLAY_H: 120DISPLAY_H - 2 * const WALL: 8WALL), const C_WALL: 2C_WALL);
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
(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
(const DISPLAY_W: 160DISPLAY_W - const WALL: 8WALL, const WALL: 8WALL, const WALL: 8WALL, const DISPLAY_H: 120DISPLAY_H - 2 * const WALL: 8WALL), const C_WALL: 2C_WALL);
} // Playback rate for the background loop from the starting pitch and how many times // the snake has grown this round. One food is start * scale, two foods is // start * scale * scale, and so on; zero growths leaves the rate at the start pitch. // Growth above MUSIC_PITCH_GROWTH_CAP no longer raises the rate (matches top speed). // Returns the rate to pass to BT.soundPlay or BT.soundPitchSet. Demo.currentMusicPitch(): numbercurrentMusicPitch() { // The ** operator is "to the power of": a ** b means a multiplied by itself b // times. growthCount 0 gives 1, so the rate stays at MUSIC_PITCH_AT_START on a // fresh round. Cap at MUSIC_PITCH_GROWTH_CAP so pitch plateaus with moveInterval // at top speed. const const growthForPitch: anygrowthForPitch = Math.min(this.Demo.growthCount: numbergrowthCount, const MUSIC_PITCH_GROWTH_CAP: numberMUSIC_PITCH_GROWTH_CAP); return const MUSIC_PITCH_AT_START: 0.57MUSIC_PITCH_AT_START * const MUSIC_PITCH_SCALE_PER_GROWTH: 1.03MUSIC_PITCH_SCALE_PER_GROWTH ** const growthForPitch: anygrowthForPitch; } // Starts the looping music voice once audio is unlocked, or restarts it if the voice // was somehow lost. Safe to call every tick - it does nothing while already playing. // Why BT.soundPlay instead of BT.musicPlay: only the SFX path exposes pitch (playback // rate), which is how we keep the beat tied to snake growth. The tradeoff is that // soundPlay is dropped before unlock, so we wait for BT.isAudioUnlocked here. Demo.ensureBackgroundMusic(): voidensureBackgroundMusic() { if (this.Demo.musicClip: AudioClip | null
@type{AudioClip | null}
musicClip
=== null || !
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
.isAudioUnlocked: boolean
Whether the audio context has been unlocked by a user gesture. Browsers require a user gesture (pointer, key, or touch press) before allowing audio playback. Starts `false`; flips to `true` for the rest of the session after the first gesture successfully resumes the audio context.
@since1.3.0@returns`true` once unlocked; `false` when locked or before initialization.
isAudioUnlocked
) {
return; } // Already have a live looping voice - nothing to do. if (this.Demo.musicRef: SoundRef | null
@type{import('blit386').SoundRef | null}
musicRef
!== null &&
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
.isSoundPlaying: (ref: SoundRef) => boolean
Reports whether a sound is still playing.
@since1.3.0@paramref - Sound to query.@returns`true` when still playing; `false` once it has stopped, been stolen, or completed.
isSoundPlaying
(this.Demo.musicRef: SoundRef
@type{import('blit386').SoundRef | null}
musicRef
)) {
return; } // Start (or restart) at the pitch for the current growth count. this.Demo.musicRef: SoundRef | null
@type{import('blit386').SoundRef | null}
musicRef
=
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.musicClip: AudioClip
@type{AudioClip | null}
musicClip
, {
VoicePlayOptions.loop?: boolean | undefined
Whether the buffer loops. Defaults to `false`.
loop
: true,
VoicePlayOptions.volume?: number | undefined
Initial gain in `[0, 1]` (unclamped). Defaults to `1`.
volume
: 0.65,
VoicePlayOptions.pitch?: number | undefined
Initial `playbackRate`. Defaults to `1`.
pitch
: this.Demo.currentMusicPitch(): numbercurrentMusicPitch(),
VoicePlayOptions.priority?: number | undefined
Allocation priority; higher survives stealing longer. Defaults to `0`.
priority
: const MUSIC_VOICE_PRIORITY: 100MUSIC_VOICE_PRIORITY,
}); } // Glides the live music loop to the playback rate for the current growth count. Does // nothing before the loop has started (still locked, or music failed to load). Demo.syncMusicTempo(): voidsyncMusicTempo() { if (this.Demo.musicRef: SoundRef | null
@type{import('blit386').SoundRef | null}
musicRef
=== null || !
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
.isSoundPlaying: (ref: SoundRef) => boolean
Reports whether a sound is still playing.
@since1.3.0@paramref - Sound to query.@returns`true` when still playing; `false` once it has stopped, been stolen, or completed.
isSoundPlaying
(this.Demo.musicRef: SoundRef
@type{import('blit386').SoundRef | null}
musicRef
)) {
return; }
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
.soundPitchSet: (ref: SoundRef, value: number, options?: SoundParamSetOptions) => void
Sets a sound's playback rate, optionally fading to it.
@since1.3.0@paramref - Sound to update.@paramvalue - Target playback rate.@paramoptions - Optional fade behavior.@paramoptions.fadeMs - Fade duration in milliseconds. Omit for an immediate change.
soundPitchSet
(this.Demo.musicRef: SoundRef
@type{import('blit386').SoundRef | null}
musicRef
, this.Demo.currentMusicPitch(): numbercurrentMusicPitch(), {
SoundParamSetOptions.fadeMs?: number | undefined
Fade duration in milliseconds. Omit for an immediate change.
fadeMs
: const MUSIC_PITCH_FADE_MS: 120MUSIC_PITCH_FADE_MS,
}); } // Places a short snake in the middle, resets speed to the slow start, and spawns the // first food dot. Demo.startRound(): voidstartRound() { this.Demo.gameOver: booleangameOver = false; this.Demo.deathTick: number | null
@type{number | null}
deathTick
= null;
// Every new round begins at the slow pace; eating food will speed things up // again. this.Demo.moveInterval: numbermoveInterval = const MOVE_INTERVAL_START: 20MOVE_INTERVAL_START; this.Demo.moveCooldown: numbermoveCooldown = this.Demo.moveInterval: numbermoveInterval; // Zero foods eaten - music returns to MUSIC_PITCH_AT_START for the new round. this.Demo.growthCount: numbergrowthCount = 0; this.Demo.syncMusicTempo(): voidsyncMusicTempo(); // Three cells in the middle row, head on the right, heading right. const const midX: anymidX = Math.floor(const CELLS_X: numberCELLS_X / 2); const const midY: anymidY = Math.floor(const CELLS_Y: numberCELLS_Y / 2); this.Demo.snake: {}
@type{{ x: number; y: number }[]}
snake
= [
{ x: anyx: const midX: anymidX, y: anyy: const midY: anymidY }, { x: numberx: const midX: anymidX - 1, y: anyy: const midY: anymidY }, { x: numberx: const midX: anymidX - 2, y: anyy: const midY: anymidY }, ]; this.Demo.dx: numberdx = 1; this.Demo.dy: numberdy = 0; this.Demo.pendingDx: numberpendingDx = 1; this.Demo.pendingDy: numberpendingDy = 0; this.Demo.placeFood(): voidplaceFood();
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
.assignTag: (label?: string) => void
Places a labeled marker on the overlay timing chart at the current tick. Requires `isOverlayTimingChartEnabled: true` in `configure()`. Tags scroll with the chart history and are pruned when they leave the visible window. Empty labels become `"Untitled"`. Chart width resets add an automatic `"Start"` tag.
@since1.1.0@paramlabel - Short event name (for example `'Round start'`).
assignTag
('Round start');
} // Picks a random empty grid cell for food. Demo.placeFood(): voidplaceFood() { // Collect every cell the snake covers as "x,y" text keys, so a quick Set lookup // tells us whether a candidate cell is free. const const occupied: anyoccupied = new Set(); for (let let i: numberi = 0; let i: numberi < this.Demo.snake: {}
@type{{ x: number; y: number }[]}
snake
.length; let i: numberi++) {
const const s: anys = this.Demo.snake: {}
@type{{ x: number; y: number }[]}
snake
[let i: numberi];
const occupied: anyoccupied.add(`${const s: anys.x},${const s: anys.y}`); } for (let let attempt: numberattempt = 0; let attempt: numberattempt < 4000; let attempt: numberattempt++) { // int() with a single argument counts from 0, so int(CELLS_X) lands on any // column from 0 to CELLS_X - 1 - exactly the valid grid positions. const const x: numberx =
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
(const CELLS_X: numberCELLS_X);
const const y: numbery =
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 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
(const CELLS_Y: numberCELLS_Y);
const const key: stringkey = `${const x: numberx},${const y: numbery}`; if (!const occupied: anyoccupied.has(const key: stringkey)) { this.
Demo.food: {
    x: number;
    y: number;
}
@type{{ x: number; y: number }}
food
= { x: numberx, y: numbery };
return; } } // Board full - no free cell found. (-1, -1) is a sentinel that render() sees and // skips drawing the food dot. this.
Demo.food: {
    x: number;
    y: number;
}
@type{{ x: number; y: number }}
food
= { x: numberx: -1, y: numbery: -1 };
} // One grid step: wall check, self check, grow or shift the tail. Demo.step(): voidstep() { const const head: anyhead = this.Demo.snake: {}
@type{{ x: number; y: number }[]}
snake
[0];
const const nx: anynx = const head: anyhead.x + this.Demo.dx: numberdx; const const ny: anyny = const head: anyhead.y + this.Demo.dy: numberdy; // Off the grid means into the wall. if (const nx: anynx < 0 || const nx: anynx >= const CELLS_X: numberCELLS_X || const ny: anyny < 0 || const ny: anyny >= const CELLS_Y: numberCELLS_Y) { this.Demo.endGame(): voidendGame(); return; } const const eating: booleaneating = const nx: anynx === this.
Demo.food: {
    x: number;
    y: number;
}
@type{{ x: number; y: number }}
food
.x: numberx && const ny: anyny === this.
Demo.food: {
    x: number;
    y: number;
}
@type{{ x: number; y: number }}
food
.y: numbery;
// Self check. The tail cell is about to move away unless we are eating, so it // does not count as a collision in that case. const const limit: anylimit = const eating: booleaneating ? this.Demo.snake: {}
@type{{ x: number; y: number }[]}
snake
.length : this.Demo.snake: {}
@type{{ x: number; y: number }[]}
snake
.length - 1;
for (let let i: numberi = 0; let i: numberi < const limit: anylimit; let i: numberi++) { const const s: anys = this.Demo.snake: {}
@type{{ x: number; y: number }[]}
snake
[let i: numberi];
if (const s: anys.x === const nx: anynx && const s: anys.y === const ny: anyny) { this.Demo.endGame(): voidendGame(); return; } } // Add the new head cell at the front of the list. this.Demo.snake: {}
@type{{ x: number; y: number }[]}
snake
.unshift({ x: anyx: const nx: anynx, y: anyy: const ny: anyny });
if (const eating: booleaneating) {
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.eatClip: AudioClip | null
@type{AudioClip | null}
eatClip
);
// Shorter wait between steps = faster snake. Clamp so it never goes below // MOVE_INTERVAL_MIN; Math.max picks the larger of the floor and the // stepped-down value, which is how we stop the interval from dropping into // the negatives. this.Demo.moveInterval: numbermoveInterval = Math.max(const MOVE_INTERVAL_MIN: 4MOVE_INTERVAL_MIN, this.Demo.moveInterval: numbermoveInterval - const MOVE_INTERVAL_STEP: 1MOVE_INTERVAL_STEP); // Keep counting every food; currentMusicPitch() caps the exponent at // MUSIC_PITCH_GROWTH_CAP so tempo stops climbing once speed has already maxed // out. this.Demo.growthCount: numbergrowthCount += 1; this.Demo.syncMusicTempo(): voidsyncMusicTempo(); this.Demo.placeFood(): voidplaceFood(); } else { // Not eating: drop the tail cell so the snake keeps its length. this.Demo.snake: {}
@type{{ x: number; y: number }[]}
snake
.pop();
} } // Freezes movement and remembers when to restart. Demo.endGame(): voidendGame() { this.Demo.gameOver: booleangameOver = true; this.Demo.deathTick: number | null
@type{number | null}
deathTick
=
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 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.gameOverClip: AudioClip | null
@type{AudioClip | null}
gameOverClip
);
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
.assignTag: (label?: string) => void
Places a labeled marker on the overlay timing chart at the current tick. Requires `isOverlayTimingChartEnabled: true` in `configure()`. Tags scroll with the chart history and are pruned when they leave the visible window. Empty labels become `"Untitled"`. Chart width resets add an automatic `"Start"` tag.
@since1.1.0@paramlabel - Short event name (for example `'Round start'`).
assignTag
('Game over');
} } 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);