Two names in this repo were quietly colliding with things that already ship. `lumbridge-simulate` is a Rust crate in the private monorepo — the Governor's memory-admission simulator, with a public product page — and in its vocabulary a "Scene" is a set of models, not a three.js scene. Naming a 3D engine the same thing would have put two different meanings on both words at once. Meanwhile world1.lumbridgecorp.com has been serving <title>Tera — Lumbridge</title> since before any of this: the world already had a name. So the map view is Tera, the interiors product is Spaces, an Office is one building's interior and a Space is a room inside it. ARCHITECTURE.md opens with that table now, because the next few thousand lines all depend on it. The Phaser 2D world comes down rather than being migrated. Its tileset is precisely what stopped it being open-sourceable, and there are no real users to strand. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Tera
The map view of Lumbridge Simulate — cities from above, in three.js. Its other half, Spaces, is the offices you walk into: one engine and one asset library, seen from outside and from inside.
Apache 2.0. Runs at tera.lumbridgecorp.com.
What it is
An engine plus data packs. The engine renders terrain, coastline, a built city on real street grids, bridges, roads, markers and air traffic. A city pack is pure data — coastlines, hills, districts, landmarks, camera chapters — so adding a city is a data contribution anyone can review, not a fork.
San Francisco ships today. Los Angeles / Orange County / Riverside is next; New York after that.
Quick start
npm install
npm run dev
Using the engine
import { createScene } from "@lumbridge/tera/engine/scene.ts";
import SAN_FRANCISCO from "@lumbridge/tera/cities/sf.ts";
const scene = createScene(canvas, {
city: SAN_FRANCISCO,
markerPalette: { hiring: 0x4ade80, closed: 0xef4444 },
});
scene.setMarkers([
{ id: "1", lat: 37.7765, lng: -122.4241, label: "Somewhere", colorKey: "hiring" },
]);
The engine renders Marker[] and looks colours up by colorKey in a palette
you supply. It does not know what your markers mean — that mapping lives in
your adapter. This is what lets one renderer serve a private map coloured by
one scheme and a public map coloured by another, without either being a fork.
Adding a city
Write src/cities/<id>.ts exporting a City. Trace the coastline and parks by
hand, place hills as radial peaks, and give each district its street bearing.
Two rules, and they are not stylistic:
- Do not import geometry from OpenStreetMap. OSM and Nominatim output is ODbL — share-alike, and incompatible with this repo's licence.
- Do not commit logos or brand assets. They are trademarks, not code.
See ARCHITECTURE.md §3 for the full reasoning, and NOTICE for the attribution and data-provenance statement.
Aircraft
The engine takes a FlightSource. Two ship here: SimulatedFlights (original,
flies real approach and departure corridors) and AdsbFlights (open community
ADS-B feeds such as adsb.lol).
FlightRadar24 is deliberately absent — their terms forbid scraping and forbid redistributing their data, so a client for it cannot live in an Apache-2.0 repository. Commercial sources belong in private deployments. The best long-term answer is an RTL-SDR receiver: first-party data with nothing to comply with.
Layout
src/engine/ renderer — terrain, blocks, structures, markers, flights, scene
src/cities/ data packs — pure geography, no code
src/adapters/ where outside data plugs in
engine never imports cities; neither imports adapters.
