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