blit-tech
v1.0.4
Published
A palette-first WebGPU retro engine for TypeScript, inspired by RetroBlit.
Maintainers
Readme
Blit-Tech
A palette-first WebGPU retro engine for TypeScript, inspired by RetroBlit. Draw with palette indices, animate with palette cycling and fades, and ship authentic VGA-era effects on modern GPUs.

Inspiration
Blit-Tech draws heavy inspiration from RetroBlit by Martin Cietwierkowski (@daafu) — a retro pixel demo framework for Unity that replaces the editor with a clean, low-level demo loop. Blit-Tech brings the same philosophy to the web using WebGPU: no scene graphs, no complex frameworks, just sprites, primitives, and fonts.
Features
- True indexed rendering: primitives and sprites write palette indices, not RGBA pixels
- Palette effects built-in: cycling, fade, flash, and swap run per frame with no per-sprite rewrites
- Built-in retro palettes: VGA, CGA, C64, Game Boy, PICO-8, and NES preset factories
- Palette offset variants: recolor one sprite sheet into team colors, states, or themes without duplicate textures
- Performance-first data model: tiny palette uploads (4 KB), smaller sprite textures, and compact primitive vertices
- WebGPU rendering with dual-pipeline architecture (primitives + sprites); automatic Canvas 2D software fallback
- Post-process effects: two-tier system — pixel tier on the
r8uintindex framebuffer; display tier on upscaled RGBA; bundled CRT presets - Primitive drawing: pixels, lines, rectangles (outline and filled)
- Sprite system: palette-indexed textures, palette offset, automatic texture batching
- Bitmap fonts: variable-width rendering from
.btfontfiles with palette offset support - Camera system: scrolling with offset/reset and world-bounds clamping via
BT.cameraClamp - Asset loading: sprite sheets and bitmap fonts with automatic caching
- Pointer input: mouse, touch, and pen unified under four slots; scroll delta; cursor control
- Keyboard input: raw keys via
KeyboardEvent.code, virtual face buttons, remapping, text accumulation - Gamepad input: up to four players via standard Gamepad API, stick dead zone, face buttons
- Fixed timestep: deterministic update loop with tick counter,
Timer, and timing helpers - Frame capture:
BT.captureFrame()andBT.downloadFrame()for PNG export
Why Blit-Tech?
| Feature | Blit-Tech | Typical 2D WebGPU engines | | ------------------------ | ----------------------------------- | -------------------------------- | | Rendering model | Native indexed palette pipeline | RGBA textures and framebuffers | | Color animation | Palette cycling/fade/flash built-in | Manual sprite or shader rewrites | | Global recolor/fade cost | One palette update | Scene redraw and blend passes | | Color variants | Palette offsets | Duplicate assets or tint logic | | Retro palette presets | C64, NES, Game Boy, CGA, VGA, etc. | Usually custom/manual only |
Prerequisites
Runtime (browser)
- A WebGPU-compatible browser (the engine falls back to Canvas 2D software rendering when WebGPU is unavailable):
- Chrome/Edge 113+ (Windows, macOS, Linux, Android)
- Firefox 141+ on Windows; 145+/147+ on macOS; Nightly on Linux and Android
- Safari 26+ (macOS Tahoe / iOS 26); or Safari 18–25 with WebGPU enabled via Feature Flags
App toolchain
- Node.js >=22.18.0 (LTS)
- An ESM bundler (Vite, webpack, esbuild, and similar) to load the published package in the browser
Installation
Install blit-tech from npm:
pnpm add blit-techOr with npm:
npm install blit-techPackage page: npmjs.com/package/blit-tech
Examples & Demos
For interactive examples and demos, visit the Blit-Tech Demos repository.
Quick Start
bootstrap() expects a canvas inside #canvas-container (defaults: canvas id blit-tech-canvas, container id
canvas-container). Pair the HTML below with a TypeScript entry module in any ESM bundler setup.
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My Blit-Tech Demo</title>
</head>
<body>
<div id="canvas-container"><canvas id="blit-tech-canvas"></canvas></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>src/main.ts
import { bootstrap, BT, Color32, Palette, Rect2i, type IBlitTechDemo } from 'blit-tech';
const BG = 1;
const WATER_A = 9;
const WATER_B = 12;
class MyDemo implements IBlitTechDemo {
async init(): Promise<boolean> {
const palette = Palette.c64();
palette.set(BG, new Color32(20, 30, 40, 255));
BT.paletteSet(palette);
// Animate every pixel that uses slots 9..12 (water/lava style cycling).
BT.paletteCycle(WATER_A, WATER_B, 6);
// Quick full-screen impact flash via palette manipulation.
BT.paletteFlash(Color32.white, 120);
return true;
}
update(): void {
// fixed-step logic here
}
render(): void {
BT.clear(BG);
BT.drawRectFill(new Rect2i(100, 100, 50, 50), WATER_A);
}
}
bootstrap(MyDemo);Scaffold and run (Vite + TypeScript):
pnpm create vite my-demo --template vanilla-ts
cd my-demo
pnpm add blit-tech
# Add the HTML and main.ts snippets above, then:
pnpm devDocumentation
See API: Core for bootstrap() options.
| Guide | What it covers |
| ---------------------------------------------------- | ------------------------------------------------------ |
| API: Core | bootstrap, game loop, camera, Timer, core types |
| API: Rendering | primitives, sprites, text, post-process, frame capture |
| API: Palette | palette setup, presets, effects, serialization |
| API: Assets | sprite sheets, bitmap fonts, asset loading |
| Input Guide | pointer, keyboard, gamepad |
| Palette Guide | palette-first workflow, offsets, effects |
| Palette Presets | built-in preset reference and exact color data |
| Post-Process Effects | effect chain, built-in effects, custom effects |
| Bitmap Fonts | .btfont format and BMFont conversion |
Browser Compatibility
WebGPU support varies by browser:
| Browser | Version | Status | | ----------- | -------------- | ---------------------------------------------------------------- | | Chrome/Edge | 113+ | Enabled by default | | Firefox | 141+ (Windows) | Enabled by default; 145+/147+ on macOS; Nightly on Linux/Android | | Safari | 26+ | Enabled by default; Safari 18–25 available via Feature Flags |
When WebGPU is unavailable the engine falls back to the Canvas 2D software renderer automatically. A dismissible
in-canvas "SOFTWARE RENDERER" banner appears to confirm the fallback is active. Use BT.activeBackend to detect which
backend is running at runtime.
Contributors
Contributor workflow, scripts, release process, and repository tooling docs live in Developer Experience and CONTRIBUTING.md.
License
ISC
