Library / SDK
tentone/geo-three avatar
tentone/geo-three

geo-three: tile-based world maps inside a three.js scene

Tile based geographic world map visualization library for threejs

963 stars143 forksTypeScriptMIT

At a glance

What is it?
geo-three is a TypeScript library that renders geographic map data in three.js using tile-based chunks, with selectable level of detail strategies, terrain geometry from height data, and adapters for several commercial and open map providers. Its npm version sits ahead of the last tagged release, and the last push was on 2026-04-05.
Who is it for?
geo-three suits web developers who already have a three.js scene and need a globe or planar map that loads and unloads tiles as the camera moves, including simulators, flight and maritime visualisations, and games. It is a poor fit if you need a static map image or a plain slippy map, and a poor fit for a project that cannot obtain an API key, since every supported provider except the open ones requires a developer account.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Activity is slowing. The repository last received commits 7 months ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 7, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • tentone/geo-three963 ★ · +3
Star history
Repository2026-10-212026-11-07
tentone/geo-three960963

3 Stars in the last 17 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

Why a map inside a three.js scene rather than a map layer

Most web maps are slippy maps: a DOM surface of image tiles with pan and zoom. That model does not fit a three.js scene, where the camera can be inside the terrain, tilted, or flying between buildings. geo-three exists for that case. It is a library to display maps using three.js, allowing full world-scale visualization of geographic data using tile-based chunks. The intended users are developers building 3D web applications: flight simulators, situational awareness views, city scale games, and data visualisations that need the map to behave like the rest of the scene graph. The tiles become geometry inside the same scene, so they can be lit, occluded, and combined with your own objects. The library also generates 3D geometry for terrain from terrain height data, either using software-generated tiles or using GPU displacement maps.

Providers behind a single MapProvider interface

The library supports multiple map service providers, each of which has its own provider object implementing the MapProvider interface. The list in the project documentation covers BingMaps, GoogleMaps, HereMaps, MapBox, MapTiler, OpenMapTiles, and OpenStreetMaps. Each of these requires a developer account and API configuration to be used, which is the first practical obstacle for anyone evaluating the library. The interface is also the extension point. It is possible to implement new providers using the MapProvider interface, with one constraint stated plainly: providers should have a tile-based map system to be supported by the library. A vector or raster service that cannot be expressed as a tile pyramid will not fit. There is also a DebugProvider, which does not show geographic imagery but instead shows the zoom level and the coordinates on each tile, which is exactly what you want while working out why a real provider is not lining up.

Getting the library in and putting a map on screen

To add the library to your project, the documentation says to get it through NPM alongside with three.js and use it as an ES module. The package is named geo-three, the manifest marks it as type module, and three is a peer dependency with a floor of >0.120.0 rather than a bundled copy, so you control the three.js version. The published entry points are a module build, a CommonJS build, and type declarations. You can also include the library directly in your webpage, but you must ensure first that THREE is available globally. The minimal scene setup is three steps:

javascript
// Create a map tiles provider object
var provider = new OpenStreetMapsProvider();

// Create the map view and add it to your THREE scene
var map = new MapView(MapView.PLANAR, provider);
scene.add(map);

// By default, coordinates are in meters. Resize to kilometers:
map.scale.set(0.001, 0.001, 0.001);

The MapView constructor takes a mode and a provider, and MapView.PLANAR is the flat projection, with a globe transition available as a separate example page. The scale line at the end is the one people miss: the library works in metres, so a kilometre-scale scene needs the scale vector set as shown, otherwise your terrain will be a million times too large.

EPSG:900913 internally, WGS84 on the way in

Coordinates are where map code usually goes wrong. The project states that it internally uses an XY EPSG:900913 coordinate format to be compatible with the XYZ coordinates used in three.js, which is the Web Mercator projection that tile services speak. Geographic coordinates in latitude and longitude are a different thing, and the library ships a converter for that. Use the UnitsUtils class to access the unit conversion methods, and to convert a latitude, longitude WGS84 pair into XY coordinates:

javascript
var coords = Geo.UnitsUtils.datumsToSpherical(40.940119, -8.535589);
controls.target.set(coords.x, 0, -coords.y);

The sign flip on the y axis in that snippet is not a typo either. It is the conversion between a geographic north-up convention and the internal axis order, and the example applies it explicitly when moving the camera target. Getting this wrong produces a camera pointed at the wrong hemisphere, which is a confusing symptom for something that is really a coordinate convention.

How the tile tree decides what to load and what to drop

Tiles are fetched from the service API configured for the provider, and each provider requires its own configuration through its MapProvider object. Base tiles are always loaded at the beginning of the process, so the world is never empty. After that, the library works by casting rays into the tile tree. Each frame a couple of rays are cast, and the number of rays can be configured using the MapView subdivisionRays attribute. The distance from the ray to the camera decides whether a node needs to be simplified or subdivided, and those thresholds are configured with the thresholdUp and thresholdDown values. So the loading decision is driven by where the camera is looking and how far away it is, not by a fixed zoom level. The trade-off is that subdivisionRays is a quality knob with a cost: more rays find more nodes worth subdividing, but the documentation notes that raycast mode uses random sampling and takes some time to pick all nodes to subdivide.

