/*

Keyboard Input: face buttons, raw keys, and typed text.
@description Face buttons for two players, raw key state with optional tick repeat, and text from BT.inputString.

Part of the BLIT386 demo series.

Prerequisites:
  - Basics: https://demos.blit386.dev/basics
  - Pointer Basics: https://demos.blit386.dev/pointer-basics

Live version: https://demos.blit386.dev/keyboard-input

Guides:
  - Input: https://blit386.dev/docs/guides/input#keyboard
  - Input: https://blit386.dev/docs/guides/input#frame-timing-semantics

---

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

A title strip across the top. On the left, two panels sit side by side like two little
gamepads: "Player 0" and "Player 1", each with the same eight face buttons in the same
order - a pip that fills while the button is held, its short name, and on the right the
key it is mapped to (arrow characters for player 1's arrow keys). Player 1 has no
Select key by default, so its last row shows a dash and never lights.

To the right of them is a column of two panels, ending level with the gamepads.
"isKeyPressed counter" shows which key you tapped last and how many times in a row. "Raw
keys" has a pip for Q, the engine tick of the last F release, a pip that lights while any
key at all is held, and the tick when the last key came up. Along the bottom, a wide
panel shows the last 48 characters you typed (on a touch screen it says "This demo needs
a keyboard" instead).

Every panel and pip is drawn with the shared UI kit (src/shared/ui.js). The inputs stay
deliberately keyboard-only: there is no touch stand-in for a physical keyboard.

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

- Face buttons BT.BTN_UP through BT.BTN_SELECT, read with BT.isDown(button, player) for
  players 0 and 1. Each button maps to one or more physical keys through the keyboard
  map; the defaults live in BT.DEFAULT_KEYBOARD_PLAYER1 and BT.DEFAULT_KEYBOARD_PLAYER2,
  and BT.inputMapReset() restores them.
- Raw keys by KeyboardEvent.code string: BT.isKeyDown(code) for held state,
  BT.isKeyPressed(code) for the press edge, BT.isKeyPressed(code, repeatTicks) for a
  fixed-tick repeat, and BT.isKeyReleased(code) for the release edge.
- Whole-keyboard checks: BT.isAnyKeyDown() and BT.isAnyKeyReleased().
- BT.inputString, the characters typed since the last update tick.
- Edges (pressed, released, inputString) are read in update(); held state is safe to
  read in render().
- isCapturingKeyboardScroll keeps arrow keys and Space from scrolling the page.

THREE LAYERS OF KEYBOARD SUPPORT
--------------------------------

- Face buttons (BT.BTN_UP through BT.BTN_SELECT) for players 0 and 1. Each button can map
  to one or more physical keys. The engine uses KeyboardEvent.code strings (like KeyW,
  ArrowUp) so labels stay the same even when the OS keyboard layout changes (unlike
  event.key, which might print different letters).
- Raw keys (BT.isKeyDown, BT.isKeyPressed, BT.isKeyReleased) when you need a specific key,
  optional fixed-tick repeats with BT.isKeyPressed(code, rate), and release edges. When
  you do NOT care which key, BT.isAnyKeyDown and BT.isAnyKeyReleased ask about the whole
  keyboard instead.
- Text input (BT.inputString) for characters typed since the last tick. The buffer clears
  once per fixed-update tick, and that tick always finishes before render() runs, so read
  it in update(), not render(), or you can miss characters.

TRY THIS
--------

- Hold W, A, S, D, Space (or B), N, 5, and Esc for player 0; the arrow keys, ; (or
  Numpad 1), ' (or Numpad 2), and Backspace (or Numpad /) for player 1. Each panel shows
  the first key of every button's mapping.
- Tap the same letter repeatedly and watch the press counter climb (edge-only
  BT.isKeyPressed, no repeat rate). Tap a different key and the counter resets to 1. The
  counter listens to letters, Space, and the arrow keys.
- Hold Q to see isKeyDown; tap F and watch the "F rel" readout.
- Hold two keys, then let go of one: "Any key held" stays lit. Let go of the last one and
  the "All up" tick updates - isAnyKeyReleased means EVERYTHING is up.
- Type letters into the buffer line at the bottom.
- If keys stop responding, click the canvas - focus may have moved to another part of the
  page after you tabbed away.

*/

