engine v5.0.3
Engine v5.0.3
June 6, 2026
A patch in the For Real line.
The launch-week patch train: solo worlds keep every area, portals stop flinging you home, joins always retry, first effects play clean, and the engine heals broken ground and graphics stalls on its own.
what's new
A maintenance release shipped on June 6, 2026.
›technical notes
- api.preloadAsset(ref): creator vocabulary for asset warming — one method for every ref creators already write (sound paths, sprite/effect textures, model urls; kind inferred by extension, manifest fallback). Deterministic by construction: pure fire-and-forget hint with zero sim-observable state (module-level hint queue outside every ECS world; server/non-client sides no-op; resim replays don't re-hint; bad refs warn once via the mutation-warn rail). Warming runs the EXISTING machinery early: audio prefetch+decode into the clip cache; texture/model warms call the asset service's normal getTexture/getModel so later scene use rides the async compile-hide rails exactly like JIT arrivals — no parallel warm path.
- Engine-automatic steady-state preload sweep: after the boot flood demonstrably drains (terrain build settled + ~3s quiet sim-side; collect backlog empty + no compile starts + no real loads renderer-side), the engine sweeps the applied spec + script literals for referenced-but-cold assets and warms them at idle priority (1/frame, 2 in flight) — backing off the instant any real load appears. Explicit creator hints outrank idle guesses; nothing outranks real loads.
- No-WebGPU browsers fail fast into the designed Browser-Update-Needed wall: the client entry checks navigator.gpu before any worker spins up and broadcasts the error kiln's loading screen has always matched — the producer side of that contract had died in a renderer rework, so unsupported browsers hung on a silent spinner (a real user sat 30 minutes). The message contract is now pinned in BOTH apps' tests. The entry also fires an async requestAdapter() probe: navigator.gpu existing with a NULL adapter (blocked/ancient GPUs, some Electron hosts) was the same silent spinner one layer deeper — the probe costs nothing on the happy path and lands on the same wall.
- getSpec is never place-filtered (the place-misdiagnosis cascade driver): the client's applied spec empties every other place's objects for the render path, and in singleplayer Savi's run_script executes on the client world — so her diagnostic reads answered from the filtered view and a healthy world diagnosed as "your objects are lost". Spec-sync now records the unfiltered merged spec at the apply seam (TomeUnfilteredSpecResource) and getSpec backfills filtered places' objects on read (live objects win id collisions; the kept place is never touched — its live array is the truth, including deletions). The render path is unchanged; server worlds are the identity; client and server sides of a mode:"both" behavior now read the same whole spec.
- Singleplayer spec writes no longer erase every other place (the "hub M.I.A." driver — distinct from and one layer below the getSpec read fix): in singleplayer the client is the authority, so ObjectAPI mutations based on the place-filtered applied spec became the tracked truth — one spec write while standing anywhere (a portal createIfMissing, a dynamic-spawn promotion, an updatePlace) permanently emptied every place the player wasn't in, on every session, while the DB stayed fully intact. Now every mutation snapshot reads the whole-world view (readAuthoritativeSpec — memoized place-filter backfill), the spec-update system applies the place-scoped view but tracks the FULL spec, and the unfiltered record stays current across mutation bursts. Multiplayer clients and server worlds were never affected. Pinned against the reporting world's exact spec shapes.
- Portal/teleport arrivals no longer get yanked back to the player template's spawn position: when the template feetPosition is terrain-relative, the terrain-install re-anchor pass teleported EVERY live player in a terrain-changed place to the TEMPLATE's x/z — and place-cleanup unloading an emptied place after each visit made every portal crossing a fresh terrain install and a fresh teleport-to-spawn. The pass now does only its boot-case job: players still standing AT the template x/z re-drop at the real height; anyone who moved is never touched.
- Savi's proactive frame-loss watcher no longer false-fires during game load: the frame-budget guard now stays in grace while a real asset flood is in progress (collect backlog non-empty or JIT loads in flight; warm-hint loads excluded so idle warming can't hold grace forever). Content-heavy worlds load past the fixed startup grace, and warning Savi about "frame losses" mid-load was a false alarm she acted on. Genuine sustained jank after the load settles still trips it.
- The renderer recovers from a GPU validation-error burst instead of black-framing until reload: on some devices a resize or GPU-process recovery leaves the post-processing chain's bind-group layout disagreeing with a cached texture's sample count, and every subsequent submit fails validation. The device-error handler now detects a sustained burst (>=3 matching errors in 2s) and rebuilds the post chain into a sample-consistent state at frame top, budget-capped (<=2/5min) with one diagnostic per occurrence.
- The child-dwarfs-parent attachment volume warning is removed: parenting has nothing to do with size — a thin anchor carrying a big model child is the engine's own standard representation and the heuristic fired on it. The escaped-child position check (world coordinates passed as parent-relative offsets) stays.
- Savi prompt: full appetite-over-avoidance rework (shame framings removed, a frame for receiving user frustration, prohibitions converted to their complements, private-lexicon guard) — ships with the chat deploy.
- UI render faults loudly when an inline handler calls a custom render-defined
window.*global (e.g.onclick="window._myHelper()"): the render runs in the worker, the click fires in the main page where onlysendAction/sendAxis/dispatchUIEventare installed, so a render-defined global never crosses the thread boundary and the button silently does nothing.reportCrossRealmHandlerFault(worker-controller) scans rendered HTML each frame, dedupes by script-ref + global name, and emits aui.handlerfault naming the offending global and the fix — covers both player HUDs (ui) and creator/god-mode tabs. Creator-tabs and game-ui skill docs corrected to teach callingsendAction/sendAxis/dispatchUIEventdirectly from inline handlers with the payload inline. - God mode now enters in singleplayer games.
god-mode.togglewas a server command, but a singleplayer client IS the authority — its server mirror is never seen and its state deltas are dropped wholesale, so the command mutated a world the creator couldn't see and god mode silently never entered (it worked in multiplayer, broke the instant a game was flipped to singleplayer). The authoritative enter/exit logic is extracted into a transport-agnostictoggleGodModeForPlayer(world, playerEntityId, spawnInput, emit)core; the server command path resolves the player from the requesting client and calls it (multiplayer unchanged, byte-for-byte), and the singleplayer client now dispatches that core directly against its local world soTomeGodModelands where the client's transition reads it. - Fixed the post-reset oplog window throwing
OplogBuffer: no buffered ticksevery tick. Every reset snapshot (join / place travel / reconnect / resync) lands viaresetToStateOwned, leaving the client's server oplog base-only —baseTickset, zero buffered entries.getAuthoritativeWorldFrameguarded withisEmpty()but calledgetNewestTick(), which disagree on exactly that state, and the throw abortedlocal-transform-projectionbefore its dirty-set clear so it rethrew every tick (unboundedly in quiescent rooms — the ~10-minute error clusters). Visible artifact: parented children collapsed onto their parents until the next real delta. Now the base snapshot is consumed as the authoritative frame; children project correctly on the very tick the snapshot lands. Same-class latent bug fixed in the debug-only ghost renderer. - Frame-budget guard gains a presented-frame-interval signal — the GPU-bound sensor it was structurally missing.
cpuMs(rAF callback time) stays small when GPU backpressure parks the worker BETWEEN callbacks, andgpuMshas been 0 in prod since GPU timestamp timing went inspector-gated — so devices continuously over the 40ms fallback budget read asokforever. The mean presented-frame interval (from the existing 250ms renderer-stats sampler) now counts as a GPU-bound overrun when it exceeds the fallback budget AND 2× what measured cpu+gpu explain, escalating through the existing warn/park path. 30Hz displays, vsync waits, and healthy 60fps devices sit under the gate; load/compile graces suppress it; a resumed hidden tab drops the in-progress stats window. This is the sensor for the adaptive quality-tier governor — governor policy still awaits ruling. - Mantle ↔ ECS bridge pre-flip cleanup, bookkeeping-only (pre/post end-state bit-identical on a multi-place vehicle scenario): the per-place per-tick whole-world body re-scan is replaced by a subscriber-maintained per-place index that preserves
entitiesWithlexicographic order (body creation order — row assignment — unchanged; pure function of the same-tick world, resim-safe), and the per-tick vehicle wheel string fingerprint is replaced by wheels-array reference identity + structural compare on ref churn (value-equal churn keeps the live vehicle, real edits rebuild exactly once). - NPC systems stop billing every game every tick: the noise rail is fully off unless some behavior actually compiled an
onNoisehook (composeBehaviorsno longer fabricates truthy wrappers; every registration path keeps ananySpecHasOnNoiseflag truthful), players come from a subscription-maintained tag index; nav-grid invalidation is change-driven via component subscriptions (the per-tick fnv1a fingerprint over every primitive is gone; overlay reads no longer poison the shared cache); NPC LOS rays route through the per-place octree with an AABB-reach high-water query bound (provably a candidate superset; octree-vs-full-scan equivalence pinned). - Repeated chunk-rescue now REMEDIATES instead of containing forever: a repeat-fire means the firing side simulates against a stale/missing support-chunk collider, so
noteRescueFiredrequests that side's collider rebuild (server: artifacts cleared + dirty-marked past the inputsHash short-circuit; client: the re-request drain), debounced per chunk per 10s window, never from resim replays. Player-support chunks get a priority lane in the server rebuild queue (numericPriority 2, above the streaming-ring band) so the broken-support window shrinks even under load. - Quantized physics reads refix:
getVehicleSpeed's replicated lane (the primary path for real vehicles) now snaps to the velocity read grid like every other lane (the raw read leaked sub-epsilon predicted-writeback drift into script state);quantizePhysicsRotationcanonicalizes hemisphere before snapping (q and −q collapse to one bit-identical representative, −0 folded) — and the changeset record is corrected: velocity reads snap for ALL body types deliberately (kinematic linVel is not provably script-authored), with the sub-grid read-modify-write freeze documented (ramp from a script-state accumulator instead). - Sibling spec writers swept onto the authoritative whole-world read (the rest of the hub-loss family): the
behavior/blueprint/brushproperty setters,resetTomeWorld, and the bounds-metadata persist all still based mutations on the place-filtered resource. All now read through the sharedspec-readmodule; reset mirrors the spec-update seam (place-scoped apply, full-spec tracking);specUpdateSystemno longer trusts any requested spec as whole-world — it backfills through the unfiltered record first, so future filtered-based callers fail soft. - Players never carry terrain anchors (the #186 refix): the boot re-drop routed through a setter that PLANTED a terrain anchor on the player, so the next terrain re-apply teleported since-moved players back to the template x/z — and
enterPlace's{x,z}spawn points armed the same anchor on every portal crossing.syncTerrainAnchoris now remove-only for session-owner entities; the spec-apply anchor pass and runtime-sculpt reanchor both skip players, so anchors planted by pre-fix sessions are inert. Object anchors untouched. - Removed
terrain.rendering.textureTilingMode— a dead knob plumbed end-to-end but consumed by zero renderer code (setting it produced a rebuild to byte-identical output). Schema, types, interpreter defaults, resolved-config production, zoo authoring, and Savi's generated docs all dropped; non-strict parses strip it from existing specs. - Room heal-by-flip → heal by full re-apply: the hydration retry's ready-flip could admit players into a partially-applied room whose clients never receive the spec (the spec push was in the skipped tail). The retry now falls through to a full re-apply (the staleness guard makes a healthy room a no-op), degraded admission survives only as the last-resort catch path, and a join that TIMES OUT now closes the socket with the retryable 4430 code — previously timeouts closed nothing and stranded clients at Loading on an open socket.
- The GPU validation-burst recovery rail's own telemetry now lands: its diagnostic code was missing from the ingest allow-list (dropped at the door), recoveries report per-occurrence via a session-monotonic ordinal, and budget-exhausted bursts emit one explicit diagnostic instead of going silent.
- The no-WebGPU/null-adapter wall is sticky at the producer: one loading-state module latches the fatal verdict so a later boot-progress broadcast can't overwrite the wall (the host page's duplicate broadcaster routed through the same producer), and post-ready transient probe failures can no longer raise a false disconnect toast.
- God mode's first-entry orientation DM rides the DM-forward rail, so it reaches Savi in singleplayer too (it read a server-only notifier resource directly — silent no-op on client authorities).
- Object-preview renders are serialized behind an in-flight guard mirroring the scene-view lane (concurrent previews re-parented the shared light rig out of each other's scenes mid-compile — Savi's preview eye rendered sunless), with wedge abandonment previews never had and a throwaway-rig backstop.
- Cross-place object-id collisions resolved with deterministic place-scoped runtime identity: the spec's object namespace is per-place but runtime entity ids were global, so co-resident places fought over the same id — the second place's spawn became an update that yanked the entity to the last-iterated place, and place unload destroyed it for everyone. Runtime identity now derives from the whole-world spec as a pure function of spec CONTENT (sorted place keys — merged-spec key order differs across realms after a place delete+recreate): the first place authoring a bare id keeps it (unique ids never change), every other copy runs as
placeId:objectId, the established instance namespace the wire already speaks. Authored specs untouched; filtered-apply seams record the unfiltered spec pre-apply so both realms derive identity from the same view; an owners cache keeps the previous spec's runtime ids stable across edits. - Interaction prompts anchor at the source's world-space bounding-box top + headroom (was feet + a hardcoded height guess — mid-chest on any real humanoid); rotation, scale, real GLB bounds, and authored child assemblies all bake in via the existing world-bounds machinery; proximity still gates on distance to the object's feet; entities without renderable bounds keep the legacy heuristic until bounds arrive.
- Cross-account player-state bleed in multiplayer closed: persistence converted ANY player patchState into a patch on the single shared spec.player.state template (identity discarded, last-writer-wins) — a relog landed you in another player's character. Persistence now gates on networking mode: singleplayer keeps template persistence byte-identical (the solo client IS the template's owner), multiplayer drops live player-state from spec persistence explicitly (durable per-player state belongs on the storage:* rail; the live sim is untouched — these are persistence-only filters). Secondary: multiplayer per-tick player patchState no longer enqueues a spec save per tick (~500 saves/min in the trigger game, feeding a 947ms tick melt).
- Audio voice-pool release invariant: only the natural-end path ever freed a one-shot's voice slot — culls at the 32-voice ceiling, immediate stops, GC on place travel, same-key retriggers, stops of pending starts, and never-loading clips all leaked the slot forever, so long sessions went permanently silent. Every exit path now releases exactly once (per-voice latch, identity-checked onended, transfer-on-retrigger); terminal load failures release their slot and stop the per-frame refetch storm; completion-driven despawn is ownership-gated so finished clips on real game objects can no longer delete the object client-side.
- Singleplayer behavior-driven parent root-slide regression pin: the 5.0.0-class tear (net-ingest spec mutations composing children against uncomposed parent frames) is pinned by a full integrated test that fails on the 5.0.0 launch tree and passes since 5.0.1 — it can never silently return.
- Encoder-death rides the device-lost rail (ledger #217a): "unable to make command encoder" (GPU-process memory starvation, classically a refresh booting into the old page's undisposed device budget) now classifies onto the existing reload wall with an allowlisted diagnostic; pagehide(persisted=false) disposes the renderer worker through to the fork's device.destroy() (the old synchronous terminate discarded the queued dispose — destroy had zero reachable callers on any exit path); latchFatalRendererError makes pre-ready boot errors sticky against racing progress posts (closes #206); {kind:"ready"} moves to the first PRESENTED frame with a 10s watchdog.
- Mobile triangle budget (ledger #217b): the device-tiered geometry budget lost in the Golden Turd deslop returns as a geometry axis on the #6710 quality governor — the hysteresis machine extracted into a reusable GovernorAxis (resolution ladder is instance one, byte-identical), triangle EMA judged against per-tier envelopes (phone 1.0M / tablet 1.6M / desktop 8M diagnostic-only), aggressive G0–G3 ladder (decoration thinning → earlier LODs → tighter far-cull), cross-escalation from the resolution floor. Never shed: sim/physics, skinned visuals, the near field, authored cameras, text/UI.
- Always-on fog-equivalence cull (the #149 pattern, ε=0.5% transmittance): models and decorations past DrawFog saturation are skipped — invisible by construction; fog clears un-cull via a revision staleness key. F2 gains a Budget row; geometry rung + triangle EMA ride the perf sample.
- Game-UI style delivery no longer depends on the Referer header (ledger #222): WebKit caps Referer to origin inside cross-site iframes, so every Safari/iOS player 404'd the Tailwind runtime since the cf-edge cutover and saw raw unstyled markup. Assets resolve via document.baseURI against the hash-qualified engine root; the kernel adopts cf-edge's pre-injected runtime when its src matches, replacing stale pre-injects.
- Sprite frame/time are render-clock state (ledger #225): the mode:"client" sprite animation system advances draw/sprite frame/time every render frame while the server never animates — the detector booked that by-design divergence as drift every tick on every animated sprite. correction:{mode:"snap"} — the declaration the #6673 sweep set on mixer/text/tween but missed on sprites. Compare-side only; authored writes still replicate; the sprite clock stays rollback-immune (pinned).
- Phone tier floors (ledger #228a): shadows off and pbr terrain off on the phone class as DEVICE_RENDER_TIERS rows — shadow passes never construct (CSM rig skipped, atlas never allocates), the terrain pool compiles the existing no-PBR variant (WGSL-pinned: zero PBR rows/NRO chains/parallax in the phone fragment). Desktop byte-identical by pin; tablets keep single-sun + pbr.
- God-mode flight parity (ledger #223): bone-attached subtrees leave the prediction-compare envelope (the renderer owns their drawn pose; the server never composes it — every delivery tick booked a srv=undef push refusal and a full rollback+resim, the storm pump); replicated state bags are wire-safe at the schema level (non-finite numbers store as the JSON null both realms decode; the god behavior's -Infinity tap sentinel became an explicit null, killing the quantized tick-zero double-tap collision).
- Render-channel string table (ledger #227): the u16 intern ceiling is soft — string definitions ride the frame stream itself (STRDEF/STRRESET) with generational eviction at any frame boundary (the old reset required a fully-drained queue: append-only under backpressure), and juice-client's fifteen counter-suffixed id mint sites (oneshot_sound_N every footstep) now recycle per-prefix id pools bounded by peak concurrency. The overflow-replay machinery is deleted.
- Six press/platforms tools collapse into publish() (ledger #230): one action-discriminated tool on the mod() pattern, per-action schema in the loadable publish skill — ~3,950 chars (~1k tokens) off Savi's always-on floor every conversation. Old tool names survive only as action literals; replayed conversations with dead tool calls pass through proven-by-test.
- God-mode duplicate surface (ledger #232): the draw/placement/attach-pick takeovers now drop the published action panel the way brush always did — duplicate shows exactly one control surface (the ✓ PLACE/✕ chips) while the copy is in hand; the copy lands selected with its own chips. Fixes desktop's diamond cluster through the same resource.
- Empty-frame floor, wave 0 (ledger #228): the frame-top clear double-present is dead (a hidden second 4xMSAA RGBA16F target cleared then painted over the canvas, ~25MB/frame of nothing); resolved-never-sampled MSAA color stores discard on TBDR (opt-in fork flags, proof-commented per attachment); the output topology defaults no-outline with lazy slot RTs (mobile slots 4→2); the local-shadow atlas allocates on first real caster; GPU timestamp measurement only runs with the F2 performance tab open.
- Client-writer guard (ledger #226): a mode:"client" system declaring writes to a replicated compared component faults at registration; a dev/test write-site guard attributes every actual write through the execution-context stack (interpolation/renderPrep compared writes are structurally unmirrorable — the theorem all three storms shared); sustained push-delivery refusals emit one allowlisted prediction-push-refusal-storm diagnostic; the compare envelope is extracted to one module the mismatch detector itself consumes.
- Datadog client telemetry restored (ledger #231): the networking deslop deleted the websocket/network rollup chain — 24 websocket.* + 6 network.* series + 5 startup phases re-homed on ClientRoomRuntime/runtime-worker seams with the same names (dashboards intact), the kiln ingest route restored verbatim. Prod only looked alive because pinned ≤4.x engines emit through a pre-deslop kiln deploy; the next deploy would have flatlined ~45 series silently.
- Input-mode self-heal (ledger #233): a lost Tab keyup (e.g. Option pressed mid-hold — handleKeyUp skipped alt-flagged Tab keyups) left the engine in tab-held forever, eating digits/Space as parent shortcuts while WASD worked; kiln only re-sent overlay state on change, so nothing healed it. Now: stale non-game mode + eaten gameplay keys >4s → state-refresh request; kiln answers with current truth and re-broadcasts on focus/visibility; no answer in 1.5s → honest fallback to game mode. Active overlay use never flickers.
- Engine diagnostics off the god-mode costume (ledger #235): the single DM rail stamped every notify_dm into the god-mode-activity wrapper — fx-cap and frame-budget warnings arrived in Savi's context costumed as creator edits, unlatched. Producers now tag source: god-mode (edit narration only) vs engine (new engine-report batch, never wrapped); fx-cap latches with an accumulating 5-min cooldown; frame-budget fires once per episode with a 10s recovery sustain; chat adds a fingerprint backstop for consecutive dupes. Chronic-only/1-per-hour-per-category tightening per Jacob's ruling follows.
- Clarity over flair, wave 1 + the DPR lift (ledger #228): phone lighting tiers' maxPixelRatio 1→2 — a 3× phone renders 44% of native pixels instead of 11% — paid for by phone-class trims: MSAA off, the UNAUTHORED look bloom default resolves to 0 (any authored bloomStrength is honored everywhere; a seeded script default exactly equal to the platform default stays unclaimed), cloud octaves 2+1, biplanar slope projection compiled out with outward-skirt FrontSide pools (2-candidate blending stays; chunk geometry/colliders untouched). Desktop pinned byte-identical at every seam; F2 Budget row reports the active floors.
- New for builders: ask Savi to preload a sound or effect before its big moment — until now everything loaded on first use, so the first gunshot played late and the first explosion rendered textureless. Now the first one comes out clean.
- Worlds also warm their own sounds and effect textures quietly right after loading — first-time effects just play clean, no script changes needed.
- If your browser can't run Spawn's graphics, you now see a clear "browser update needed" card in about a second — instead of a loading screen that never ends.
- Savi can now always see your whole world when diagnosing, so she'll never again mistake a far-away area for missing.
- Fixed a serious bug in solo worlds with multiple areas: making any change while standing in one area could make every other area look permanently empty (your data was always safe — the world just stopped loading it). Areas now stay exactly where you built them.
- Doors and teleporters now drop players exactly where you aimed them — previously, in worlds where the player spawns on terrain, every trip through a portal could fling the player back to the world spawn point instead of the door's destination.
- Savi won't flag performance problems while your game is still loading its assets anymore — she waits until things have settled, so a normal loading hitch no longer makes her think the game is slow.
- Fixed a rare crash where the screen could go black and stay black mid-session on some graphics setups (usually after resizing the window) — the engine now repairs its render pipeline on the fly and keeps going, with at most a one-frame flicker.
- Buttons in god-mode panels and custom HUDs that quietly did nothing now tell Savi exactly why — so she wires them the way that works the first time.
- God mode now works in solo worlds. Building a singleplayer game and hitting the God Mode button (Tab+G) used to do nothing — now it drops you into build mode the same way it does in multiplayer.
- Attached and parented objects no longer pile onto their parent for a moment after joining a world or traveling between areas.
- The engine now notices when a device is drowning in graphics work, not just script work — sessions that quietly ran at 10–25fps on GPU-limited hardware now get the same automatic warn-then-recover treatment heavy scripts always did.
- Worlds with NPCs spend less time thinking about nothing — guard hearing, pathfinding, and line-of-sight now only do work when something actually changed or can actually hear.
- Falling through the ground and snapping back in a loop now heals itself: the engine detects the broken patch of ground and rebuilds it instead of bouncing you forever — and ground under players jumps the rebuild queue when the server is busy.
- Editing terrain no longer teleports players back to where they spawned or last stepped through a portal — anyone who has moved stays exactly where they are.
- Driving feels steadier online: the main speed readout cars use now rounds the same way on your machine and the server, so steering and gear logic built on it stops triggering tiny corrections while you drive.
- Closed the remaining ways a solo world with multiple areas could suddenly show other areas as empty: changing an object's behavior or toolbar metadata, resetting the world, and the engine's automatic model-size bookkeeping were all still able to trigger it.
- A join that hangs no longer strands you on a silent loading screen — it retries automatically, the same way failed joins already did.
- Places no longer fight over objects that share a name. If your arena and your stages both have a "wall-north", every copy now spawns and stays in its own place — nothing silently goes missing when multiple places are loaded at once.
- Interaction prompts now float above things instead of inside them — "Press E" sits over a character's head, not in their chest.
- Joining a multiplayer world never puts you in another player's character again. One player's live state used to overwrite the shared player template — now each player gets the template you authored, and solo worlds keep persisting your own character exactly as before.
- Sound effects no longer fade away and die over a long session: every sound now frees its voice slot, every time — busy moments can't permanently silence your world, and a clip that fails to load can't jam the audio engine anymore.
- Phones that lose the GPU mid-session or after a refresh now get an honest reload wall instead of a frozen black canvas, and leaving the page returns its GPU memory immediately.
- Mobile gets its triangle budget back: phones cap at ~1M triangles with a degradation ladder — decoration thinning, earlier model LODs, tighter far-culling — driven by the same governor that already scales resolution.
- Game UI now styles correctly on iPhone and Safari — HUDs, menus, and buttons no longer collapse into unstyled text in the top-left corner.
- Worlds with animated sprites (2D characters, billboard NPCs) no longer stutter from constant prediction corrections — sprite animation is smooth even in multiplayer.
- Phones render dramatically sharper — the resolution cap doubled, so a 3× phone now draws 4× the pixels it did. It's paid for by effects a phone screen couldn't resolve anyway: MSAA, the engine's default bloom glow, two cloud-noise octaves, and steep-slope terrain projection. Anything you or Savi explicitly authored — bloom included — renders exactly as set, on every device. Desktop is unchanged.
- Phones also skip shadow rendering and heavyweight terrain material effects — the same world, several milliseconds lighter every frame — and games run smoother across the board now that the engine stopped paying for invisible work in every frame.
- God mode no longer fights you in multiplayer: flying with a blueprint in hand could rubber-band every frame. Flight state now stays identical on both sides, and attached props no longer grind the prediction system into constant corrections.
- Duplicating an object in god mode no longer leaves the old object's buttons stacked on the Place/Cancel controls — one clean placement surface while the copy is in hand.
- Fixed a wedge where a game could permanently stop seeing number keys and Space (while movement still worked) after overlay/Tab interactions — the engine now detects the stale state and recovers within seconds.
- Long sessions with lots of sounds and effects no longer risk the renderer silently dropping objects when an internal 65k budget filled up — the budget recycles itself continuously now.
- Savi no longer gets spammed with duplicate engine warnings dressed up as your god-mode edits — she hears about a problem once, with a count if it kept happening, and her picture of what you built stays clean.
- A whole class of multiplayer stutter bugs (constant invisible prediction corrections) now gets caught while engine features are being built, and worlds that hit it report a clear diagnostic instead of silently re-simulating every tick.