Release 1.0.3
Blit-Tech 1.0.3 is the first stable release of the engine, representing the full development arc from the 0.2.x prototype. The rendering stack is now palette-first end-to-end: draw calls write palette indices into an r8uint framebuffer, a PaletteResolveUpscalePass converts to RGBA just before output, and a Canvas 2D SoftwareRenderer kicks in automatically when WebGPU is unavailable. The input system is complete - pointer, keyboard, and gamepad all share the BTN_* bitmask API. The BT namespace has reached its stable shape, with configure(), init(), and IBlitTechDemo replacing their earlier names across the board.
API Changes
The BT namespace and demo lifecycle went through several rounds of renaming and hardening to reach their stable forms.
- Breaking:
IBlitTechGamerenamed toIBlitTechDemo;bootstrap()now accepts a demo class rather than a game class; default canvas ID changed fromgame-canvastoblit-tech-canvas. (#38) - Breaking:
BT.initialize()andIBlitTechDemo.initialize()renamed toinit()across the entire engine lifecycle -BTAPI,IBlitTechDemo,Renderer,PrimitivePipeline,SpritePipeline, andinitializeWebGPU()all shortened to theirinitequivalents. (#114) - Breaking:
IBlitTechDemo.queryHardware()renamed toconfigure()and made optional; demos that omit it fall back todefaultConfig()(320x240 display, 640x480 canvas, 60 FPS, nearest-neighbor upscale). (#115) BTAPIsplit into three focused modules:GameLoopowns the fixed-timestep accumulator and double-RAF canvas-ready delay;WebGPUContextowns adapter/device/context setup;BTAPIdelegates to both. (#42)BT.getActiveBackend()facade added and exposed on theBTnamespace, returning'webgpu','software', ornull. (#132, #134)BT.effectAdd,effectRemove, andeffectClearwired throughBTAPIfor managing the post-process chain from demo code. (#107)- Opt-in dropped-frame detection added via
HardwareSettings.detectDroppedFrames;GameLoopcalibrates a 60-frame rolling baseline and fires anonFrameDropcallback when the browser misses vsync deadlines. (#105) - Obsolete WebGPU-only bootstrap helpers removed:
checkWebGPUSupport,detectBrowser,getWebGPUInstructions,buildErrorPreviewEntries, andpreviewWebGPUErrors. (#133) FullscreenEffectandFullscreenPixelEffectexported from the package entry point so npm consumers can build custom post-process passes without reaching into internal paths. (#149)- Engine initialization hardened against common beginner mistakes: runtime guards catch pre-bootstrap draw calls, missing
awaiton asset loads, invaliddrawPixelargument shapes, and missingupdate/rendermethods on the demo class. (#39, #122) - All engine error messages rewritten to beginner-friendly plain language: friendly second-person voice, contractions, concrete valid examples, actionable next steps. A shared
src/utils/errorMessages.tsmodule preventsBootstrap.tsand canvas error handling from drifting apart. (#120, #121, #122, #123, #124, #125)
Rendering
The rendering architecture moved from a single-file WebGPU pipeline to a modular, palette-first, backend-agnostic system.
Renderer.tssplit intoPrimitivePipeline.ts(colored geometry pipeline, batched palette-index writes) andSpritePipeline.ts(palette-indexed textured quads, nearest-neighbor, auto-batched by texture). (#40)- Primitive pipeline reworked to write palette indices instead of RGBA colors; all
drawPixel,drawLine, anddrawRectcalls now resolve through the active palette on the GPU. (#87) IRendererinterface introduced as a backend-agnostic contract; existing WebGPU implementation migrated toWebGpuRendererimplementingIRenderer;BTAPIholdsIRendererinternally for backend selection. (#129)SoftwareRendereradded as a Canvas 2D fallback backend: palette-first rendering, camera offsets, filled/outline rects, Bresenham lines, indexed sprite blits, and bitmap text. Post-process effects throw with a clear redirect to the WebGPU backend. Activates automatically when WebGPU init fails; force withHardwareSettings.renderer: 'software'or?renderer=software. (#130)- BTAPI backend selection wired end-to-end; a dismissible in-canvas
SoftwareTickerbanner confirms fallback mode each frame. (#132) r8uintlogical framebuffer adopted for the WebGPU pipeline: the framebuffer stores one palette slot per pixel at logical resolution, pixel-tier effects run on the index buffer, thenPaletteResolveUpscalePassapplies the LUT and upscales to canvas resolution before display-tier effects. (#136)- Stackable post-process effect chain added with zero idle cost: pixel-tier effects (
PixelGlitch,PixelMosaic) run on ther8uintindex buffer; display-tier effects (BarrelDistortion,Scanlines, chromatic aberration, vignette) run on the upscaled RGBA output. Pre-configuredcrtPipBoy,amber, andgreenpresets included. (#107, #109) - Frame capture to PNG via GPU readback added:
BT.captureFrame()reads the framebuffer asynchronously and returns a PNG data URL;BT.downloadFrame()triggers a browser download. (#51)
Asset System
The palette and sprite asset pipeline grew from basic color storage to a fully integrated palette-first system.
Paletteclass introduced with 256-color support, VGA preset, serialization, named indices, GPU-compatible export, and a_dirtyflag that auto-propagates mutations to the GPU without requiring a redundantBT.paletteSet()call at the end of every update. (#85, #90)SpriteSheet.indexize()added for converting RGBA sprite pixels to palette indices via exact color matching;loadIndexed()convenience path chainsload,loadColorsIntoPalette, andindexizein one call. (#89)SpriteSheet.loadColorsIntoPalette()added: walks a PNG and registers every unique opaque color into the palette starting atstartSlot, sorted darkest-first by Rec.601 luminance by default. (#103)SpriteSheet.width,heightgetters andfullRect()helper added for whole-sheet source rectangles in sprite draw workflows. (#118)SpriteSheet.fromIndexedPixels()andgetIndexedPixels()added for CPU-side pixel access required bySoftwareRenderer. (#130)PaletteEffectsystem introduced viaPaletteEffectManager:CycleEffect(zero-alloc rotation),FadeEffect/FadeRangeEffect(timed interpolation with easing),FlashEffect(temporary color override with auto-restore), andpaletteSwap(instant two-entry exchange). Runs afterdemo.render()and before GPU upload; skipped entirely when no effects are active. (#94)Palette.applyHUD()convenience method added: writes six built-in HUD UI colors (white, bg, label, header, dim, code) contiguously fromstartSlotand registershud_*name aliases. (#135)- Built-in 6x14 system font embedded in the library: 95 ASCII glyphs as bit-pattern data, synchronous atlas builder, and
BT.systemPrint()/BT.systemPrintMeasure()for rendering text without loading external files. (#97, #98) - Asset load error messages rewritten with beginner guidance: status-specific font messages, hints for missing
/or./path prefixes, and wrong file extension detection. (#119)
Input
A complete unified input system was built from scratch covering all three device types.
PointerInputsubsystem added with four slots: slot 0 for mouse, slots 1-3 for touch/pen contacts in arrival order.BT.pointerPos,pointerDelta,pointerPosValid,pointerScrollDeltaexposed;BTN_POINTER_A/B/C/Dwired intobuttonDown/buttonPressed/buttonReleased. Mouse buttons follow the RetroBlit canonical mapping (A=left, B=right, C=middle, D=back). (#110)KeyboardInputsubsystem added:KeyboardEvent.codestate with edge detection, tick-based repeat, andbeforeinputtext accumulation intoBT.inputString.BT.keyDown,keyPressed, andkeyReleasedexposed;BTN_UP..BTN_SELECTmapped for players 0-1 viaDEFAULT_KEYBOARD_PLAYER0/1. Canvas gainstabIndexand auto-focus in bootstrap. (#111)- Keyboard face-button remapping added:
BT.inputMapSet,inputMapGet, andinputMapResetlet demos remap face buttons per player at runtime;DEFAULT_KEYBOARD_PLAYER0/1serve as the out-of-box defaults. (#112) GamepadInputsubsystem added: polling-based per-frame snapshots, dead-zone-filtered axes, player connectivity helpers, and repeat-capable edge detection.BTN_*constants migrated to bit flags (powers of 2); keyboard and gamepad semantics merged for players 0 and 1; players 2 and 3 are gamepad-only. Default stick dead zone is0.75. (#113)
Core and Utils
A set of standalone utilities was extracted and several engine-wide quality-of-life improvements landed.
Bootstrap.tssplit intoBootstrap.ts(demo bootstrap utilities) andBootstrapHelpers.ts(canvas lookup and error display), reducing coupling and making each file independently testable. (#41)Easingmodule added:EasingFunctiontype andapplyEasing()supportinglinear,ease-in,ease-out, andease-in-outcurves, used byFadeEffectandFadeRangeEffect. (#93)- Browser-specific WebGPU error guidance added:
detectBrowser()parses the UA string andgetWebGPUInstructions()maps each browser (Chrome, Edge, Firefox Nightly, Safari) to actionable flag pages, download links, and version requirements. Later consolidated intoSoftwareRendererauto-fallback and theerrorMessages.tsmodule. (#100, #102) BT.cameraClamp()added: wrapsclampCameraToWorld(camera, worldSize, viewSize?)from a newCameraUtilsmodule;viewSizedefaults toBT.displaySize. (#116)Color32.luminancegetter (Rec.601 formula) added andSpriteSheetluminance sorting migrated to use it, removing duplicate inline formulas. (#126)Color32.lerp(a, b, t)static helper added, delegating to the existing instancelerp. (#138)- Breaking:
Color32.white(),black(),transparent(), and color primary statics converted from zero-argument methods to static getters returning frozen singletons. Call sites must drop parentheses (Color32.whitenotColor32.white()).Color32.gray(value)is unchanged. (#139) - Named color registry added to
Color32:register,update,unregister, andresolveAPIs with normalized key handling, duplicate/missing-name validation, andhud_*name aliases populated byPalette.applyHUD(). (#128) Timerutility added for fixed-timestep interval checks in update loops; exported from the public API. (#127)IndexedSpriteLoadResultexported as a named type so demos can annotateSpriteSheet.loadIndexedresults without importing from internal paths. (#116)
Tests
Comprehensive test coverage was built from scratch across all four test tiers.
- Full unit and integration test suite introduced covering
Vector2i,Rect2i,Color32,GameLoop,AssetLoader,BitmapFont,SpriteSheet,Renderer,PrimitivePipeline,SpritePipeline,BootstrapHelpers, andIBlitTechDemo.src/__test__/webgpu-mock.tsprovides reusable GPU mock factories. (#46, #48, #49) - Playwright visual regression suite introduced for pixel-level rendering verification: primitives, sprites, fonts, mixed scenes, post-process (baseline, CRT, CRT+bloom), camera, and software-mode baselines. (#47, #50)
- CPU benchmark infrastructure added via Vitest bench:
pnpm benchandpnpm bench:jsonscripts;Vector2i,Color32,Rect2i, andBitmapFontbenchmark suites with#regionorganization. (#75, #76, #77, #78, #79) - Playwright GPU frame-time performance fixture added (later removed; see CI section). (#80)
- Visual regression baselines refreshed after software renderer ticker changes. (#117, #141)
- 80% unit test coverage threshold enforced via
pnpm test:unit:coverage. (#50)
CI and Tooling
CI was rebuilt from scratch with DCO enforcement, benchmark automation, and a significant toolchain upgrade.
- DCO sign-off enforcement added to CI; merge commits skipped in DCO validation. (#8, #10)
- pnpm version pinned in CI;
commitlintconfigured via environment variables rather than config drift. (#11) - Changesets infrastructure removed in favor of manual releases;
.changeset/directory andCHANGELOG.mddeleted. (#37, #44) - Benchmark CI job added: runs Vitest bench on every push to
mainand on PRs labeledperf; uploads results as artifacts; compares against the latestmainbaseline; comments on the PR and fails on regressions greater than 25%. (#81) - Tier-2 GPU performance tests (Playwright frame-time benchmarks) removed after consistently timing out on hosted runners with no real GPU.
perf-tier-1CI label renamed toperf. (#82, #108) Run TestsCI job enabled and test command fixed. (#88)- Major dev dependency upgrade: TypeScript pinned to 5.9.3 (to align with API Extractor for declaration rollup), Vite 8, ESLint 10, knip 6, cspell 10. (#104, #143)
Documentation
API reference guides (docs/api-core.md, docs/api-assets.md), palette guide, post-process effects guide, and testing documentation were introduced and continuously updated alongside every feature. Architecture maps in CLAUDE.md updated to reflect the final subsystem layout. Node minimum requirement aligned to >=22.18.0 across all docs and CI. (#83, #84, #95, #137, #142, #144, #145, #146, #147, #148)
Full Changelog: 0.2.0...1.0.3