Simulate · Spaces

Spaces.

The inside of the world. Tera is a city from above; a Space is a room you are in. An Office is one building's interior and a Space is a room within it, and both are described by a data pack the engine renders without being told what any of it means.

An office is data

An Office is strictly JSON-serialisable — no functions, no classes, no THREE types, no Date — so a pack hand-written as a .ts module and a pack arriving as a .json body over HTTP are literally the same object. Offices are authored in metres, 1 unit = 1 m. src/offices/lumbridge-hq.ts is the compact reference pack: SF HQ is a 12 x 9 m live/work studio with four rooms, eight walls, 28 explicit props, five seats and five views. LA HQ is the larger two-level example with 16 rooms, 31 walls and exactly 24 seats around an open courtyard. Both carry CC0-1.0 in their metadata, so the plans and seat ids can be taken whole. The plan resolver drops rather than throws: a self-crossing polygon or a duplicate id is reported through plan.problems and the rest of the pack still opens.

Walls are an explicit list with 1-D openings

A room is a floor slab and implies no walls; walls are a separate explicit list, and the two are never derived from each other. Deriving walls from shared room edges needs float-equality to decide whether two rooms touch. Doors and windows are openings punched in a wall — an interval along the wall, metres from its own end and a width — never placed objects, because shipping both a door prop and a door-shaped hole leaves the collider with no gap where the door is. One pass splits every wall around its openings and emits both the solid runs the renderer draws and the collision segments a walker would use. A hole counts as a way through only if its sill is on the floor and its head clears 1.1 m, so a window at a 0.9 m sill is drawn and still blocks.

Presence binds to a seat id, never a coordinate

A seat is an address: a stable name for a spot on the floor, with a facing and a pose. It is not a chair and not a person. A Presence carries a seatId and no position, and never appears in an office pack at all. That split lets geometry be distributed while occupancy stays private — the pack knows where eng-04 is, and a private API behind authentication knows who is sitting in it. A presence whose seat is not in the plan is dropped rather than placed.

Two scenes, one renderer

The city and an office cannot share a THREE.Scene: San Francisco puts one scene unit at about 94 m with 3.6x vertical exaggeration and clips at 900, while an office is 1 unit = 1 m with a 0.05 near plane. So there are two scenes and one renderer, and stepping inside pauses the city rather than disposing it. An unsited office remains offline under its fixed interior rig; a sited pack receives the same computed sun and sky from its authored location.

Packs are served by the optional API at GET /api/v1/offices/:id, one file per office in TERA_OFFICES_DIR, with no database and no build step. A pack that does not declare its visibility is read as private, and a private office answers 404 rather than 403, identically to an office that does not exist — a 403 is an existence oracle. On the hosted service, member characters and private offices are invite-only while platform godmode remains administrative. Self-hosted Spaces use private distribution. Collision-aware dog or humanoid walking, authenticated seat presence, HTTP/SSE poses and deterministic robot jobs are built; LiveKit voice remains separate and optional.

Read about Lumbridge Simulate