A WebGPU retro game engine for TypeScript, inspired by RetroBlit. Build pixel-perfect 2D games with a clean, fantasy-console-style API.
blit-tech docs api-core.md
10 kB · 242 lines
Markdown
at main

API: Core #

Bootstrap, initialization, game loop timing, camera, and core types.


Bootstrap #

The bootstrap() function is the recommended entry point. It handles DOM ready, canvas lookup, backend selection (WebGPU or software fallback), and error display automatically.

import { bootstrap, type BootstrapOptions } from 'blit-tech';

// One-liner - canvas id defaults to 'blit-tech-canvas', container to 'canvas-container'
bootstrap(MyDemo);

// With options
bootstrap(MyDemo, {
  canvasId: 'my-canvas',
  containerId: 'error-wrapper',
  onSuccess: () => console.log('started'),
  onError: (err) => trackError(err),
  waitForDOMReady: true, // default true; set false in Electron after DOMContentLoaded
});

BootstrapOptions fields:

Field Type Default Description
canvasId string 'blit-tech-canvas' Canvas element id
containerId string 'canvas-container' Container id for error display
onSuccess () => void - Called after successful init
onError (error: Error) => void - Called on any init failure
waitForDOMReady boolean true Wait for DOMContentLoaded

Manual utilities (for custom initialization flows):

import { displayError, getCanvas } from 'blit-tech';

const canvas = getCanvas('my-canvas'); // returns null on missing element
displayError('Init Failed', 'WebGPU unavailable.', 'my-container');

Initialization #

const ok = await BT.init(demo, canvas); // low-level init; prefer bootstrap()
BT.displaySize; // Vector2i - configured logical resolution (clone per read)
BT.canvasDisplaySize; // Vector2i | null - output buffer when set in configure()
BT.outputSize; // Vector2i - effective drawing-buffer size (clone per read)
BT.targetFPS; // number - target updates per second
BT.activeBackend; // 'webgpu' | 'software' | null

BT.init() selects WebGPU or falls back to the Canvas 2D software renderer automatically. When not using bootstrap(), set canvas.tabIndex = 0 and call canvas.focus() so keyboard events reach the canvas.

HardwareSettings (resolved after configure(); the hook may return a partial object):

configure() may return only the fields you want to override. The engine merges them with defaultConfig() via mergeHardwareSettings() (also exported). Omit displaySize to inherit the full default resolution and 640×480 output buffer. Include displaySize when you want a custom logical size; optional fields you omit then stay unset (for example no canvasDisplaySize means a 1:1 drawing buffer).

Field Type Default Description
displaySize Vector2i 320×240 Logical render resolution
canvasDisplaySize Vector2i 640×480 CSS/output size; enables display-tier effects
maxCanvasDisplaySize Vector2i 960×720 Maximum on-screen canvas CSS size
targetFPS number 60 Fixed-update rate
renderer 'webgpu' | 'software' 'webgpu' Force backend
outputUpscaleFilter 'nearest' | 'linear' 'nearest' Upscale filter
detectDroppedFrames boolean false Log a console warning on missed vsync

displaySize, canvasDisplaySize, and maxCanvasDisplaySize must be positive whole-number pixel dimensions. Each size is capped at 8192×8192 per axis and 16,777,216 total pixels (4096×4096). Invalid sizes make initialization fail before the engine applies canvas layout, sets canvas backing dimensions, or allocates renderer buffers. BT.init() returns false and logs a specific [BT] message to the browser console (press F12); the on-canvas bootstrap error stays a generic init failure message. In WebGPU mode, the requested logical and output sizes must also fit the active adapter/device maxTextureDimension2D limit. GPU limit failures do not fall back to the software renderer.

BT getters vs configure() fields: displaySize, canvasDisplaySize, and targetFPS on BT mirror the same names on HardwareSettings. outputSize is the effective drawing-buffer size (canvasDisplaySize ?? displaySize). activeBackend is the backend that actually started (after fallback), not the renderer value from configure().


Game Loop Timing #

BT.deltaSeconds; // seconds per fixed tick (1 / BT.targetFPS)
BT.timeSeconds; // elapsed seconds since init (ticks × deltaSeconds)
BT.ticks; // current tick counter (increments each update)
BT.ticksReset(); // reset tick counter to 0

Timer #

Timer fires once per fixed interval. Use it for periodic events: particle spawns, score ticks, palette swaps.

const spawn = new Timer(180); // fires every 180 ticks (3 s at 60 FPS)

// Inside update():
if (spawn.tick()) {
  spawnEnemy();
}

