The player installs models for its current place and any active one-hop portal destination, plus the shared traveler rig. Hidden objects remain dependencies so showing one never reveals missing geometry. Unused catalog entries and models used only in other places do not occupy GPU resources.
A gateway prepares an independent native simulation with destination collision and a fresh presentation baseline. The destination model catalog uploads while the current place stays intact and movement waits. Once preparation succeeds, graphics and simulation publish the destination, the room receives the new place pose, and retired GPU resources are released. Preparation failure keeps the source simulation and offers a retry. An uncertain commit acknowledgement invokes recovery instead of claiming the source is definitely unchanged.
#Run and try it
From the repository on codex/engine-foundations:
npm ci
npm run dev:editorOpen the private editor URL printed by the launcher. Save a world with connected places, choose Start shared room or Apply saved revision, then Join the room. Import different models into different places using the asset controls; leave an unused model in the library if you want to check residency.
If this editor is already running on port 5181, the launcher prints its private link and exits successfully. Keep using the existing instance. To rebuild its pinned engine after code changes, stop that editor's original terminal with Ctrl+C and run the command again. A different application on that port is reported without stopping it.
- Press F2 in the player and note Resident model MiB and Resident model buffers.
- Cross a gateway. Preparing [destination]… appears while loading needs time; the traveler remains in the source place until ready. The destination appears with its authored arrival position and heading.
- Cross back and repeat. Each place should return to its previous resource count. Models still used by that place remain included even when currently hidden.
- Apply an edit while another traveler is in another place. Each gets the accepted revision with its own model catalog.
- Reload in the destination, or use Make yourself comfortable → Return to the starting point…. Reload restores the saved place’s catalog; returning prepares the entry place before releasing the welcome screen.
Automated acceptance: npm run test:browser:residency. It uses a fresh isolated production server and an imported two-place fixture. Native WebGPU fault injection rejects an allocation, holds destination preparation, destroys the device, and tracks live buffers over repeated crossings. The developer-only fault controls are supplied by the test and are absent from the app.
Use DW_TEST_MAIN_GRAPHICS=1 npm run test:browser:residency to exercise the main-thread renderer fallback. The reset check runs after the initial startup deadline has expired, rejects one allocation while retaining the destination, then retries successfully. Returning to the start no longer reuses the initial startup timer.
#Ownership and limits
PlaceTravel owns one candidate at a time. Shared revisions cancel an uncommitted crossing or wait for a committing one before preparing their own graphics. New room state is reconciled at publication. Input is released during loading; pause changes are retained, and forward movement follows the new arrival heading. Returning to the start uses the same staging path.
Each view retains its existing 32 MiB logical peak model budget, including old and candidate catalogs during a swap. A device-wide model pool additionally limits unique buffers and logical texture bytes to 64 MiB and 32,768 handles across source, portal and pending candidates. This is model memory only; render targets, lettering and the other renderer resources retain their separate limits. The counts do not measure driver overhead.
Validated vertex/index/tangent buffers, skin influence buffers and material images are shared by content identity across views and replacements. Each allocation keeps a reference until its draw owner has retired; the last reference destroys the native handle. Animation palettes and material bind groups/uniforms stay private to each view. Unresolved native validation cannot publish a shared entry. A concurrent request may allocate a separate candidate while the first is still validating, under the same aggregate bound. Failure returns every partial reservation without destroying accepted siblings. A device identity fence rejects cross-device sharing; device disposal releases the entire pool and makes late releases harmless.
F2 and diagnostic exports distinguish Resident model MiB (the current view's logical catalog) from Device model MiB (unique model allocations across views), Device model peak / limit MiB, and Shared model reuses. In the native portal fixture, two logical catalogs total 3,962,984 bytes while unique model allocations occupy 2,243,668 bytes—about 43% less. Retiring the source catalog preserves the destination picture and its last references. This is a fixture measurement, not a general memory or frame-rate promise.
This remains graphics residency within the existing embedded world format: source models, compiled collision and CPU presentation descriptions still load with the whole world. Network asset streaming, adjacent-place prefetch, larger catalog limits and cross-host travel remain open. Server-authoritative crossing and durable player-state transactions have separate scoped acceptance in persistent rooms, trusted rooms and the portal journeys.