LOCI · Method of Loci

A room you look around, then recall.

Objects sit in places. You look around, then answer what was where. The 3D is the method, not decoration: the method of loci has been spatial since antiquity.

It is also a testbed. A room is one JSON file, five interchangeable runtimes draw the same scene, and every session reports frames, jank, input lag and load time from the device that ran it.

Premise

Serving 3D content on the web

The question was not whether a browser can draw a room. It can. The question is what it costs to author one, hand it to development, put it in front of a phone, and measure how that phone handled it. LOCI exists to run that loop once, end to end.

Content outside the codeIf a new room needs a developer, planning does not own the content. A room is one file, and the directory is the catalog.
The runtime is interchangeableAssets and content sit outside the renderer, so swapping engines changes one module. Five already do, Unity among them.
Measured on the deviceEvery session reports what was actually measured on it. Estimates of phone performance have been wrong twice already.
Scope

What is built

A room is a box with furniture, places on the furniture, and objects on the places. Everything about it (size, light, furniture, places, objects, learning time, price tier) lives in one JSON file that planning edits in a visual editor with the same renderer beside it.

One contract, three consumersThe validation rules live in a single file shared by the editor, the CLI, and the runtime. Two copies diverge, and then a room passes review and breaks on screen.
Rules that are conditionsPlaces must differ in height, or they stop being distinct places and nothing is retained. Two items may not share a word, or scoring has no answer. A training room may not exceed sixteen places, because past that no one is memorizing anything.
Five runtimes, one sceneRaw WebGL2, three.js, raw WebGPU, Canvas 2D and Unity WebGL draw the same room from the same bytes. Only the selected runtime's library is fetched, so the transfer comparison is not skewed.
Nothing ships without reviewPlanning uploads, validation gates it, and it waits until development approves. Uploads, rejections and approvals are appended to a ledger everyone reads.
Pipeline

Authoring to operations, once through

Four stages, each with a gate that stops errors propagating to the next.

01
Authoring

A form with the live scene next to it. Furniture comes from presets whose positions are computed against the room size, and a place snaps onto a surface rather than being typed as coordinates.

Gate: validation runs on input. On failure the upload button is disabled.
02
Handoff

Upload puts the room in review, not in front of users. Development opens it at a staged address that requires a login, and either approves or rejects with a reason planning reads.

Gate: validation runs again at approval. What passed an hour ago is not what is being published now.
03
Serving

Static files only. The user picks the room and the runtime on the first screen. Everything is precompressed with brotli and served that way.

Gate: the server serves an allow-list. Anything not named is a 404, including the directory listing.
04
Operations

Each session reports frames, worst 1%, jank, input lag, thermal drift, load breakdown and per-place results, split by runtime. Feedback is left on the section it is about, and acceptance is decided and recorded.

Gate: the ledger is append-only. There is no code path that deletes from it.
Measured

Measured on the device

One Android phone, five runtimes, seven rooms, and one room built to break them. Ninety-six sessions on the same scenario, and the numbers came back from the device.

60fpsAll five runtimes, 16.7 ms median, across seven rooms
0.8 → 36%Hitching on the Versailles room, 3.7 M triangles. This is where they split
3.6 MBUnity's additional transfer size. three.js adds 153 KB
723 msUnity's first frame on the seven standard rooms. The other four: 274 to 350 ms
RuntimeAPI Draw callsFrame Hitch First frameLibrary
three.jsWebGL225416.7 ms0.8%522 ms153 KB
Unity 6WebGL2 · wasm316.7 ms2.6%913 ms3.6 MB
Canvas 2DCanvas 2D1,14916.7 ms3.6%466 ms2.9 KB
Raw WebGL2WebGL2416.7 ms20.8%241 ms4.6 KB
Raw WebGPUWebGPU416.7 ms35.9%453 ms5.1 KB

Galaxy Note10+ 5G (SM-N976N, Android 12), the Versailles room: 60 places, 817 pieces of furniture, 3.7 million triangles, over a local link so the network stays out of it. Hitch is the share of frames over 33 ms. Library size is what crossed the wire, brotli compressed. Median of two runs each.

Below a certain load, the runtime does not matterSeven rooms, up to 40 places and 80 pieces of furniture: all five held 16.7 ms on the phone. Draw calls ranged from 3 to 1,149 and the frame did not move.
Above it, the hand-written renderers hit their limit firstOn the Versailles room raw WebGL2 and WebGPU hitched on 21% and 36% of frames with only four draw calls, because they push 1.8 million vertices with no culling. three.js splits the same scene into 254 calls and frustum-culls what is off screen: 0.8%. The 153 KB buys the culling.
Unity costs 24× the transfer of three.js and is not faster3.6 MB on the wire, 723 ms to first frame against 350 on the standard rooms, 2.6% hitching on Versailles against 0.8. Once lighting, shadows and geometry were matched it did not look different either. At this content scale an engine offers no advantage; the integration stays so it can be re-measured when the rooms get heavier.
Earlier, on an iPhone over the public addressprecompressing the mesh from 365 KB to 73 KB did not move the first frame (parsing, shader compilation and GPU upload were the cost rather than transfer), and the first run paid 22–24 ms of input lag the second did not. Both reproduced. The four-runtime tie did not hold once the room got big enough.
Limits

What this does not claim

The figures above are measurements. Below is what no measurement here supports.

No claim that it improves memoryThe method of loci is a technique, and LOCI is an environment for practicing it. Nothing here has been trialled, and no cognitive, medical or preventive benefit is asserted.
The sample is personal hardwarePhones, tablets and laptops on hand: no device matrix, no device farm. Direction, not population statistics.
Godot was measured and dropped; Unreal is not measuredGodot 4's empty web export came to 6.7 MB, twice Unity's, with no benefit. Unreal has had no official web target since 4.24. Unity's figure on the admin side is now a measurement, not an estimate.
Payment is an interface slot onlyTier and price are in the schema and the operations view shows the fields, but nothing is charged and the figures are simulated.
Assets that reach a browser cannot be protectedAnything rendered has to reach the client. What can be done is gate assets before sending, watermark them to trace a leak, and keep high-value assets off the wire.
Open a room A minute. Six places, thirty seconds, then answer what was where.