Three loose ends turned into four, because one of them was not a stale comment. `cityDaylight()` closed its fog at 210/460 — numbers tuned when San Francisco's 230-unit board was the only one. Every caller who supplies an `Atmosphere` overrides them, which is why this survived: the demo does, so nothing looked wrong. A self-hoster who renders a city with no clock and no weather gets the default path, and on the Bay Area's 1003 units that fog closes well inside the city. It now scales to the board exactly as the camera limits do, with the old constants preserved as the default for a caller holding a palette but no world. The three that really were comments: socal.ts justified its `latScale` by citing the 340-unit orbit cap and the 460-unit fog as things the engine insisted on, and the engine stopped insisting when both became board-relative — 285 is now a choice the pack makes because its lot sizes and hill radii were authored against it. CONTRACT.md and ARCHITECTURE.md still measured the retention argument against a 336,864-point San Francisco that has been the whole Bay Area for some time. And server/package.json listed five routes where there are six. None of these changed behaviour except the first. All of them would have gone on quietly disagreeing with the pages that now cite them. 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.
