Repository navigation
v0.7.0
Ten days since v0.6.0 — 538 feat/fix/perf commits in what this snapshot ships, out of 986 touching
the published tree.
This is a durability release, not a feature release. Two classes dominate it.
The first is state outliving the thing it belonged to: a session torn down but still writing, a
scene load that lost a race and overwrote the winner anyway, a guard that survived the operation it
was guarding. The second is an authored value that reads back as set and does nothing — a field
the Inspector shows, the scene stores, and no code ever reads. Both are quiet by nature: nothing
throws, the wrong value simply persists or the right one silently fails to apply. Most were found by
sweeping for the shape rather than from a report.
Dependencies
- three.js stays at 0.184.0. The 0.185.1 bump was made during this cycle and then reverted: it
black-screens every iOS device on the first launch after a clean install, on both the WebGPU and
the WebGL2 paths. Android is unaffected. The pin is exact so it cannot drift back. - PixiJS 8.19.0 → 8.20.1
- Rapier stays pinned at 0.19.3. 0.20.0 was bumped and reverted: it panics when you remove a
collider something is standing on. Root-caused upstream as a CCD cache bug, so the pin is
deliberate and documented rather than drift.
New capabilities
- 2D masking — a
Mask2Dtrait that genuinely clips a PixiJS subtree. - Multi-touch gestures — a pan / pinch / tap layer, scoped to the game's own DOM surface.
UIElement.autoFitText— shrink-only font fitting, converged by measurement.- A global input lock, gated on the action completing rather than on a timer.
- A container can swallow a tap without having to pretend to be a button.
- Three Android tablet presets in the editor.
- PixiJS geometry bytes and geometry churn are now counted in the profiler.
Authored values that did nothing
The engine's own rule is that an unwired field is a lie with a tooltip. This release went looking for
the lies:
- Four
UIElementfields read alive and applied nothing;textAligndid nothing on the path most
text takes;zIndexwas shadowed with no validator mention. UIAnchordiscarded all four authored margins in silence.- Six of the 14 fields a pooled row pins were overwritten without a word.
- An entry prefab root's
%size was pinned aspx— the unit was read and dropped. - Spacing faked with whitespace, which the DOM collapses.
Alongside it, a long correctness pass on the layout denominator itself: percentage sizing under
uiScale, safe-area percentages against a root with no layout box, rotated scenes, and
AutoFitText's fit denominator mixing screen px with layout px (up to 67% wrong).
Lifecycle and liveness
Sessions, scenes and realms now let go of their state when they end.
- A torn-down IAP session stops listening, and can no longer write entitlements into the next
game. A parkedpurchase()is bounded, released on a webview reload, and settled through one
unpark(). - A superseded scene load no longer overwrites the winner's path, undo stack and journal. A load
racingunloadAllcannot claim a success it did not get, and a post-swap tail cannot leave
activeScenePathpointing at a torn-down scene. - Navigation records history at the world swap, not in the continuation — a failed scene swap
used to corrupt the back stack. - A realm-shutdown registry means native listeners die with the realm.
- One
withTimeoutwith the abandoned operation's disposition required, replacing five defects
that shared one shape: a timeout that abandons without cancelling. - Roughly a dozen unguarded deferrals across the runtime and editor, one of them silent data loss.
- Video once-guards no longer outlive the playback they guarded.
Resource leaks
A sustained sweep, mostly GPU-side, and several were leaks where the release path existed but
nothing reached it:
- A compiled
WebGLProgramleaked on every text rebuild. - The environment PMREM leaked a whole generator per scene swap, with no reachable
dispose. - Six further GPU/DOM release defects, plus a guard to catch the next one.
- GPU-loss detection lived in one global slot, so a second viewport disarmed the first; every
renderer construction site now wires it. - A rebuilt 2D renderer replayed the dead one's instructions.
- The engine survives a jetsammed WebKit GPU process and instruments what killed it.
Refusing rather than overwriting
Formats that could not be read used to be overwritten in place. Now they refuse:
- A too-new scene refuses, and the guard moved ahead of the mutators.
- A corrupt
.mesh.jsonno longer mints a fresh GUID over itself; particle and atlas assets refuse
instead of overwriting what they could not parse. - The verdict is split from the disposition — the previous behaviour destroyed the asset, not just
its unreadable bytes. - A v3 scene no longer skips its first three migration rungs.
Two version fields were being written and read by nothing — the asset sidecar's (written at 14
sites) and PlayerPrefs' save-format version. Both are now load-bearing or gone. Unknown keys
survive normalization one level down, and asset writes keep their trailing newline.
Failures that used to be silent
- A play request the browser refused is reported as
@video.blocked. - A rejected
PlayerPrefsDELETE is no longer invisible; a failed flush is re-queued and retried
on the pause edge. - A refused
.meta.jsonpark tells the human, not only the console. - A missing asset file stops reporting itself as corrupt JSON.
- Store failures are named rather than swallowed.
- Ten "warn-once" sets that had degraded into warn-never.
Editor
The Inspector's asset views were autosaving to disk on every keystroke; those writes are now
parked, with the compare-and-swap moved onto the parked write. The Scene inspector's baseScene
field and the atlas view had the same defect. An older editor no longer deletes a config section a
newer branch added, and a panel crash message is selectable.
iOS and safe area
safeArea became a push signal rather than a read-triggered poll — its forced layout had been
stalling the iPhone 8's touch-scroll compositor. The audio gesture-unlock no longer disarms itself,
which had silenced the game until relaunch after an interruption. Reload-on-resume after a long
background.
OTA
Anti-rollback via a monotonic release sequence. A bundle's manifest is chained into the signed
release, so a signed release commits to bundle contents rather than a version pointer. Mandatory
updates are sticky and clearing one is a deliberate act. Superseded version folders are reclaimed
without one bundle deleting another's. The publish CLI now enforces the identity guards that
previously existed only in the editor route.
Build and release
Release snapshots are cut from a dedicated release_X_Y_Z branch and the publisher refuses to
publish from anything else — the tree that ships is now the tree that was tested. One
canonicalPath/samePath replaces the hand-rolled path-identity recipes that had drifted apart.
The native build fails when node_modules holds a previous tarball's bytes.
Work under games/ is not part of the published engine and is not described here.
Full Changelog: v0.6.0...v0.7.0