import { function bootstrap(DemoClass: DemoConstructor, options?: BootstrapOptions): Promise<boolean>
One-liner bootstrap function for BLIT386 demos. Handles canvas retrieval and engine initialization. Backend selection (WebGPU or software fallback) is managed internally by BTAPI. This function provides a streamlined way to start a demo with sensible defaults while allowing customization through options.
@since0.2.0@changed1.4.0 Calling `bootstrap()` again while already initialized now routes to a hot swap (via {@link registerHotReload}) when a Vite HMR context is registered, or logs a double-bootstrap guard and returns `false` otherwise - previously it silently started a second, unstoppable `GameLoop`.@changed1.7.0 Exposes `BT` on `window.BT` after bootstrap finishes, gated by {@link BootstrapOptions.exposeGlobal} (default: {@link BT.isDevMode}).@paramDemoClass - Demo class constructor implementing `IBTDemo` (optional `configure()` for hardware settings).@paramoptions - Optional configuration for IDs and callbacks.@returns`true` when the demo boots successfully; otherwise `false`.@example// Simplest usage - uses default IDs. bootstrap(MyDemo);@example// With custom options. bootstrap(MyDemo, { canvasID: 'custom-canvas', containerID: 'custom-container', onSuccess: () => console.log('Demo started!'), onError: (err) => analytics.trackError(err), });@example// Await the result. const success = await bootstrap(MyDemo); if (success) { console.log('Demo is running'); }
bootstrap
,
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
} from 'blit386';
import { import keyLabelkeyLabel } from './shared/key-names.js'; import { import applyThemeapplyTheme, import FONT_WFONT_W, import uiui, import UI_ANCHORSUI_ANCHORS } from './shared/ui.js'; /** @typedef {import('blit386').Palette} Palette */ // How many characters the typed-text line keeps (a rolling window). The line lives in a // 308-pixel-wide panel and the system font is 6 pixels per character, so 48 characters // fill the panel without spilling over its border. const const TYPED_BUFFER_MAX: 48TYPED_BUFFER_MAX = 48; // Keys the press counter listens to (KeyboardEvent.code strings): every letter, Space, // and the four arrow keys. const const PRESS_COUNTER_KEYS: {}PRESS_COUNTER_KEYS = [ ...'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('').map((letter: anyletter) => `Key${letter: anyletter}`), 'Space', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', ]; // Fixed layout, in display pixels, on a 4-pixel grid: 4 pixels from the screen edges and // 4 between panels. The kit normally stacks panels on its own, but this page places // several groups side by side, so each one gets a pinned x/y. // // - Two 76-pixel gamepad panels on the left, a 152-pixel column on the right // (4 + 76 + 4 + 76 + 4 + 152 + 4 = 320). // - The right column holds two 73-pixel panels (22 title + 4 rows of 10 + 11 bottom), // 150 pixels with the gap between them, and the gamepad panels are made exactly // that tall too, so every bottom edge lines up. // - The typed-text panel spans the full width under them. const const EDGE: 4EDGE = 4; const const GAP: 4GAP = 4; const const PANEL_TOP_Y: 32PANEL_TOP_Y = 32; const const PAD_PANEL_W: 76PAD_PANEL_W = 76; const const PLAYER0_PANEL_X: 4PLAYER0_PANEL_X = const EDGE: 4EDGE; const const PLAYER1_PANEL_X: numberPLAYER1_PANEL_X = const PLAYER0_PANEL_X: 4PLAYER0_PANEL_X + const PAD_PANEL_W: 76PAD_PANEL_W + const GAP: 4GAP; const const READOUT_COLUMN_X: numberREADOUT_COLUMN_X = const PLAYER1_PANEL_X: numberPLAYER1_PANEL_X + const PAD_PANEL_W: 76PAD_PANEL_W + const GAP: 4GAP; const const READOUT_COLUMN_W: numberREADOUT_COLUMN_W = 320 - const EDGE: 4EDGE - const READOUT_COLUMN_X: numberREADOUT_COLUMN_X; const const READOUT_PANEL_H: numberREADOUT_PANEL_H = 22 + 4 * 10 + 11; const const RAW_PANEL_Y: numberRAW_PANEL_Y = const PANEL_TOP_Y: 32PANEL_TOP_Y + const READOUT_PANEL_H: numberREADOUT_PANEL_H + const GAP: 4GAP; const const COLUMN_H: numberCOLUMN_H = 2 * const READOUT_PANEL_H: numberREADOUT_PANEL_H + const GAP: 4GAP; const const TYPED_PANEL_Y: numberTYPED_PANEL_Y = const PANEL_TOP_Y: 32PANEL_TOP_Y + const COLUMN_H: numberCOLUMN_H + const GAP: 4GAP; const const TYPED_PANEL_W: numberTYPED_PANEL_W = 320 - 2 * const EDGE: 4EDGE; // Inside a gamepad panel: eight 10-pixel pip rows with a 5-pixel gap after each of the // first seven, plus 2 pixels at the end, fill the 117 pixels between the 22-pixel title // strip and the 11-pixel bottom rim (8 x 10 + 7 x 5 + 2 = 117 = 150 - 22 - 11). const const PAD_ROW_GAP: 5PAD_ROW_GAP = 5; const const PAD_ROW_PITCH: numberPAD_ROW_PITCH = 10 + const PAD_ROW_GAP: 5PAD_ROW_GAP; const const PAD_END_SPACER: numberPAD_END_SPACER = const COLUMN_H: numberCOLUMN_H - 22 - 11 - (8 * 10 + 7 * const PAD_ROW_GAP: 5PAD_ROW_GAP); // Kit panel content starts 9 pixels in from the panel edge and 22 pixels below its top. const const PANEL_PAD: 9PANEL_PAD = 9; const const PANEL_CONTENT_TOP: 22PANEL_CONTENT_TOP = 22; // Shows keyboard face-button maps, raw key queries, and BT.inputString side by side. class class DemoDemo { /** @type {Palette | null} */ Demo.palette: Palette | null
@type{Palette | null}
palette
= null;
// Palette slots of the shared UI theme colors, filled by applyTheme() in init(). /** @type {ReturnType<typeof applyTheme> | null} */ Demo.theme: any
@type{ReturnType<typeof applyTheme> | null}
theme
= null;
// The eight face buttons for each player, built once in init(): the on-screen label, // the engine's button constant, and the short name of the first key mapped to it // ('-' when the player has no key for that button). Index 0 is player 0's list, // index 1 is player 1's. /** @type {Array<Array<{ label: string, code: number, key: string }>>} */
Demo.faceButtons: Array<Array<{
    label: string;
    code: number;
    key: string;
}>>
@type{Array<Array<{ label: string, code: number, key: string }>>}
faceButtons
= [];
// Engine tick when BT.isKeyReleased('KeyF') last fired. /** @type {number | null} */ Demo.lastFReleaseTick: number | null
@type{number | null}
lastFReleaseTick
= null;
// Engine tick when BT.isAnyKeyReleased() last fired (every key up). /** @type {number | null} */ Demo.lastAllUpTick: number | null
@type{number | null}
lastAllUpTick
= null;
// KeyboardEvent.code of the key we are counting presses on. /** @type {string | null} */ Demo.activePressKey: string | null
@type{string | null}
activePressKey
= null;
// How many edge-only BT.isKeyPressed events fired for activePressKey in a row. Demo.keyPressCount: numberkeyPressCount = 0; // Text built from BT.inputString over time (capped at TYPED_BUFFER_MAX). Demo.typedBuffer: stringtypedBuffer = ''; // Keeps the arrow keys and Space from scrolling the page, and turns the engine // overlay off - the panels already show everything this page is about.
Demo.configure(): {
    isCapturingKeyboardScroll: boolean;
    isOverlayEnabled: boolean;
}
configure
() {
return { // Arrow keys and Space scroll the host page by default. This demo maps those // keys (player 1 uses arrows), so opt in so pressing them does not move the // page. isCapturingKeyboardScroll: booleanisCapturingKeyboardScroll: true, isOverlayEnabled: booleanisOverlayEnabled: false, }; } // Installs the shared UI theme and builds the face-button lists once at startup. async Demo.init(): unknowninit() { // inputMapReset() restores the engine's default player 0 / player 1 key bindings. // Other demos (for example Input Map Remapping) may change those maps; we reset // here so W/A/S/D and the arrow keys always match what this page describes.
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
();
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);
// applyTheme() installs the twelve shared UI colors (into high palette slots, far // above where scene art normally lives) and reports their slots, so render() can // clear the screen with the theme's background color. Every panel, pip, and text // row on this page draws with these colors - no hand-picked HUD slots needed. this.Demo.theme: any
@type{ReturnType<typeof applyTheme> | null}
theme
= 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
);
// The full face-button roster, in the order the panels list them. Each entry // pairs a short on-screen label with the engine's button constant. const const allButtons: {}allButtons = [ { label: stringlabel: 'Up', code: numbercode:
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
},
{ label: stringlabel: 'Dn', code: numbercode:
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
},
{ label: stringlabel: 'Lf', code: numbercode:
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
},
{ label: stringlabel: 'Rt', code: numbercode:
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
},
{ label: stringlabel: 'A', code: numbercode:
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_A: number
A button bit flag.
@since0.1.0
BTN_A
},
{ label: stringlabel: 'B', code: numbercode:
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_B: number
B button bit flag.
@since0.1.0
BTN_B
},
{ label: stringlabel: 'St', code: numbercode:
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_START: number
Start button bit flag.
@since0.1.0
BTN_START
},
{ label: stringlabel: 'Sl', code: numbercode:
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_SELECT: number
Select button bit flag.
@since0.1.0
BTN_SELECT
},
]; // Give every button the short name of the first key in each player's default map // (keyLabel() from src/shared/key-names.js turns KeyW into W and the arrow keys // into arrow characters). // Both players keep all eight rows, so their panels line up row for row; a button // with no key (player 1's Select, for example) shows a dash and never lights. // .map() walks the two default maps, and the inner .map() the eight buttons. this.
Demo.faceButtons: Array<Array<{
    label: string;
    code: number;
    key: string;
}>>
@type{Array<Array<{ label: string, code: number, key: string }>>}
faceButtons
= [
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 DEFAULT_KEYBOARD_PLAYER1: Readonly<Record<number, {}>>
Default `KeyboardEvent.code` values for player index 0 (first keyboard player).
@since1.0.3@changed1.8.0 Now the `'versus'` `keyboardLayout` table (the default); see `BT.DEFAULT_KEYBOARD_SINGLE_PLAYER1` for `'single'`.
DEFAULT_KEYBOARD_PLAYER1
,
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 DEFAULT_KEYBOARD_PLAYER2: Readonly<Record<number, {}>>
Default `KeyboardEvent.code` values for player index 1 (second keyboard player).
@since1.0.3@changed1.8.0 Now the `'versus'` `keyboardLayout` table (the default); see `BT.DEFAULT_KEYBOARD_SINGLE_PLAYER2` for `'single'`.
DEFAULT_KEYBOARD_PLAYER2
].map((map: anymap) =>
const allButtons: {}allButtons.map((button: anybutton) => { const const keys: anykeys = map: anymap[button: anybutton.code] ?? []; return { ...button: anybutton, key: anykey: const keys: anykeys.length > 0 ? import keyLabelkeyLabel(const keys: anykeys[0]) : '-' }; }), ); return true; } // Reads keyboard state after the engine has updated input for this tick. Demo.update(): voidupdate() { // The UI kit's once-per-tick housekeeping. This demo has no kit buttons, but // ui.tick() is also where the kit notices touch contacts - ui.hasTouch() in // render() relies on it to know when to show the "needs a keyboard" notice. import uiui.tick(); // Raw keys (KeyboardEvent.code strings, layout-independent): // // BT.isKeyDown(code) is true EVERY tick while the key is held (like holding a // door shut). BT.isKeyPressed(code) is true only on the FIRST tick the key goes // down (a one-shot "tap"). BT.isKeyPressed(code, repeatTicks) is true on that // first tick and then again every repeatTicks fixed engine ticks while still held // (repeat on the game clock, not the OS key-repeat rate). BT.isKeyReleased(code) // is true only on the FIRST tick the key comes up (a one-shot "let go"). // // Face buttons (BT.BTN_UP, BT.isDown, ...) are separate: they go through the // input map. Use raw keys when you need a specific key regardless of player slot // or remapping. // Release edge for F: fires once when you let go of F. We remember the engine // tick it happened on so the raw-key panel can show it. if (
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
.isKeyReleased: (key: string) => boolean
Checks whether a keyboard key was released on the current frame. Call from `update()`, not `render()`: the release edge clears once per fixed-update tick, which always runs before that frame's `render()`, so a release read from `render()` can be intermittently missed under rapid input.
@since1.1.1@paramkey - DOM keyboard code string.@returns`true` on the release edge.
isKeyReleased
('KeyF')) {
this.Demo.lastFReleaseTick: number | null
@type{number | null}
lastFReleaseTick
=
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
;
} // Every key up: unlike isKeyReleased('KeyF') above, this does NOT fire when you // let go of one key while another is still down - only when nothing is held any // more. Like the other edges it must be read here in update(), so we remember the // tick for render(). if (
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
.isAnyKeyReleased: () => boolean
Checks whether every keyboard key has just been released. Matches RetroBlit: `true` only on the tick where no key is held any more and at least one was held before. Releasing one key while another stays down does not count; use {@link isKeyReleased } for per-key edges. Call from `update()`, not `render()`.
@since1.8.0@returns`true` on the tick the last held key comes up.
isAnyKeyReleased
()) {
this.Demo.lastAllUpTick: number | null
@type{number | null}
lastAllUpTick
=
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
;
} // Q held: we only read isKeyDown in render() for a live lit/unlit pip (no state // here). // Edge-only press counter: tap the same key to climb; another key resets to 1. for (let let i: numberi = 0; let i: numberi < const PRESS_COUNTER_KEYS: {}PRESS_COUNTER_KEYS.length; let i: numberi++) { const const code: anycode = const PRESS_COUNTER_KEYS: {}PRESS_COUNTER_KEYS[let i: numberi]; if (!
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
.isKeyPressed: (key: string, repeatRate?: number) => boolean
Checks whether a keyboard key was pressed on the current fixed-update tick. Optional `repeatRate` is in fixed ticks between repeats (`0` or omitted = edge only). When `repeatRate > 0`, repeats fire while held per `(ticks - firstPressTick) > 0 && (ticks - firstPressTick) % repeatRate === 0`. Call from `update()`, not `render()`: the press edge clears once per fixed-update tick, which always runs before that frame's `render()`, so a press read from `render()` can be intermittently missed under rapid input.
@since1.1.1@paramkey - DOM keyboard code string.@paramrepeatRate - Ticks between repeat triggers; omit or `0` for no repeat.@returns`true` on the press edge (and on repeat ticks when configured).
isKeyPressed
(const code: anycode)) {
continue; } if (this.Demo.activePressKey: string | null
@type{string | null}
activePressKey
=== const code: anycode) {
this.Demo.keyPressCount: numberkeyPressCount += 1; } else { this.Demo.activePressKey: string | null
@type{string | null}
activePressKey
= const code: anycode;
this.Demo.keyPressCount: numberkeyPressCount = 1; } break; } // Text buffer: the characters typed since the last tick arrive here once, so add // them to our own string now or they are gone next tick. const const chunk: stringchunk =
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
.inputString: string
Text accumulated since the last fixed-update flush from filtered `beforeinput` (and Tab / Escape where `beforeinput` is unreliable). Read during `update()`; the buffer clears at the end of each fixed update step.
@since1.0.4@returnsCharacters typed since the last fixed-update flush.
inputString
;
if (const chunk: stringchunk.length > 0) { this.Demo.typedBuffer: stringtypedBuffer += const chunk: stringchunk; if (this.Demo.typedBuffer: stringtypedBuffer.length > const TYPED_BUFFER_MAX: 48TYPED_BUFFER_MAX) { // Keep the most recent characters so long paste tests still show the // tail. this.Demo.typedBuffer: stringtypedBuffer = this.Demo.typedBuffer: stringtypedBuffer.slice(-const TYPED_BUFFER_MAX: 48TYPED_BUFFER_MAX); } } } // Clears the frame and declares every kit panel. Demo.render(): voidrender() { // Paint the whole screen with the theme's deep navy background.
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.clear: (paletteIndex: number) => 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
(this.Demo.theme: any
@type{ReturnType<typeof applyTheme> | null}
theme
.bg);
// Full-width title strip across the top, in the classic 22-pixel top-bar style. import uiui.begin(import UI_ANCHORSUI_ANCHORS.TOP_BAR); import uiui.panel('Keyboard Input (KeyboardEvent.code)'); import uiui.end(); // The two face-button panels sit side by side, like two little gamepads. this.Demo.renderFacePanel(player: any, x: any): voidrenderFacePanel(0, const PLAYER0_PANEL_X: 4PLAYER0_PANEL_X); this.Demo.renderFacePanel(player: any, x: any): voidrenderFacePanel(1, const PLAYER1_PANEL_X: numberPLAYER1_PANEL_X); // The right-hand column: press counter on top, raw-key readouts below. this.Demo.renderPressCounter(): voidrenderPressCounter(); this.Demo.renderRawKeyPanel(): voidrenderRawKeyPanel(); // The typed-text line spans the full width under the other panels. this.Demo.renderTypedLine(): voidrenderTypedLine(); } // Draws one player's face buttons as a panel of lit/unlit pip rows, each with the key // it is mapped to on the right. `player` is 0 or 1 (the BT.isDown player index), `x` // the panel's left edge in display pixels. Demo.renderFacePanel(player: any, x: any): voidrenderFacePanel(player: anyplayer, x: anyx) { // Pin the panel to its column; both player panels share the same top edge. import uiui.begin(import UI_ANCHORSUI_ANCHORS.TOP_LEFT, { x: anyx, y: numbery: const PANEL_TOP_Y: 32PANEL_TOP_Y, width: numberwidth: const PAD_PANEL_W: 76PAD_PANEL_W }); import uiui.panel(player: anyplayer === 0 ? 'Player 0' : 'Player 1'); const
const buttons: Array<Array<{
    label: string;
    code: number;
    key: string;
}>>
buttons
= this.
Demo.faceButtons: Array<Array<{
    label: string;
    code: number;
    key: string;
}>>
@type{Array<Array<{ label: string, code: number, key: string }>>}
faceButtons
[player: anyplayer];
// One read-only pip per button: filled while the button is held, hollow when it // is up. BT.isDown() reports held state (not a press edge), so reading it here in // render() is safe - only press/release EDGES must stay in update(). A small // spacer after each row (except the last) spreads the rows out so the panel ends // level with the column beside it. for (let let i: numberi = 0; let i: numberi <
const buttons: Array<Array<{
    label: string;
    code: number;
    key: string;
}>>
buttons
.length; let i: numberi++) {
const {
const label: Array<Array<{
    label: string;
    code: number;
    key: string;
}>>
label
,
const code: Array<Array<{
    label: string;
    code: number;
    key: string;
}>>
code
} =
const buttons: Array<Array<{
    label: string;
    code: number;
    key: string;
}>>
buttons
[let i: numberi];
import uiui.pip(
const label: Array<Array<{
    label: string;
    code: number;
    key: string;
}>>
label
,
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
.isDown: (button: number, player?: number) => boolean
Checks whether a button is currently held. For pointer buttons (`BTN_POINTER_A..D`), the second parameter is the pointer slot index (0 = mouse, 1-3 = touch / pen). For mouse slot 0: `A` is left, `B` is right, `C` is middle, `D` is back / forward (matches RetroBlit canonical, not DOM `PointerEvent.button` index). Touch / pen slots only support `A`; B/C/D return `false`. `button` accepts one or more bit flags from the `BTN_*` set (for example `BT.BTN_A | BT.BTN_B`). Matching uses ANY semantics: returns `true` when any selected button is held. For face buttons (`BTN_UP`…`BTN_SELECT`), players `0` and `1` merge keyboard and gamepad input (logical OR). Players `2` and `3` use gamepad only. Pointer flags (`BTN_POINTER_*`) use the `player` argument as pointer slot. `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 / keyboard, or pointer slot (0-3) for `BTN_POINTER_*`.@returns`true` while the button remains pressed.
isDown
(
const code: Array<Array<{
    label: string;
    code: number;
    key: string;
}>>
code
, player: anyplayer));
if (let i: numberi <
const buttons: Array<Array<{
    label: string;
    code: number;
    key: string;
}>>
buttons
.length - 1) {
import uiui.spacer(const PAD_ROW_GAP: 5PAD_ROW_GAP); } } import uiui.spacer(const PAD_END_SPACER: numberPAD_END_SPACER); import uiui.end(); // The mapped keys, right-aligned against the panel's right edge, on the same rows // as the pip labels. Every character of the system font is FONT_W (6) pixels // wide. The kit prints a pip's label 1 pixel below the top of its row, and a // caption 4 pixels above the y it is given, so y = rowTop + 5 puts both texts on // the same line. const const rightEdge: numberrightEdge = x: anyx + const PAD_PANEL_W: 76PAD_PANEL_W - const PANEL_PAD: 9PANEL_PAD; for (let let i: numberi = 0; let i: numberi <
const buttons: Array<Array<{
    label: string;
    code: number;
    key: string;
}>>
buttons
.length; let i: numberi++) {
const {
const key: Array<Array<{
    label: string;
    code: number;
    key: string;
}>>
key
} =
const buttons: Array<Array<{
    label: string;
    code: number;
    key: string;
}>>
buttons
[let i: numberi];
const const rowTop: numberrowTop = const PANEL_TOP_Y: 32PANEL_TOP_Y + const PANEL_CONTENT_TOP: 22PANEL_CONTENT_TOP + let i: numberi * const PAD_ROW_PITCH: numberPAD_ROW_PITCH; import uiui.caption(const rightEdge: numberrightEdge -
const key: Array<Array<{
    label: string;
    code: number;
    key: string;
}>>
key
.length * import FONT_WFONT_W, const rowTop: numberrowTop + 5,
const key: Array<Array<{
    label: string;
    code: number;
    key: string;
}>>
key
, { color: stringcolor: 'dim' });
} } // Draws the readout for edge-only press counting on one key at a time. Demo.renderPressCounter(): voidrenderPressCounter() { import uiui.begin(import UI_ANCHORSUI_ANCHORS.TOP_LEFT, { x: numberx: const READOUT_COLUMN_X: numberREADOUT_COLUMN_X, y: numbery: const PANEL_TOP_Y: 32PANEL_TOP_Y, width: numberwidth: const READOUT_COLUMN_W: numberREADOUT_COLUMN_W }); import uiui.panel('isKeyPressed counter'); // Before any counted key is tapped there is nothing to show, so both rows fall // back to placeholder text. const const hasKey: booleanhasKey = this.Demo.activePressKey: string | null
@type{string | null}
activePressKey
!== null;
import uiui.kv('Key', const hasKey: booleanhasKey ? import keyLabelkeyLabel(this.Demo.activePressKey: string | null
@type{string | null}
activePressKey
) : 'none yet');
import uiui.kv('Count', const hasKey: booleanhasKey ? this.Demo.keyPressCount: numberkeyPressCount : '-'); import uiui.label('Same key: +1', { color: stringcolor: 'dim' }); import uiui.label('New key: reset', { color: stringcolor: 'dim' }); import uiui.end(); } // Draws the panel for Q held state, the last F release, and the whole-keyboard // checks. Demo.renderRawKeyPanel(): voidrenderRawKeyPanel() { import uiui.begin(import UI_ANCHORSUI_ANCHORS.TOP_LEFT, { x: numberx: const READOUT_COLUMN_X: numberREADOUT_COLUMN_X, y: numbery: const RAW_PANEL_Y: numberRAW_PANEL_Y, width: numberwidth: const READOUT_COLUMN_W: numberREADOUT_COLUMN_W }); import uiui.panel('Raw keys'); // Held state is safe to read in render() (see renderFacePanel above). import uiui.pip('Q (isKeyDown)',
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
.isKeyDown: (key: string) => boolean
Checks whether a keyboard key is currently held. Uses `KeyboardEvent.code` (for example `"KeyW"`, `"Space"`, `"ArrowUp"`).
@since1.1.1@paramkey - DOM keyboard code string.@returns`true` while the key remains pressed.
isKeyDown
('KeyQ'));
// The release EDGE was caught in update(); here we only display the remembered // tick. import uiui.kv('F rel', this.Demo.lastFReleaseTick: number | null
@type{number | null}
lastFReleaseTick
=== null ? 'tap F' : `tick ${this.Demo.lastFReleaseTick: number
@type{number | null}
lastFReleaseTick
}`);
// Held state again, but for the whole keyboard: lit while ANY key is down. import uiui.pip('Any (isAnyKeyDown)',
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
.isAnyKeyDown: () => boolean
Checks whether at least one keyboard key is currently held.
@since1.8.0@returns`true` while any key is down.
isAnyKeyDown
());
// The "all keys up" edge was caught in update(); here we only show the remembered // tick. import uiui.kv('All up', this.Demo.lastAllUpTick: number | null
@type{number | null}
lastAllUpTick
=== null ? 'press, release' : `tick ${this.Demo.lastAllUpTick: number
@type{number | null}
lastAllUpTick
}`);
import uiui.end(); } // Draws the text accumulated from BT.inputString. On a touch device it shows a // warning instead - this page has no on-screen substitute for a physical keyboard. Demo.renderTypedLine(): voidrenderTypedLine() { // A fixed width keeps the panel spanning the screen even while the buffer is // short. import uiui.begin(import UI_ANCHORSUI_ANCHORS.TOP_LEFT, { x: numberx: const EDGE: 4EDGE, y: numbery: const TYPED_PANEL_Y: numberTYPED_PANEL_Y, width: numberwidth: const TYPED_PANEL_W: numberTYPED_PANEL_W }); import uiui.panel('BT.inputString (typed this session)'); const const hasText: booleanhasText = this.Demo.typedBuffer: stringtypedBuffer.length > 0; if (import uiui.hasTouch()) { import uiui.label('This demo needs a keyboard', { color: stringcolor: 'warm' }); } else { import uiui.label(const hasText: booleanhasText ? this.Demo.typedBuffer: stringtypedBuffer : '...', { color: stringcolor: const hasText: booleanhasText ? 'text' : 'dim' }); } import uiui.end(); } } 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);