Three level of detail strategies and a hook for your own

The library includes multiple methods for LOD control, defining how tiles are subdivided or simplified. Raycast uses ray casting to determine the distance of nodes to the camera view, considering only nodes inside the view frustum, using random sampling, and it is described as taking some time to pick all nodes to subdivide but being overall faster. Radial calculates the distance from the camera to each node, subdividing closer nodes and simplifying distant ones, considering all nodes, and it provides a more consistent result. Frustum is similar to the radial mode but only nodes inside the view frustum are considered for subdivision. If none of these fit, a custom LODControl can be implemented by extending the LODControl object and implementing the updateLOD(view, camera, renderer, scene) method. The example in the project documentation normalises the camera distance by the tile level before comparing against a threshold, which is the detail that makes a custom controller behave sensibly across zoom levels.

Building, documenting, and running the examples

The repository is a conventional TypeScript project with rollup for bundling and typedoc for the API documentation. The scripts are worth reading as a description of the workflow:

json
"start": "npm run examples && http-server examples",
"build": "rollup -c rollup.config.js",
"docs": "typedoc --out docs ./source/Main.ts",

The examples script bundles the example pages with a separate rollup config, and the start script then serves the examples directory over http-server, so you can run the live demos locally rather than only on the project page. The publish script chains build, docs, examples, and npm publish in that order. Linting is eslint over the TypeScript sources. The examples directory in the repository holds the files behind those pages, including basic, orthographic, providers, and transition examples with matching scripts, and the documentation points at a hosted sandbox page for providers and a globe transition example. Some of the live demos require API keys for the map services used, so expect to add your own keys before the provider comparison page shows anything useful.

Version drift, the licence, and what to check before committing

The maintenance picture needs a close reading. The repository is not archived, and the last push was on 2026-04-05, which is more than six months before today, so it should not be described as actively developed. The tagged releases tell their own story: 0.1.14 from 2023-07-16, 0.1.11 from 2022-12-26, and 0.1.10 from 2022-12-20. The manifest version is 0.1.15, which is ahead of the newest tag, so a published npm install and the repository state are not the same thing. Licensing is MIT, which is permissive and suits commercial use with the notice retained, and the package is marked sideEffects: false so bundlers can drop unused exports. Two things deserve attention before you build on it. The three.js peer dependency range is wide at >0.120.0 while the development dependency is pinned near 0.160.0, so a very recent three.js release is not something the project has been developed against. And every provider other than the open ones needs its own account and key, so the cost of the library in practice is the cost of the map service.

Editorial conclusion

geo-three suits web developers who already have a three.js scene and need a globe or planar map that loads and unloads tiles as the camera moves, including simulators, flight and maritime visualisations, and games. It is a poor fit if you need a static map image or a plain slippy map, and a poor fit for a project that cannot obtain an API key, since every supported provider except the open ones requires a developer account. Before adopting it, check the provider you need against the MapProvider interface and confirm it is tile based, run npm run examples to see the live sandbox and globe pages, and read the repository package.json rather than the release list, since the manifest version is ahead of the newest tag.

Frequently asked questions

How do I add a geo-three map to a three.js scene?

Create a provider object, for example new OpenStreetMapsProvider(), then create the view with new MapView(MapView.PLANAR, provider) and add it to the scene with scene.add(map). Because coordinates are in metres by default, set map.scale.set(0.001, 0.001, 0.001) to work in kilometres.

Which map providers does geo-three support?

BingMaps, GoogleMaps, HereMaps, MapBox, MapTiler, OpenMapTiles, and OpenStreetMaps, each through its own provider object implementing the MapProvider interface. Every one of them requires a developer account and API configuration to be used.

Can I add my own map provider to geo-three?

Yes, by implementing the MapProvider interface. The constraint is that a provider should have a tile-based map system to be supported by the library. There is also a DebugProvider that shows zoom level and coordinates on each tile instead of imagery.

What level of detail modes does geo-three offer?

Raycast, which uses random sampling and only considers nodes in the view frustum, Radial, which calculates the distance from the camera to every node, and Frustum, similar to radial but restricted to nodes inside the frustum. A custom mode can be added by extending LODControl and implementing updateLOD(view, camera, renderer, scene).

How do I run the geo-three examples locally?

The package scripts bundle the example pages with rollup and then serve them: npm start runs npm run examples and http-server examples. The scripts also include build for the library bundle, docs for typedoc, and lint for eslint over the TypeScript sources.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. tentone/geo-three on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/tentone-geo-three.svg)](https://hysenlabs.com/projects/tentone-geo-three)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/tentone-geo-three.svg)](https://hysenlabs.com/projects/tentone-geo-three)

Was this useful?