/**
 * Keyboard Input Demo - 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
 *
 * This page shows 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.
 * - Text input (BT.inputString) for characters in one frame. 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.
 *
 * The panels and readouts are drawn with the shared demo UI kit (src/shared/ui.js),
 * so this page looks like every other demo in the series. The inputs themselves stay
 * deliberately keyboard-only - there is no touch stand-in for a physical keyboard,
 * so on a touch device the page shows a "needs a keyboard" notice instead.
 *
 * Try this:
 * - Hold W, A, S, D and Space / N on player 1; arrow keys and ; ' on player 2.
 * - Tap the same letter repeatedly and watch the press counter climb (edge-only
 *   BT.isKeyPressed, no repeat rate). Press a different key and the counter resets.
 * - Hold Q to see isKeyDown; tap F and watch the release readout.
 * - 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;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
} from 'blit386';
import { import applyThemeapplyTheme, import uiui, import UI_ANCHORSUI_ANCHORS } from './shared/ui.js'; /** @typedef {import('blit386').IBTDemo} IBTDemo */ /** @typedef {import('blit386').HardwareSettings} HardwareSettings */ /** @typedef {import('blit386').Palette} Palette */ // How many characters we keep in the typed-text demo line (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). const const PRESS_COUNTER_KEYS: {}PRESS_COUNTER_KEYS = [ ...'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('').map((letter: anyletter) => `Key${letter: anyletter}`), 'Space', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', ]; // Palette slots of three shared UI theme colors, for the overlay timing chart below. // applyTheme() in init() writes the twelve theme colors into slots 240-251 (its default // start slot), but configure() runs BEFORE init(), so the chart style cannot read // this.theme yet - we spell out where the colors will land: 244 = text, 245 = dim gray, // 247 = accent green. const const THEME_TEXT_SLOT: 244THEME_TEXT_SLOT = 244; const const THEME_DIM_SLOT: 245THEME_DIM_SLOT = 245; const const THEME_ACCENT_SLOT: 247THEME_ACCENT_SLOT = 247; // Fixed vertical layout, in display pixels. 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. const const NOTICE_Y: 24NOTICE_Y = 24; // The hint / touch-notice line right under the title strip. const const PANEL_TOP_Y: 42PANEL_TOP_Y = 42; // Top edge of the face-button panels and the press counter. const const RAW_PANEL_Y: 134RAW_PANEL_Y = 134; // Top edge of the raw-key panel (below the press counter). const const PLAYER0_PANEL_X: 6PLAYER0_PANEL_X = 6; // Left edge of the player 0 panel. const const PLAYER1_PANEL_X: 68PLAYER1_PANEL_X = 68; // Left edge of the player 1 panel (next to player 0). const const READOUT_COLUMN_X: 130READOUT_COLUMN_X = 130; // Left edge of the press-counter / raw-key column. const const TYPED_PANEL_WIDTH: 308TYPED_PANEL_WIDTH = 308; // The typed-text panel spans almost the full screen. /** * Turns `KeyH` into `H`, `Digit5` into `5`, and leaves other codes readable. * * @param {string} code - KeyboardEvent.code value. * @returns {string} */ function function formatKeyCode(code: string): string
Turns `KeyH` into `H`, `Digit5` into `5`, and leaves other codes readable.
@paramcode - KeyboardEvent.code value.@returns
formatKeyCode
(code: string
- KeyboardEvent.code value.
@paramcode - KeyboardEvent.code value.
code
) {
if (code: string
- KeyboardEvent.code value.
@paramcode - KeyboardEvent.code value.
code
.startsWith('Key')) {
return code: string
- KeyboardEvent.code value.
@paramcode - KeyboardEvent.code value.
code
.slice(3);
} if (code: string
- KeyboardEvent.code value.
@paramcode - KeyboardEvent.code value.
code
.startsWith('Digit')) {
return code: string
- KeyboardEvent.code value.
@paramcode - KeyboardEvent.code value.
code
.slice(5);
} return code: string
- KeyboardEvent.code value.
@paramcode - KeyboardEvent.code value.
code
;
} /** * Shows keyboard face-button maps, low-level key queries, and `inputString`. * * @implements {IBTDemo} */ class class Demo
Shows keyboard face-button maps, low-level key queries, and `inputString`.
@implementsIBTDemo
Demo
{
/** @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;
/** * Face buttons that have at least one key in each player's default map, built once * in init(). Index 0 is player 0's list, index 1 is player 1's. * * @type {Array<Array<{ label: string, code: number }>>} */
Demo.faceButtons: Array<Array<{
    label: string;
    code: number;
}>>
Face buttons that have at least one key in each player's default map, built once in init(). Index 0 is player 0's list, index 1 is player 1's.
@type{Array<Array<{ label: string, code: number }>>}
faceButtons
= [];
/** @type {number | null} Engine tick when `BT.isKeyReleased('KeyF')` last fired. */ Demo.lastFReleaseTick: number | null
@type{number | null} Engine tick when `BT.isKeyReleased('KeyF')` last fired.
lastFReleaseTick
= null;
/** @type {string | null} KeyboardEvent.code for the key we are counting presses on. */ Demo.activePressKey: string | null
@type{string | null} KeyboardEvent.code for the key we are counting presses on.
activePressKey
= null;
// How many edge-only `BT.isKeyPressed` events fired for `activePressKey` this run. Demo.keyPressCount: numberkeyPressCount = 0; // Text built from `BT.inputString` over time (capped). Demo.typedBuffer: stringtypedBuffer = ''; /** * Timing chart on so key-release milestones show on the overlay HUD. * * @returns {Partial<HardwareSettings>} */ Demo.configure(): Partial<HardwareSettings>
Timing chart on so key-release milestones show on the overlay HUD.
@returns
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, isOverlayTimingChartEnabled: booleanisOverlayTimingChartEnabled: true,
overlayTimingChartStyle: {
    updateBarPaletteIndex: number;
    renderBarPaletteIndex: number;
    tagPaletteIndex: number;
}
overlayTimingChartStyle
: {
updateBarPaletteIndex: numberupdateBarPaletteIndex: const THEME_DIM_SLOT: 245THEME_DIM_SLOT, renderBarPaletteIndex: numberrenderBarPaletteIndex: const THEME_TEXT_SLOT: 244THEME_TEXT_SLOT, tagPaletteIndex: numbertagPaletteIndex: const THEME_ACCENT_SLOT: 247THEME_ACCENT_SLOT, }, }; } /** * Install the shared UI theme and build the face-button lists once at startup. * * @returns {Promise<boolean>} */ async Demo.init(): Promise<boolean>
Install the shared UI theme and build the face-button lists once at startup.
@returns
init
() {
// inputMapReset() restores the engine's default player-1 / player-2 key bindings. // Other demos (for example input-map remapping) may change those maps; we reset here // so W/A/S/D and 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;
    ... 106 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`. Same tables as `BT.DEFAULT_KEYBOARD_PLAYER1` and `BT.DEFAULT_KEYBOARD_PLAYER2`.
@since1.0.3
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;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.paletteCreate: (size?: number) => Palette
Creates a standalone palette instance.
@since1.0.3@paramsize - Palette size. Defaults to 256 colors.@returnsNew mutable palette.
paletteCreate
(256);
// 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;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.paletteSet: (palette: Palette) => void
Stores the active engine palette. Use this to swap the **entire palette** (e.g. switch between a day and night theme). After this call the renderer uploads the new palette uniform on the next frame. **Palette-value swap (change what a slot looks like):** mutate the live {@link BT.palette } in place with `palette.set(slot, newColor)`. The renderer uploads dirty slots on the next frame; no `paletteSet()` or {@link BT.spritesRefresh } needed. **Palette-layout swap (same colors, different slot positions):** build a new palette with the same colors at new indices, call `paletteSet()`, then call {@link BT.spritesRefresh } so every sprite sheet re-maps its original RGBA pixels against the new slot layout.
@since1.0.3@parampalette - Palette to make active.
paletteSet
(this.Demo.palette: Palette
@type{Palette | null}
palette
);
// 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;
    ... 106 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;
    ... 106 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;
    ... 106 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;
    ... 106 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;
    ... 106 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;
    ... 106 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;
    ... 106 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;
    ... 106 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
},
]; // Keep only the buttons that actually have keys in each player's default map // (player 2's Select slot is empty, for example, so its row would never light). // .map() walks the two default maps and .filter() drops the empty slots. this.
Demo.faceButtons: Array<Array<{
    label: string;
    code: number;
}>>
Face buttons that have at least one key in each player's default map, built once in init(). Index 0 is player 0's list, index 1 is player 1's.
@type{Array<Array<{ label: string, code: number }>>}
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;
    ... 106 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
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;
    ... 106 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
DEFAULT_KEYBOARD_PLAYER2
].map((map: anymap) =>
const allButtons: {}allButtons.filter((button: anybutton) => (map: anymap[button: anybutton.code] ?? []).length > 0), ); return true; } /** * Read keyboard state after the engine has updated input for this tick. */ Demo.update(): void
Read keyboard state after the engine has updated input for this tick.
update
() {
// 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) = true EVERY tick while the key is held (like holding a door shut). // BT.isKeyPressed(code) = true only on the FIRST tick the key goes down (one-shot "tap"). // BT.isKeyPressed(code, repeatTicks) = first tick down, then again every repeatTicks fixed // engine ticks while still held (repeat on the game clock, not the OS key-repeat rate). // BT.isKeyReleased(code) = true only on the FIRST tick the key comes up (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;
    ... 106 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} Engine tick when `BT.isKeyReleased('KeyF')` last fired.
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;
    ... 106 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;
    ... 106 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
('Key F released');
} // 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;
    ... 106 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} KeyboardEvent.code for the key we are counting presses on.
activePressKey
=== const code: anycode) {
this.Demo.keyPressCount: numberkeyPressCount += 1; } else { this.Demo.activePressKey: string | null
@type{string | null} KeyboardEvent.code for the key we are counting presses on.
activePressKey
= const code: anycode;
this.Demo.keyPressCount: numberkeyPressCount = 1; } break; } // Text buffer // Characters arrive for this frame only; concat now or they are gone next frame. 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;
    ... 106 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); } } } /** * Clear the frame and declare every kit panel. */ Demo.render(): void
Clear the frame and declare every kit panel.
render
() {
// 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;
    ... 106 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.clear: (paletteIndex: number) => void
Sets the frame clear color using a palette index. The renderer uses this color when clearing the full display at the start of the next frame.
@since0.1.0@parampaletteIndex - Palette index for the full-screen clear pass.
clear
(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(); // One borderless line right under the title. On a touch device it becomes a // warning - this page has no on-screen substitute for a physical keyboard - and // otherwise it lists the default key maps as a quick reference. pad: 0 removes // the group's inner padding so the single line sits snug against its y position. import uiui.begin(import UI_ANCHORSUI_ANCHORS.TOP_LEFT, { y: numbery: const NOTICE_Y: 24NOTICE_Y, pad: numberpad: 0 }); if (import uiui.hasTouch()) { import uiui.label('This demo needs a keyboard', { color: stringcolor: 'warm' }); } else { import uiui.label('P0: W A S D Space N 5 Esc - P1: arrows ; quote /', { color: stringcolor: 'dim' }); } import uiui.end(); // The two face-button panels sit side by side, like two little gamepads. this.Demo.renderFacePanel(player: number, x: number): void
One player's mapped face buttons as a panel of lit/unlit pip rows.
@paramplayer - 0 or 1 (`BT.isDown` player index).@paramx - Left edge of the panel in display pixels.
renderFacePanel
(0, const PLAYER0_PANEL_X: 6PLAYER0_PANEL_X);
this.Demo.renderFacePanel(player: number, x: number): void
One player's mapped face buttons as a panel of lit/unlit pip rows.
@paramplayer - 0 or 1 (`BT.isDown` player index).@paramx - Left edge of the panel in display pixels.
renderFacePanel
(1, const PLAYER1_PANEL_X: 68PLAYER1_PANEL_X);
// The right-hand column: press counter on top, raw-key readouts below. this.Demo.renderPressCounter(): void
Readout for edge-only press counting on one key at a time.
renderPressCounter
();
this.Demo.renderRawKeyPanel(): void
Panel for Q held state and the last F release.
renderRawKeyPanel
();
// The typed-text line hugs the bottom edge of the screen. this.Demo.renderTypedLine(): void
Shows text accumulated from `BT.inputString`.
renderTypedLine
();
} /** * One player's mapped face buttons as a panel of lit/unlit pip rows. * * @param {number} player - 0 or 1 (`BT.isDown` player index). * @param {number} x - Left edge of the panel in display pixels. */ Demo.renderFacePanel(player: number, x: number): void
One player's mapped face buttons as a panel of lit/unlit pip rows.
@paramplayer - 0 or 1 (`BT.isDown` player index).@paramx - Left edge of the panel in display pixels.
renderFacePanel
(player: number
- 0 or 1 (`BT.isDown` player index).
@paramplayer - 0 or 1 (`BT.isDown` player index).
player
, x: number
- Left edge of the panel in display pixels.
@paramx - Left edge of the panel in display pixels.
x
) {
// Pin the panel to its column; both player panels share the same top edge. import uiui.begin(import UI_ANCHORSUI_ANCHORS.TOP_LEFT, { x: numberx, y: numbery: const PANEL_TOP_Y: 42PANEL_TOP_Y }); import uiui.panel(player: number
- 0 or 1 (`BT.isDown` player index).
@paramplayer - 0 or 1 (`BT.isDown` player index).
player
=== 0 ? 'Player 0' : 'Player 1');
const
const buttons: Array<Array<{
    label: string;
    code: number;
}>>
buttons
= this.
Demo.faceButtons: Array<Array<{
    label: string;
    code: number;
}>>
Face buttons that have at least one key in each player's default map, built once in init(). Index 0 is player 0's list, index 1 is player 1's.
@type{Array<Array<{ label: string, code: number }>>}
faceButtons
[player: number
- 0 or 1 (`BT.isDown` player index).
@paramplayer - 0 or 1 (`BT.isDown` player index).
player
];
// 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(). for (let let i: numberi = 0; let i: numberi <
const buttons: Array<Array<{
    label: string;
    code: number;
}>>
buttons
.length; let i: numberi++) {
const {
const label: Array<Array<{
    label: string;
    code: number;
}>>
label
,
const code: Array<Array<{
    label: string;
    code: number;
}>>
code
} =
const buttons: Array<Array<{
    label: string;
    code: number;
}>>
buttons
[let i: numberi];
import uiui.pip(
const label: Array<Array<{
    label: string;
    code: number;
}>>
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;
    ... 106 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.
@since1.1.1@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;
}>>
code
, player: number
- 0 or 1 (`BT.isDown` player index).
@paramplayer - 0 or 1 (`BT.isDown` player index).
player
));
} import uiui.end(); } /** * Readout for edge-only press counting on one key at a time. */ Demo.renderPressCounter(): void
Readout for edge-only press counting on one key at a time.
renderPressCounter
() {
import uiui.begin(import UI_ANCHORSUI_ANCHORS.TOP_LEFT, { x: numberx: const READOUT_COLUMN_X: 130READOUT_COLUMN_X, y: numbery: const PANEL_TOP_Y: 42PANEL_TOP_Y }); 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} KeyboardEvent.code for the key we are counting presses on.
activePressKey
!== null;
import uiui.kv('Key', const hasKey: booleanhasKey ? function formatKeyCode(code: string): string
Turns `KeyH` into `H`, `Digit5` into `5`, and leaves other codes readable.
@paramcode - KeyboardEvent.code value.@returns
formatKeyCode
(this.Demo.activePressKey: string | null
@type{string | null} KeyboardEvent.code for the key we are counting presses on.
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(); } /** * Panel for Q held state and the last F release. */ Demo.renderRawKeyPanel(): void
Panel for Q held state and the last F release.
renderRawKeyPanel
() {
import uiui.begin(import UI_ANCHORSUI_ANCHORS.TOP_LEFT, { x: numberx: const READOUT_COLUMN_X: 130READOUT_COLUMN_X, y: numbery: const RAW_PANEL_Y: 134RAW_PANEL_Y }); import uiui.panel('Raw keys'); // Held state is safe to read in render() (see renderFacePanel above). import uiui.pip('Q held (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;
    ... 106 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} Engine tick when `BT.isKeyReleased('KeyF')` last fired.
lastFReleaseTick
=== null ? 'tap F' : `tick ${this.Demo.lastFReleaseTick: number
@type{number | null} Engine tick when `BT.isKeyReleased('KeyF')` last fired.
lastFReleaseTick
}`);
import uiui.end(); } /** * Shows text accumulated from `BT.inputString`. */ Demo.renderTypedLine(): void
Shows text accumulated from `BT.inputString`.
renderTypedLine
() {
// A fixed width keeps the panel spanning the screen even while the buffer is // short; bottomLeft anchors it just above the bottom edge. import uiui.begin(import UI_ANCHORSUI_ANCHORS.BOTTOM_LEFT, { width: numberwidth: const TYPED_PANEL_WIDTH: 308TYPED_PANEL_WIDTH }); import uiui.panel('BT.inputString (typed this session)'); const const hasText: booleanhasText = this.Demo.typedBuffer: stringtypedBuffer.length > 0; 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 Demo
Shows keyboard face-button maps, low-level key queries, and `inputString`.
@implementsIBTDemo
Demo
);