// Additional API:
spawn.reset(); // restart interval from now
spawn.elapsedTicks(); // ticks since last fire/reset
spawn.remainingTicks(); // ticks until next fire
spawn.intervalTicks; // readonly interval size

Timer.tick() advances the internal baseline on each true return. Pass BT.ticks explicitly only when you need a specific snapshot; the default is the engine tick counter.


Camera #

The camera applies a global pixel offset to all subsequent draw calls. Integer only - pass Vector2i, never floats.

BT.cameraSet(new Vector2i(scrollX, scrollY)); // apply offset
BT.camera; // Vector2i - current offset
BT.cameraReset(); // set back to (0, 0)

// Clamp a camera origin so the viewport stays within a world:
const clamped = BT.cameraClamp(desired, worldSize);
// Optional third argument overrides the viewport size (default: BT.displaySize):
const clamped = BT.cameraClamp(desired, worldSize, new Vector2i(160, 120));

Core Types #

Vector2i #

Integer 2D vector. Constructor auto-truncates floats toward zero. Used for all positions, sizes, and camera offsets.

const v = new Vector2i(10, 20);
v.x;
v.y; // direct access
v.width;
v.height; // aliases for x / y (useful when treating the vector as a size)

// Static factories
Vector2i.zero(); // (0, 0) - cached frozen singleton
Vector2i.one(); // (1, 1) - cached frozen singleton
Vector2i.up(); // (0, -1)
Vector2i.down(); // (0, 1)
Vector2i.left(); // (-1, 0)
Vector2i.right(); // (1, 0)
Vector2i.fromFloat(x, y); // truncate floats → integer vector

// Math utilities
Vector2i.distance(a, b); // Euclidean distance (float)
Vector2i.sqrDistance(a, b); // squared distance (avoids sqrt)
Vector2i.dotProduct(a, b); // scalar dot product
Vector2i.lerp(a, b, t); // interpolate, t clamped [0,1], result truncated
Vector2i.lerpTo(a, b, t, out); // zero-allocation lerp into existing vector

Instance methods: .add(), .sub(), .scale(), .dot(), .clone(), .equals(), .negate(), .abs(), .min(), .max(), etc. See src/utils/Vector2i.ts for the full list.

Rect2i #

Integer rectangle with x, y, width, height fields.

const r = new Rect2i(x, y, width, height);

// Static factories
Rect2i.zero(); // (0,0,0,0) - cached frozen singleton
Rect2i.fromMinMax(min, max); // from corner vectors
Rect2i.fromMinMaxXY(minX, minY, maxX, maxY); // zero-allocation variant
Rect2i.fromCenterSize(center, size); // centered rectangle
Rect2i.fromCenterSizeXY(cx, cy, w, h); // zero-allocation variant

// Instance methods
r.contains(point); // boolean - point inside rect
r.intersects(other); // boolean - rects overlap
r.intersection(other); // Rect2i | null - overlap area
r.center; // Vector2i getter
r.min; // Vector2i getter (top-left)
r.max; // Vector2i getter (bottom-right)

Color32 #

32-bit RGBA color (channels 0-255).

const c = new Color32(r, g, b, a);

// Cached color constants (static getters; frozen singletons)
Color32.white       Color32.black       Color32.transparent
Color32.red         Color32.green       Color32.blue
Color32.yellow      Color32.cyan        Color32.magenta
Color32.gray(value)   // grayscale, value 0-255

// Hex parsing
Color32.fromHex('#ff8800')    // returns Color32

// Named color registry (global, case-insensitive)
Color32.registerColor('brand', new Color32(255, 128, 0, 255));
Color32.resolveNamedColor('brand');   // → Color32 | undefined
Color32.updateColor('brand', newColor);
Color32.unregisterColor('brand');

// Interpolation
Color32.lerp(a, b, t);       // blend a→b; t clamped [0,1]; RGBA independent; truncates like instance lerp
c.lerp(other, t);            // same semantics as static helper

// Conversion
c.toFloat32Array()            // [r/255, g/255, b/255, a/255]
c.luminance                   // perceived brightness: 0.299r + 0.587g + 0.114b
c.toHex()                     // '#rrggbb' string

See Also #

Guide What it covers
API: Rendering primitives, sprites, text, post-process, frame capture
API: Palette palette setup, presets, effects
Palette Guide palette-first workflow and practical patterns
API: Assets sprite sheets, bitmap fonts, asset loading
Input Guide pointer, keyboard, gamepad
Testing test tiers, WebGPU mocks