Releases: blit386/blit386
Release list
Release 1.4.0
You change a line, hit save, and the game keeps running – ticks, camera, palette still where you left them. That is what 1.4.0 is about: true engine hot reload, fast enough that you forget the page used to blink, with a blit386/vite plugin, in-place asset hot-replace, and a loading-progress API for the screens you still have to wait for. CI, agent tooling, and docs catch up around the edges.
Hot Reload
Edit, save, keep playing – that is the whole trick.
- Engine-side hot-swap runtime (
src/hot/):registerHotReload, optionalIBTDemo.onHotReload(context), and a three-tier swap – method-only prototype rebind when only method bodies change, full re-init wheninit()or the constructor changes, full page reload when hardware settings change. Ticks, camera, palette, and palette effects persist across method and re-init swaps. A secondbootstrap()under a registered hot context routes to a swap instead of starting a secondGameLoop; without one, it logs and returnsfalse. (#374) blit386/vitedev-server plugin (import { blit386 } from 'blit386/vite'): injects the hot-reload registration snippet into entry modules that callbootstrap(and import from'blit386', watches configured asset directories (defaultpublic/), and broadcastsblit386:asset-changedHMR events (full reload for unrecognized extensions). Ships as a second build entry (dist/vite.js/.cjs/.d.ts) with no runtime dependency onviteitself. (#375)- In-place asset hot-replace for images, audio, and
.btfontfiles:AssetLoader.evict,SpriteSheet/AudioClip/BitmapFonthot-replace (dev registries gated on an active hot context), and music restart viaMusicPlayerwhen the current track's buffer is replaced. A broken asset event cannot crash the game loop. (#376) - Tier 3 under
?backend=softwareno longer forces a full reload on every edit – the hardware-settings diff now reapplies the URL backend override on the candidate before comparing. Plain.js/.mjsentries are syntax-checked with acorn before snippet injection so Vite's error overlay surfaces parse failures that previously only failed on the client. (#378)
Asset System
Loading screens finally get a real signal to poll.
BT.loadingAssetsCountreturns the combined in-flight image + audio load count (AssetLoader.loadingCount+AudioClip.loadingCount).SpriteSheet.status('loading' | 'ready' | 'failed') and coarseSpriteSheet.progress(0or1.0) expose per-sheet hot-reload replacement state for loading UI. (#388)
API Changes
Deprecation tags grow version stamps; dead internal aliases leave.
- Every remaining date-only
@deprecatedJSDoc tag insrc/is upgraded toDeprecated since <version> (2026-05-31)., and dead internal-only compatibility aliases (PaletteView,RenderPaletteUsage,AssetLimits,OverlayToggleIcon) with no public callers are removed.docs/reference-deprecations.mdnow lists a removal target of2.0.0per alias group. (#406)
Input
Touch scrolling follows the same opt-in as pointer wheel capture.
canvas.style.touch-actionis no longer hardcoded tononeon attach. It tracksHardwareSettings.isCapturingPointerScroll(and the overlay palette band's forced capture):nonewhile capturing,pan-yotherwise, updated live so touch tap-hold-scroll past the canvas works when scroll capture is left at its defaultfalse. (#387)
Security
The expired esbuild exception goes away for real.
pnpm.overrides.esbuildbumps to^0.28.1, theGHSA-gv7w-rqvm-qjhraudit ignore is removed, and the Active exceptions row is cleared.esbuildis added as a directdevDependencyso Vite 8's optional minify peer is present;fast-uriis pinned to clear the restored audit gate. (#390)
Developer Experience
Remote sessions and agent tooling stop needing a scavenger hunt.
- Fresh remote/web sessions auto-bootstrap via
scripts/session-start-bootstrap.sh(Claude CodeSessionStart, CursorsessionStart, optional.devcontainerpostCreateCommand): Corepack enable when needed, thenpnpm install --frozen-lockfile, skipped when the lockfile checksum stamp still matches. (#383) - Missing
.claude/rulesmirrors for Cursor-only rule files restore Claude/Cursor parity ahead of the drift checker. (#379) agents:checkverifies.cursor/rules↔.claude/rulesparity,.agents/skills/*symlink integrity, and theAGENTS.md→CLAUDE.mdpointer; wired intopreflightand the CI quality job. (#381).github/copilot-instructions.mdpoints Copilot agents atAGENTS.md/CLAUDE.mdand the hard rules (no emoji, integer coordinates,BTnamespace, American English, Conventional Commits + DCO). (#385).cursor/commands/are generated from.claude/skills/*/SKILL.mdviascripts/sync-cursor-commands.mjs, withsync:cursor-commands:checkandtest:cursor-commandsin preflight and CI. (#386)agents:checkalso covers Copilot instructions presence and Zed settings JSON consistency; the annotatedsrc/architecture tree moves into a mirrored rule pair andCLAUDE.mdshrinks further. (#410)- Agent rules, skills, and command mirrors cleaned up after the architecture extraction – stale cross-refs and duplicated guidance trimmed. (#413)
CI and Tooling
Gates tighten, Renovate wakes up, and duplicate work gets cut.
- Markdown link checks run with concurrency 8; quality and bundle-size checks move into
ci.yml(including Knip and a 70 KB gzipped ESM limit), dropping the duplicate jobs frompr-checks.yml. (#380) checkFilesettles exactly once when botherrorandclosefire, and each markdown-link-check child gets a timeout so a hung process cannot stalldocs:linksforever. (#382)docs:linksenumerates files withgit ls-files "*.md" "*.mdx"so checks honor.gitignoreand skip symlink duplicates under.agents/skills/*. (#391)preflightgainstest:api-historyandtest:security-preflight; CI quality runsapi:since:check/api:history:check(withfetch-tags: true), and PR-scoped workflow concurrency cancels duplicate runs. (#394)- Dormant
renovate.jsonrepaired: real package matchers, 7-dayminimumReleaseAgealigned with.npmrc, and Dependabot (security-only) vs Renovate (versions + Actions digests) documented. (#395) - Renovate commits get
:gitSignOffand lowercasecommitMessageAction: "update"so they pass DCO and commitlint; workflownode/pnpmpins are excluded from Actions digest updates. (#401) - GitHub Actions digests and the workflow Node version bump (
22.18.0→22.23.1) via Renovate. (#402) - Dead root configs removed (
.npmignore,.markdownlint.json); inert options trimmed in Vite, Biome, Prettier, and knip configs. (#405) - Guided GitHub issue forms (bug / feature / docs), issue chooser with contact links, and a PR template that surfaces DCO, Conventional Commits, preflight, and visual-test checklist. (#407)
- Biome / ESLint / Prettier patch bumps (
@biomejs/biome2.5.4, ESLint 10.7.0, Prettier 3.9.5). (#404) pnpm run lintuses--max-warnings 0; BiomenoExplicitAny,noNonNullAssertion, andnoParameterAssignpromote from warn to error so full-repo lint matches pre-commit. (#408)- CodeRabbit reviews gated on the
crlabel only, and path filters / path instructions expanded for generated mirrors and house TypeScript / docs rules. (#411) (#412) - Post-1.3.1 API-history bookkeeping:
UNRELEASED_VERSIONadvanced anddocs/_api-history.jsonregenerated for the next cycle. (#373) - Release tracking advanced toward 1.4.0 in the API-history generator metadata. (#389)
Tests
Post-process effects finally carry their own coverage floor.
- Unit coverage for
FullscreenPixelEffect/FullscreenEffect,PixelGlitch/PixelMosaic, post-process tier dispatch, and software-rendererBT.effectAddrejection; 80% per-directory threshold forsrc/render/effects/**. (#392)
Documentation
Hot reload gets a guide; the rest of the docs catch up.
- New
docs/guide-hot-reload.md: swap tiers,onHotReload/HotReloadContext, theblit386:hot-reloadDOM event, the asset hot-replace matrix, full-reload triggers, and theblit386/viteplugin – published via the sitemap. (#377) CLAUDE.mdtrimmed from a session-loading monolith into short summaries with pointers; rule files become the canonical detail;AGENTS.mdgrows into a real standalone quick start. (#384)- Changelog gains the missing 1.4.0 editorial entry for the asset loading-progress API (
BT.loadingAssetsCount, loader counters,SpriteSheet.status/progress). (#393) - Stale agent line citations removed,
SECURITY.mdpoints at GitHub private vulnerability reporting only, andCONTRIBUTING.mdgets a Getting Started path. (#409) - Docs index, hot-reload cross-links, loading-progress guidance, and related API/guide pages refreshed after the 1.4.0 surface settled. (#414)
Full Changelog: 1.3.1...1.4.0
Release 1.3.1
This release focuses on mobile and embedding-friendly hardware controls: screen orientation detection with an optional lock, a Screen Wake Lock API to keep the display awake during gameplay, and opt-in scroll capture for both the pointer wheel and the keyboard so an embedded canvas no longer hijacks page scrolling by default. The overlay's toggle hit region shrinks further with a new debug outline, and a dependency bump closes nine open Dependabot alerts.
Core and Utils
Two new opt-in hardware subsystems land alongside a screen orientation getter.
- Screen orientation detection and optional lock:
BT.screenOrientationreads the currentscreen.orientation.type(nullwhen unavailable),IBTDemo.onOrientationChange(type)fires on rotate, andHardwareSettings.preferredOrientation('landscape' | 'portrait' | 'any', default'any') attemptsscreen.orientation.lock()afterinit(). Unsupported or rejected locks are silent no-ops and never fail initialization. (#370) HardwareSettings.isWakeLockEnabled(defaultfalse) requests a screen wake lock after a successfulinit()to stop mobile browsers from dimming or locking the screen during active gameplay, and re-acquires it automatically once the page returns to the foreground. Backed by a new internalWakeLocksubsystem; not exported as a publicBT.*method. (#368)
Input
Both canvas scroll paths move from unconditional capture to an explicit opt-in.
- Breaking: pointer wheel capture is now opt-in via
HardwareSettings.isCapturingPointerScroll(defaultfalse). The canvas used to callpreventDefault()on every wheel event unconditionally, blocking host-page scroll the moment the pointer crossed the canvas even when nothing readBT.pointerScrollDelta. Games that map scroll input must now set the flag; the overlay's palette band still force-captures scroll so palette scrolling never leaks to the page. (#367) HardwareSettings.isCapturingKeyboardScroll(defaultfalse, additive) callspreventDefault()on canvaskeydownfor the arrow keys, Space, Page Up/Down, Home, and End when enabled, so games can map those keys without scrolling the host page. (#369)
Overlay
- The bottom-left toggle hit region shrinks from
48×48to17×13pixels to match the visible toggle icon, and a newisOverlayToggleHitDebugVisibleflag (defaultfalse) draws a 1px outline of that region for tuning. (#366)
CI and Tooling
- 15 devDependencies bumped and
pnpm-lock.yamlregenerated, resolving all 9 open Dependabot alerts (js-yaml,undici,@babel/core).contents: readpermissions added to thequality,commitlint, andbundle-sizeGitHub Actions jobs, closing 3 open code-scanning alerts. (#365)
Documentation
- API history updated with the
1.3.0release timestamp, and audio and game-loop demos embedded across the Audio API, overlay API, and audio and game-loop guide pages. (#364)
Full Changelog: 1.3.0...1.3.1
Release 1.3.0
This is the audio release: the engine gains a full sound subsystem – bus mixing, streamed clip loading, a voice pool with priority stealing, deterministic synthesis, and crossfading music – plus BT.renderAlpha for smoother motion between fixed update steps. The overlay gains audio level meters and drift-free dividers, a handful of camera and frame-capture bugs are fixed, and the published docs move to a site-first workflow (Fumadocs components, Twoslash-checked examples, per-symbol version history) for blit386.dev.
Audio
The engine's first audio subsystem, built around a three-bus Web Audio graph.
- Added a full audio subsystem:
AudioManagerrunssfxandmusicbuses intomain, with per-busBT.audioVolumeSet/BT.audioVolumeGet,BT.audioMuteSet/BT.isAudioMuted, and theBT.isAudioUnlockedgetter tracking the first-gesture autoplay unlock.HardwareSettings.audioVoices(default16, range1-64) sizes the SFX voice pool. (#327) AudioClipasset loading: streamed fetch and decode with byte-accurate progress, per-URL caching with in-flight request deduplication, and ordered fallback URL lists for browsers that cannot decode the first choice.AudioClip.loadAllruns requests in parallel;unload()releases decoded buffers. (#328)- Sound-effect playback through a fixed-size voice pool with priority-based stealing:
BT.soundPlay,BT.soundStop,BT.isSoundPlaying, per-voiceBT.soundVolumeSet/BT.soundPitchSet/BT.soundPanSet(plus getters), and generationalSoundRefhandles that safely no-op once stolen or stopped. (#329) - Deterministic procedural sound synthesis via
AudioClip.synth(params)– waveforms, an ADSR envelope, a linear pitch sweep, vibrato, and noise mixing rendered on the CPU with no source file – plus a preset library,BT.synthPreset.jump/pickup/explosion/laser/hit/blip. (#330) - Crossfading music playback:
BT.musicPlay,BT.musicStop,BT.musicVolumeSet/BT.musicVolumeGet, and theBT.isMusicPlayinggetter, with configurable fade duration, crossfade overlap, and loop points. A music request made before the audio context unlocks is remembered and starts automatically on the first gesture. (#333) - Overlay audio meters: a per-bus level band plus a voices used/total, steal, and drop readout, gated behind
HardwareSettings.isOverlayAudioMetersEnabledwith lazyAnalyserNodecreation so metering costs nothing when disabled. (#337)
Overlay
Two toggle-timing and layout fixes land alongside the new audio meter band.
- The overlay's Backquote toggle press is now sampled during the fixed-update tick instead of the render phase, fixing intermittently dropped toggle presses under rapid input – previously missed roughly 20% of the time on 120+ Hz displays. (#314)
- Engine-composed overlay label separators (backend/resolution, frame metrics, timing, renderer diagnostics, the audio meter voice readout) now render as 1px gap-colored divider fills instead of
|glyph text, with wider edge margins and divider gaps. (#339) - Numeric overlay fields (FPS, frame/update/render milliseconds, the update-step suffix, renderer diagnostics counts) are now padded to a fixed character width, fixing divider drift that occurred whenever a value's digit count changed between frames. (#341)
Rendering and Camera
- The camera offset now persists across render frames that run zero
update()steps, so scrolling no longer snaps back to the previous frame's origin on high-refresh displays; the render-loop delta is also snapped to the nearest fixed-update interval multiple to reduce timing drift. (#343) - WebGPU canvas configuration now requests
COPY_SRCusage alongsideRENDER_ATTACHMENT, fixingBT.captureFrame()/BT.downloadFrame()producing fully transparent PNGs on backends that enforce texture-usage validation strictly. (#335)
Asset System
BitmapFont.measureTextSize()now allocates a fresh{ width, height }object on every call instead of returning a shared internal instance, fixing two outstanding results silently aliasing each other.measureTextSizeInto()remains the zero-allocation option for hot loops. (#334)
Core and Utils
- Added the read-only
BT.renderAlphagetter: the fractional interpolation progress in[0, 1)between fixedupdate()steps, for smoothing motion duringrender()on displays that render faster than the update rate. (#344)
CI and Tooling
- Independent quality-check steps (format, lint, typecheck, spellcheck, docs:links, doc-banner check) now run concurrently in CI via
background: true/wait-all, cutting overall pipeline time. (#313)
Documentation
The published docs move to a site-first workflow for blit386.dev, with a large restructuring and tooling pass alongside.
- A self-maintaining blit386.dev banner now appears below the H1 of every published doc, generated from
docs/_sitemap.jsonand kept in sync bypnpm run sync:doc-banners/sync:doc-banners:checkin CI. (#307) - Published docs adopt Fumadocs MDX components (
Callout,TypeTable,Steps,Tabs,Accordions,Cards) across 15 pages for a more interactive read on the site. (#308) - The oversized
api-core.mdpage is split into six focused pages (overlay, game loop, camera, easing, core types, browser support); guides, reference, and performance docs are renamed to mirror their sitemap section (guide-*,reference-*,performance-*), and a house prose style (no bold, no---rules,×for dimensions) is codified. (#309) - Documentation and rule/skill files across the repo drop decorative bold emphasis and reflow headings and lists for consistency;
CLAUDE.mdis condensed. (#310) - All TypeScript code blocks in published docs now use
ts twoslash, enabling type-on-hover popups on the live site; every block was rewritten to compile standalone. (#311) - Broadened
markdown-link-checkignore patterns to stop CI false failures onblit386.devanddemos.blit386.devlinks. (#315) - Dash typography (en dash for parenthetical breaks and ranges) standardized across documentation. (#316)
- README rewritten with a beginner-first voice and updated to point at the typeset docs on blit386.dev. (#317)
- Added interactive demo embeds across API and guide pages (assets, camera, core, easing, game loop, palette, rendering, bitmap fonts, input, overlay, palette presets, post-process effects) – documentation only, no API changes. (#318)
- Introduced
scripts/gen-api-history.mjs, generatingdocs/_api-history.jsonfrom@since/@changed/@deprecatedJSDoc tags on every public symbol; addedapi:history,api:history:check, andapi:since:checktopnpm run preflight, plus<Since>,<ApiAvailability>, and<PageChangelog>doc components and a newdocumentation-and-versioning-guide.md. (#331) - Wired the
@since/@changed/@deprecatedtagging workflow into the docs-sync rules so adding or changing a public API symbol reliably surfaces the required tags and theapi:historyregeneration step. (#332) - Normalized British spellings to American English (
optimisationtooptimization,cancelledtocanceled) across docs and JSDoc, and codified the convention as a repo rule. (#338) - Documented Safari Low Power Mode / WebKit
requestAnimationFramethrottling and its effect onrender()cadence versus the fixed-update accumulator, including the overlay'sxNmulti-update indicator. (#342) - Audited documentation, rules, and skills against the shipped 1.3.0 surface and closed the gaps: added the changelog entry, tagged
HardwareSettings.audioVoicesand the synth pitch/vibrato types with@since 1.3.0, corrected several audio-subsystem JSDoc comments that still described a future phase, and removed a leftover "coming soon" placeholder section. (#345)
Full Changelog: 1.2.1...1.3.0
Release 1.2.1
Keyboard edges were getting eaten on 120+ Hz displays – fast taps between fixed update steps could just disappear. Fixed now. Docs got a proper cross-reference pass too, which honestly should have been there from the start, and .mdc files finally land in the spellcheck and formatting hooks.
Core and Utils
A single targeted fix to keyboard edge buffering.
KeyboardInputnow buffers press and release edges from DOM events intopendingPress/pendingReleasebuffers, which are consumed and cleared byendUpdate()at the fixed update boundary. PreviouslyendFrameran once per render frame whileupdateran attargetFPS; on 120+ Hz displays with a 60 FPS target this caused fast taps and releases to be dropped between fixed steps. The newendUpdate(_currentTick)is the canonical boundary;endFramebecomes a deprecated alias forwarding to it. (#304)- Stray
keyupevents for keys that were not actually held no longer produce a released edge; guard added inside the DOM event handler. (#304)
Documentation
Cross-referencing and navigation improved across the full docs surface.
- "See Also" guide tables added or expanded in 16 documentation files (
docs/api-core.md,docs/api-palette.md,docs/api-rendering.md,docs/api-assets.md,docs/bitmap-fonts.md,docs/deprecations.md,docs/input.md,docs/overlay.md,docs/palette-guide.md,docs/palette-presets.md,docs/performance-best-practices.md,docs/performance-testing.md,docs/post-process-effects.md,docs/testing.md,docs/tooling.md,docs/voice.md) and the three security docs. Related guides now link to each other. (#303) README.mdrewritten with a beginner-first voice: leads with a completebootstrap()-based sample, adds a "Load a sprite and draw it" walkthrough and a palette cycling example, removes the marketing blocks, and trims Prerequisites down to what's actually required. (#303)docs/api-core.mdgained a "Browser support" section describing the WebGPU-first / Canvas 2D fallback behavior. (#303)BT.inputStringtiming semantics indocs/input.mdcorrected: the typed-text buffer clears at the end of each fixed update step (read duringupdate()), not at end-of-frame. (#304)
CI and Tooling
Dev tooling got broader .mdc coverage and more robust link-checking.
.editorconfig,.lintstagedrc.json, and thespellcheckscript now include*.mdcfiles alongside*.mdand*.mdx. Thebt-spellcheckskill documentation updated to match. (#305).husky/commit-msgnow quotes"$1"for safer path handling;.husky/ensure-pnpm.shonly sourcesnvmwhen running under bash or zsh. (#305)scripts/check-markdown-links.mjstreatsresult.erroras a failure condition (previously onlyresult.status === 'dead'was caught); documentation updated to reflect full recursive scanning. (#305).github/markdown-link-check.jsonignore patterns narrowed to match/blit386or/create-blit386specifically;demos.blit386.devadded as an ignored domain. (#303, #305).gitignoreaddspnpm-store/;.prettierignoreaddsyarn.lock. (#305)"Waku"added tocspell.json;"gamedev"added topackage.jsonkeywords. (#303, #305)
Full Changelog: 1.2.0...1.2.1
Release 1.2.0
Release 1.2.0
The package is blit386 now. Was blit-tech, which nobody could spell anyway – dist files, default canvas ID, and the public demo interface (IBTDemo, replacing IBlitTechDemo) all follow. A renderer fix lands alongside: module-level GPUTextureUsage access was crashing non-WebGPU browsers at load time; it's inlined at call sites now. The rest is housekeeping.
API Changes
The rename is a breaking change – update imports and type annotations.
- Breaking: The npm package is now
blit386(wasblit-tech). Import paths,package.jsonname, repository URLs, and dist output filenames (blit386.js,blit386.cjs,blit386.d.ts) all updated. The public demo interface is nowIBTDemo(wasIBlitTechDemo).DEFAULT_CANVAS_IDis now'blit386-canvas'(was'blit-tech-canvas'). Default frame-capture filename is now'blit386-capture.png'. TheREGISTRY_TITLE_PATTERNinlabels.tswas also fixed to matchBLIT386 Demotitles (the regex was already broken before the rename). (#282)
Rendering
A load-time crash on non-WebGPU browsers is fixed, and a previous dynamic-import workaround is reverted.
- Three module-scope constants (
SCENE_TARGET_USAGE,OFFSCREEN_USAGE,OUTPUT_USAGE) evaluatedGPUTextureUsageat module load time, crashing browsers without WebGPU globals (e.g. Firefox on Linux). They are now inlined at their call sites.WebGPURendererreverts to a static import; the dist output is back to two files instead of the split chunks introduced by the prior dynamic-import workaround. (#272)
Security
A temporary audit exception covers an esbuild CVE while the minimum-release-age gate clears.
GHSA-gv7w-rqvm-qjhr(esbuild) is accepted as a temporary audit exception via--ignoreon thesecurity:auditscript. The patched release (esbuild 0.28.1) was published too recently for the seven-day release-age gate;pnpm.auditConfig.ignoreGhsasdoes not work in pnpm 10.x, making the CLI flag the only effective path. Exception documented indocs/security/audit-exceptions.mdwith a review-by date of 2026-06-21. (#278)
CI and Tooling
A batch of identity cleanup, config extractions, and dependency bumps rounds out the release.
- All internal references to
vancura/blit-tech/Blit-Techupdated toblit386/blit386/blit386across Claude skills, Cursor rules, the GitHub security-risk acceptance template, and editor config. Biome bumped from 2.4.15 to 2.5.0; CSS linter rules migrated to the newpreset: "recommended"form.cspell.jsontidied (reordered entries, removed redundantrasterizing). (#291) vitebumped from 8.0.14 to 8.0.16 (Dependabot). (#281)- Engine logo updated. (#300)
package.jsonmetadata updated (repository URL, homepage, bugs link) to match the newblit386/blit386identity. (#280)lint-stagedconfiguration extracted frompackage.jsoninto a dedicated.lintstagedrc.jsonfile. No behavior change. (#275)bt-issue-auditClaude skill added: re-audits open GitHub issues against the current codebase and posts an audit-update comment only when the status has materially changed since the last audit. Mirrored into.agents/skills/for Zed. (#273)- Claude skill descriptions expanded for
bt-deep-review,bt-format,bt-pr,bt-preflight,bt-quick-format,bt-review,bt-spellcheck, andbt-testto include "Use when..." guidance for better skill selection. (#277) AGENTS.mdentry point added;.claude/rules/docs-sync-required.mdrule added defining when documentation must be updated alongside implementation changes. PostToolUse hooks in.claude/settings.jsonupdated to usepnpm execfor formatting and spellchecking. (#276)
Documentation
Onboarding now leads with the scaffolder rather than manual Vite boilerplate.
README.mdQuick Start replaced with thenpm create blit386@latestscaffolder workflow. Manual Vite setup boilerplate removed.docs/api-core.mdgets a callout near the top directing new users to the scaffolder.CLAUDE.mddocuments the new "Onboarding and the scaffolder" section. A docs-sync rule added requiring scaffolder templates, kit docs, and pinned version ranges to stay in sync when onboarding surface changes. (#274)
Full Changelog: 1.1.1...1.2.0
Release 1.1.1
The biggest thing in 1.1.1 is a crash fix that should have been caught earlier: WebGPURenderer was imported statically, which evaluated GPUTextureUsage at module load time, which meant Firefox – and anything else running without WebGPU globals – died before the software fallback could do anything about it. That's fixed. Everything else is cleanup: the BT API got a naming pass so boolean queries, predicates, and HardwareSettings config flags all follow is*/has* consistently, the asset subsystem went through a structural refactor across BitmapFont, SpriteSheet, Palette, PaletteEffect, and SystemFont, and documentation was corrected and expanded across 45+ files, including a new overlay guide that probably should have existed a year ago.
API Changes
A large naming normalization pass touches the BT namespace and related input/config surfaces.
- Boolean queries (
isDown,isPressed,isPointerActive), predicate methods (isEqual,isContaining,isIntersecting), andHardwareSettingsconfig flags (isOverlayEnabled,isDetectingDroppedFrames) now all followis*/has*conventions consistently across the board. Deprecated aliases keep previous names alive for backward compatibility. (#241)
Asset System
Seven refactor PRs tighten control flow, strengthen validation, and extract focused helpers across every asset class.
- WebGPU mock factories and test-time stubs consolidated across asset loading, palette management, sprite-sheet processing, font generation, and preset data. Shared helpers introduced; duplication removed. (#256)
BitmapFontload helpers, glyph lookup, and measurement now use single-return control flow..btfontJSON is parsed asunknownand narrowed through a runtimeFileDataguard so texture and glyph fields are validated before use. Benchmark glyph seeding merged into one loop. (#257)AssetLoader.buildExtensionHintuses a single return path, matching the style used elsewhere in the asset system. (#258)Palette.fromJSONvalidates with positive checks and loads colors and names in one loop.Palette.setusesif/elsefor slot 0 vs other indices; constructor positioned before theisDirtygetter. (#259)PaletteEffectManager.update,CycleEffect.update,FlashEffect.update, andpaletteSwapcollapsed to single return points. Flash apply/restore loops extracted into module helpers soupdate()stays loop-free. Related test assertions deduplicated. (#260)SystemFont.writeGlyphPixelsreplaces nestedx/yloops with a single flat index loop, preserving atlas write semantics and bitmap bit decoding. (#261)SpriteSheetcolor collection, palette-fit validation, palette writes, and usage-mask marking extracted into focused helpers. Method complexity reduced; dead oversized-image canvas stubs removed from tests. (#262)
CI and Tooling
Zed agent skill support landed, editor configs were cleaned up, and // #region markers were removed everywhere.
.agents/skills/added with symlinks to.claude/skills/for Zed skill discovery. All skills renamed withbt-prefix (perf→bt-perf,release→bt-release,test→bt-test). Requirednamefrontmatter added to allSKILL.mdfiles for Zed validation. (#253)- Dead VS Code debug launch config removed.
.zed/settings.jsonis now tracked (.gitignoreupdated to mirror the.vscodepattern). Prettier coverage expanded to.mdcfiles. Zed agent terminal/git/write/edit allowlists tightened. (#254) // #regionand// #endregionmarkers stripped fromsrc/,scripts/, tests, and benchmarks. CLAUDE.md anddocs/testing.mdupdated to remove guidance that recommended region markers for long files. (#263)- Broad reformat pass across documentation and internal code structure. No public API or behavior changes. (#266)
Rendering
GPU acronym casing normalized to uppercase across all identifiers.
WebGpuRenderer,disposeGpuState,renderDimensionGpuLimitError, and related identifiers renamed to their uppercaseGPUforms (WebGPURenderer,disposeGPUState,renderDimensionGPULimitError). Changes touch source, tests, and documentation; behavior is unchanged. (#267)
Core and Utils
The software fallback was unreachable on browsers without WebGPU globals.
- Static import of
WebGPURendererwas evaluatingGPUTextureUsageat module load time, crashing Firefox on Linux and any browser without WebGPU globals before the software backend could start.WebGPURendereris now loaded dynamically only after WebGPU init succeeds, so the software fallback can actually reachinit. (#240)
Tests
- Visual test specifications and fixtures refactored for naming consistency and code organization. Linting improvements across test specs. (#255)
Documentation
Four PRs corrected factual accuracy and added new guides across the full doc set.
- Cursor glob-scoped rule and Claude Code rule added documenting
src/file layout, class member order as enforced byperfectionist/sort-classes, and the ban on// #regionmarkers. Mirrors CLAUDE.md and the developer-experience guide. (#265) - CLAUDE.md architecture map updated to reflect
SystemFont,Timer,fullscreenVS, overlay constants/labels/types, and theconsumer-doc-importstest guard.BTAPI methods list expanded with grouped categories, deprecated aliases, and top-level package exports. (#268) - New
docs/overlay.mdcovering subsystem layout, visibility model, customoverlayRows, optional bands, HUD palette slots, and the present/target FPS distinction. Factual accuracy corrected acrossapi-assets.md,api-core.md,api-utils.md, and other existing guides. (#269) - 45+ files of JSDoc precision work: all
{@link}tags qualified to fullinterface.memberform,paletteSetvs in-place mutation semantics clarified,spritesRefreshdocumented for when it is actually required (layout swaps, not value changes),effectAdd/effectRemove/effectClearcorrected. (#270)
Full Changelog: 1.1.0...1.1.1
Release 1.1.0
Version 1.1.0 is the overlay release. See the NPM package.
The old software-mode ticker is gone, replaced by a proper engine HUD that now lives in its own src/overlay/ subsystem: a toggle-able bar stack with a live palette swatch grid, and a scrolling timing chart that grew grid lines, severity tints, event tags you drop with BT.assignTag, and renderer pipeline diagnostics. Two breaking vocabulary changes ride along. HardwareSettings.renderer is now backend, the canvasDisplaySize / maxCanvasDisplaySize pair moves to a drawingBufferSize / maxCanvasSize resolution model, and every statsOverlay* setting drops its stats prefix.
API Changes
Three breaking renames clean up the public configuration vocabulary, plus one new getter.
- Breaking: the rendering-mode API renamed from
renderertobackend.RendererBackendis nowBackend,HardwareSettings.rendereris nowHardwareSettings.backend, and the URL override is?backend=software(was?renderer=software).BT.activeBackendkeeps its name and now explicitly reports the backend that actually started after fallback. (#176) - Breaking:
HardwareSettingsdisplay fields move to a resolution model.canvasDisplaySizebecomesdrawingBufferSizeandmaxCanvasDisplaySizebecomesmaxCanvasSize;BT.canvasDisplaySizeis replaced byBT.drawingBufferSize, andBT.outputSizenow derives fromdrawingBufferSize ?? displaySize.configure()merging also rejects invalid sizing via validation instead of throwing on clone. (#189) - Breaking: the stats overlay was promoted to a top-level
src/overlay/subsystem and everystatsOverlay*HardwareSettingsfield, thestatsOverlayRows()hook, and theStatsOverlay*exported types lost theirstats/StatsOverlayprefix (overlayEnabled,overlayRows(),OverlayRow,OverlayStyle, and so on). TheSTATS_OVERLAY_NO_BACKENDerror constant is nowOVERLAY_NO_BACKEND. No compatibility shims remain. (#226) - Added
BT.requestedBackend, a getter for the resolved init backend (afterconfigure()merge and?backend=software). It stays fixed even when WebGPU falls back to software; useBT.activeBackendfor runtime feature gating. (#178)
Asset System
- Redrew the ampersand glyph (codepoint 38) in the system font and regenerated
systemFontData.tsfrom the updatedassets/system-font.png. (#180)
CI and Tooling
Tooling housekeeping ahead of the public release: skill namespacing, link checking, and dependency bumps.
- Namespaced the Claude skill commands under
bt-*(/preflightis now/bt-preflight, and so on) and standardized every doc, workflow, andpackage.jsonscript onpnpm run <script>. (#172) - Expanded
.gitignore/.npmignoreto cover local AI tooling and MCP config files, and added Cursor IDE hooks for formatting, shell safety, and the RTK rewrite, plus coding-rule documents. (#173) - Bumped dev dependencies (commitlint, Playwright, vitest, eslint, biome, vite, typescript, and others) and refreshed the
package.jsonkeyword list toward palette-first and WebGPU themes. (#227) - Added a
docs:linksscript that recursively validates Markdown links viamarkdown-link-check, wired into preflight, the pre-push hook, and the CI quality jobs. (#228) - Added
contributor-covenant.organdconventionalcommits.orgto the link-check ignore list; both return intermittent Status 0 on GitHub Actions runners and caused flaky failures. (#234)
Overlay HUD
The release theme. The software ticker was replaced by an engine-managed overlay, which then grew a palette grid, a timing chart, interaction, and its own subsystem.
- Added the engine-managed overlay HUD showing demo title, active backend, resolution, and FPS, replacing the old
SoftwareTickerbanner. Toggle-able at runtime and available on both backends; opt out withconfigure({ overlayEnabled: false }). (#174) - Added overlay compositing layers on WebGPU so HUD content batches after the sprite and primitive passes and reliably renders on top; the software backend preserves the same layering through call order. (#175)
- Expanded the overlay to a three-bar layout with per-frame timing metrics (frame,
update(),render()times, update steps, draw calls) and renamed the FPS readout to "Present FPS" to match the measuredrequestAnimationFramecadence. (#182) - Split the monolithic overlay into a focused module tree (orchestrator, bars, toggle, FPS/timing samplers, dynamic layout planner) behind a barrel export, with no change to the public surface. (#183)
- Added a live palette swatch grid to the overlay bottom band, driven by a per-frame palette-usage mask that records which indices each demo draw, clear, and sprite call references. Opt in via
overlayPaletteView. (#185) - Gated palette-usage tracking behind overlay visibility and the palette grid being enabled, reused scratch buffers in
markPaletteIndicesInRect, and cached the hint-exclusion rect to cut per-draw allocations. The CI benchmark regression threshold was raised from 10% to 25%. (#186) - Added an opt-in scrolling timing-chart band that records per-frame update/render timings into a ring buffer and draws one-pixel dots with sub-millisecond minimum visibility. Configure via
overlayTimingChart,overlayTimingChartHeight, andoverlayTimingChartStyle. (#187) - The overlay body now starts hidden behind a toggle hint by default. Added
overlayVisibleAtStart,overlayToggleHintVisible, andoverlayToggleEnabled, and split the footer into a palette band and a hint bar. (#190) - Added palette swatch hover tooltips and click-to-copy: clicking a swatch writes its index to the clipboard, with a transient copy-status tooltip. Introduced a foreground overlay tier so popovers render above other overlay content. (#224)
- Removed the overlay-specific
OnTop/Foregroundmethods fromIRendererand routed the HUD through an internalOverlayDrawTargetport (drawBarFill/drawLabel) with fill-then-label ordering, dropping the extra WebGPU foreground pipelines. (#225) - Fixed palette tooltip z-ordering so tooltips composite above custom row labels, via new
drawBarFillOnTop()/drawLabelOnTop()overlay draw methods and a late WebGPU batch. (#229) - Added
gapPaletteIndextoOverlayStylefor independent coloring of the 1px row gaps and cluster separators between overlay bands; it falls back tobarPaletteIndexwhen omitted. (#230) - Added a scrollable, row-capped viewport for the palette grid with a macOS-style scrollbar. Cap visible rows with
overlayPaletteRowsVisible; scroll by wheel inside the palette band or by dragging the thumb. (#231) - Replaced the text
[~]toggle hint with an inline 11x7 bitmap icon, moving the hint and its hit region to the bottom-left corner. (#232) - Added severity tinting to the timing chart: update/render dots switch to warning or error palette indices when frames run over budget or drop, with error taking precedence.
OverlayTimingSnapshotnow carries adroppedFramescount. (#233) - Added horizontal grid reference lines to the timing chart at 5 ms, 10 ms, and 33.33 ms, plus a dynamic line at the
targetFPSframe budget, with dots anchored to the baseline. Style via the newgridPaletteIndex. (#235) - Added
BT.assignTag(label?)to drop labeled milestone markers on the scrolling timing chart at the current tick; tags scroll with samples, prune off-screen, and a "Start" tag is injected on reset. Breaking: theeventPaletteIndexstyle field is renamedtagPaletteIndex. (#236) - Extended the overlay timing snapshot with WebGPU renderer pipeline diagnostics (primitive/sprite overflow counts and submitted vertex counts), surfaced via
overlayTimingChartDiagnostics('minimal' | 'rich' | false) and an optionaloverlayRendererDiagnosticsBar. (#237)
Documentation
Documentation tracking the API renames, plus terminology cleanup and a pre-release scrub.
- Clarified the distinction between the fixed
update()simulation rate (targetFPS,ticks,Timer) and the measured render rate, and renamed the overlay label from "FPS" to "Render FPS". (#177) - Unified palette addressing terminology across the API docs:
slot(prose),paletteIndex(absolute draw index), andpaletteOffset(per-draw shift on stored texels), withBT.paletteSetvsBT.paletteguidance. (#179) - Added a resolution-model glossary to
api-core.mddisambiguating logical size, drawing buffer, CSS cap, effect tier, andBT.outputSize. (#181) - Reclassified
BT.outputSizeas a derived getter (drawingBufferSize ?? displaySize) with no matchingHardwareSettingsfield, across the getter rules and DX guide. (#188) - Stripped all Linear
VV-*ticket IDs andlinear.applinks from comments, JSDoc, docs, and templates ahead of the public release, and synced the architecture map and README overlay section to the hidden-by-default body and new toggle flags. (#238)
Full Changelog: 1.0.5...1.1.0
Release 1.0.5
Most of this release is about trust - the kind you either build incrementally or don't have when it matters. The MCP preflight infrastructure lands with documented fallbacks for when tooling goes offline; the CI now runs a dependency audit on every PR so CVEs don't quietly accumulate between releases. The btfont loader grew real validation this cycle: embedded textures must be data:image/png;base64 with a 512 KiB cap, and glyph metrics are checked before any atlas decode begins. The configure() method now accepts a partial HardwareSettings object, so demos can override just targetFPS without spelling out every field.
Asset System
The btfont pipeline got proper input validation.
- Embedded font textures in
.btfontfiles must now usedata:image/png;base64with a 512 KiB payload cap; other URI schemes and oversized payloads fail fast with a clear error. Glyph metrics are validated before atlas decode, and atlas bounds are checked after. A newAssetLimitsmodule centralises these constants. (#168)
Security
Three PRs tighten dependency and tooling security posture.
- MCP preflight script (
scripts/security/mcp-preflight.mjs) added with governance shadow detection; classifies servers as healthy, auth_required, errored, or absent and produces a proceed/deny decision before security scans run. A new/security-runskill and runbook fallback matrix ensure scans never silently fail when MCP tooling is degraded. (#163) - A parallel CI job now runs
pnpm security:audit(all deps) andpnpm security:audit:prod(production only) at moderate+ severity on every PR and main push. A newdocs/security/audit-exceptions.mdplaybook covers the risk-acceptance workflow. (#164) brace-expansionpinned to>=5.0.6andwspinned to>=8.20.1viapnpm.overrides; both excluded fromminimum-release-ageso patches apply immediately.@typescript-eslint/*bumped to 8.59.3. (#165)
CI and Tooling
Tooling additions and a typographic cleanup land in this cycle.
- A
/releaseClaude Code skill added at.claude/skills/release/SKILL.md; automates gathering PRs since the last tag, grouping by semantic section, and writingRELEASE.md. Does not modify source files or create commits. (#170) - Em dashes, en dashes, and spaced double hyphens normalised to ASCII hyphens across 49 files: docs, JSDoc, inline comments, error messages, and region markers. No functional changes. (#161)
- VS Code launch configuration added at
.claude/launch.jsonto start the siblingblit-tech-demosVite dev server on port 5173 from within this repo. (#160)
Tests
Test coverage expanded for the render dimension validation layer.
- Unit tests added for
RenderLimits,WebGPUContext, andBTAPIcovering invalidmaxCanvasDisplaySize(NaN),canvasDisplaySizeexceeding adaptermaxTextureDimension2D, and GPU limit failures without software fallback. TherenderDimension*error message helpers are now covered by Vitest assertions. (#169)
Documentation
Security documentation filled in for the hardening program.
- Maintainers section added to
docs/security/security-runbook.mdwith a named primary owner, explicit no-backup stance, and step-by-step triage; closes the VV-522 ownership fallback requirement. (#166) - GitHub Actions pinning guidance added to
docs/security/dependency-policy.md: SHA format requirement, per-job minimum permissions, Renovate vs manual bump procedures, and a note on why npm provenance is not currently enabled for the local release flow. (#167)
Examples
The configure() API now accepts a partial settings object.
IBlitTechDemo.configure()return type changed toPartial<HardwareSettings>; the engine merges the result with defaults via the new exportedmergeHardwareSettings()function. Demos can now return only the fields they need to override (e.g.{ targetFPS: 30 }) instead of a completeHardwareSettingsobject. (#162)
Full Changelog: 1.0.4...1.0.5
Release 1.0.4
One breaking change: BT namespace accessors are proper ES getters now, so BT.displaySize() becomes BT.displaySize, BT.ticks() becomes BT.ticks. Drop the parentheses and you're done. The rest is security work – RenderLimits and AssetLimits cap dimensions before anything gets allocated, pnpm 10 supply-chain hardening is on, and CI action tags are pinned to commit SHAs instead of version tags that can move.
API Changes
The BT namespace read-only accessors changed shape.
- Breaking:
BT.displaySize(),BT.ticks(),BT.deltaSeconds(),BT.timeSeconds(),BT.pointerScrollDelta(),BT.gamepadCount(), andBT.inputString()are now ES getters – remove the call parentheses. Four accessors were also renamed:fps()toBT.targetFPS,getActiveBackend()toBT.activeBackend,paletteGet()toBT.palette,cameraGet()toBT.camera. Two new getters added:BT.canvasDisplaySizeandBT.outputSize(canvasDisplaySize ?? displaySize).Vector2igetters return a fresh clone per read. (#153)
Core and Utils
Two new utility modules centralise dimension handling and canvas CSS sizing.
CanvasLayoutStylesextracts canvas CSS sizing logic from renderer initialization.HardwareSettingsgains an optionalmaxCanvasDisplaySizefield (default 960x720). CSS custom properties--canvas-display-w,--canvas-display-h,--canvas-max-w, and--canvas-max-hare injected onto the layout container;max-width/max-heightare enforced via inline styles. (#152)RenderLimitsvalidatesdisplaySize,canvasDisplaySize, andmaxCanvasDisplaySizebefore any canvas, renderer, or GPU resource is allocated. Checks cover positive finite integers, a per-axis cap of 8192 px, and a total-pixel cap of 16M. WebGPU configurations additionally validate against adapter and devicemaxTextureDimension2Dlimits. Oversized or invalid configs fail withRenderDimensionLimitErrorbefore any allocation occurs. (#155)
Asset System
Asset loading now enforces hard dimension and payload caps before any GPU or buffer work.
AssetLimitsenforces max 8192 px per side and 16M total pixels for images and indexed buffers;.btfontfiles are capped at 512 KiB JSON and 65535 glyphs.AssetLoaderrejects oversized images before caching;SpriteSheetvalidates inputs before GPU allocation;BitmapFontvalidates JSON byte size, glyph count, glyph metrics, atlas bounds, and the font texture before any decode. Invalid assets surfaceAssetLimitErrorwith clear messages. (#156)- Software renderer clips sprite source rectangles via
clipSpriteSourceRect; partially out-of-bounds blits are clipped while preserving destination alignment..btfontmetadata fieldssize,lineHeight, andbaselineare now coerced to positive finite numbers instead of trusted as-is. (#156)
Security
pnpm supply-chain posture tightened.
- pnpm 10 hardening applied:
minimum-release-age=10080(7 days) blocks packages published less than a week ago;block-exotic-subdeps=trueandstrict-dep-builds=trueare enabled. esbuild's post-install script is whitelisted viapnpm.allowBuildsinstead of the deprecatedignoredBuiltDependencieskey. (#154)
CI and Tooling
Third-party action tags replaced with pinned commit SHAs.
- All GitHub Actions in
ci.ymlare now pinned to 40-character commit SHAs:actions/checkout,pnpm/action-setup,actions/setup-node,actions/upload-artifact,actions/github-script, andcodecov/codecov-action. Jobs that lacked explicit permissions now declarepermissions: contents: read. (#157)
Documentation
Architecture map updated to reflect the new utility modules.
CLAUDE.md"Where to Find Information" table andsrc/utils/architecture index updated to includeCanvasLayoutStyles.ts,RenderLimits.ts, andAssetLimits.tswith links to dimension constraint documentation. (#158)
Full Changelog: 1.0.3...1.0.4
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 inst...