Rendering PLATEAU 3D city models: MapLibre, Cesium, TerriaJS, Navara and ortho-earth compared

2026-10-20 · Kenji Yoshida

Introduction

I drew the LOD2 buildings of PLATEAU, Japan's open 3D city model, for Chiyoda ward in central Tokyo with five web 3D map engines under the same conditions, and measured them on three machines of mine: a MacBook Pro, an M1 Mac mini and an iPhone 16 Pro. I learned three things.

  1. For the first display, the leaders tie on every machine, and on the Macs even the slowest engine is only about 0.5 s behind. Almost all of the time is spent downloading tiles. On the iPhone, the engines that deliberately hold back fall behind.
  2. While the camera moves, every engine sticks to 60 fps, except MapLibre + deck.gl, which does not reach 60 fps on the M1 or the iPhone and needs 1 to 3 s to finish drawing after the camera stops.
  3. The same 60 fps hides different work. Cesium does not load while moving, Navara coarsens the picture on phones, and ortho-earth and MapLibre draw while loading. How each engine cuts corners reveals its design.

I should say up front that I am the developer of ortho-earth. To avoid favouring my own engine, I also include reference runs that align each engine's default behaviour, and I describe the strengths and weaknesses of the other engines exactly as the numbers came out. A developer's point of view may still have crept in. The code and raw data are public, so please check them yourself and judge fairly.

The five engines

All five are open-source map engines that run in the browser, but they come from different places and are good at different things.

MapLibre GL JS: one of the most widely used libraries for 2D web maps. When Mapbox GL JS stopped being open source in 2020, volunteers forked the last open version and carried it on. Its job is drawing vector tiles with WebGL, and it cannot read 3D Tiles, so here the buildings are drawn by the Tile3DLayer of the visualisation library deck.gl (born at Uber, now part of the OpenJS Foundation), overlaid on the map. Licence: BSD-3-Clause (deck.gl is MIT).

CesiumJS: the standard 3D globe engine. The 3D Tiles format itself came from Cesium and became an OGC standard. It has the longest history of reading 3D Tiles, so it serves as the yardstick here. Licence: Apache-2.0.

TerriaJS: built by Australia's national science agency CSIRO (Data61) for the national map site NationalMap. It uses CesiumJS for 3D drawing and adds the parts of a map application on top: a data catalogue, legends and sharing. I loaded PLATEAU into its sample application, TerriaMap. The first version of PLATEAU VIEW was also built with TerriaJS. Licence: Apache-2.0.

Navara: a new 3D map engine published under the MapLibre organisation on GitHub (version 0.2.1 here). Decoding and geometry building happen in a Rust (WebAssembly) layer, and drawing is left to Three.js. It offers a graded choice, from the ease of writing only a configuration down to the depth of writing shaders. Licence: MIT or Apache-2.0.

ortho-earth: the engine I build. It uses ordinary map tiles but draws the Earth on screen as a sphere. It draws with WebGPU and switches to WebGL2 where WebGPU is not available. Drawing happens inside a worker, which leaves the main thread free. Its API follows the shape of MapLibre GL JS. Licence: GPL-3.0-or-later.

How I compared them

Every engine loaded the same tileset.json from the same origin and drew only the buildings on a plain background.

EngineVersionHow it reads 3D Tiles
MapLibre GL JS6.15.0 + deck.gl 9.4.0deck.gl Tile3DLayer overlaid (MapLibre alone cannot read 3D Tiles)
CesiumJS1.146.0Cesium3DTileset
TerriaJSTerriaMap, terriajs 8.13.0 (about Cesium 1.142)a 3d-tiles item in the init file
Navara@navaramap/three 0.2.13D Tiles source
ortho-earth@ortho-earth/globe 2.0.2map.add3DTiles
MachineSpecBrowser, display, network
MacBook ProM5 Pro, 64 GBChrome, external 4K 60 Hz, Wi-Fi
Mac miniM1, 16 GBChrome, 60 Hz display, Wi-Fi
iPhone 16 ProiOS 18.7Safari 26.6, Wi-Fi (the Mac mini served the pages and relayed the tiles)

To keep differences in features out of the comparison, I aligned the following:

In this comparison every engine reads PLATEAU's 3D Tiles as they are, and ortho-earth was measured through this general path too. ortho-earth's map of Japan has a dedicated path that converts PLATEAU buildings in advance to a lighter format, serves them, and keeps them on the device for reuse, which displays faster than the numbers here (that path is not part of this measurement).

Result 1: first display

First display took 4.7 to 8.2 s. On the Macs, the spread within one machine was about 0.5 s. On the iPhone, Navara and ortho-earth fall behind.

Time to first display in seconds, median of 5 runs, vsync on, every engine at its defaults

MacBook Pro (M5 Pro)
ortho-earth5.90 s
Cesium5.91 s
Navara6.23 s
MapLibre + deck.gl6.31 s
TerriaJS6.38 s
Mac mini (M1)
ortho-earth4.66 s
Cesium4.72 s
Navara5.10 s
MapLibre + deck.gl4.76 s
TerriaJS5.19 s
iPhone 16 Pro
ortho-earth6.99 s
Cesium6.32 s
Navara8.16 s
MapLibre + deck.gl5.86 s
TerriaJS5.85 s

(My measurements, 19–20 October 2026. MacBook from the re-run on 20 October, Mac mini at 60 Hz, iPhone excluding runs disturbed by sleep. The 5 runs of one engine spread by ±0.3 to 0.5 s.)

Most of the first display is the time to download the roughly 70 to 75 MB (about 110 to 120 tiles) needed for the initial view. The time to fetch a single tile hardly differs between engines; the differences come from the order of loading and how many requests are in flight at once. The top three are within about 0.1 s, less than the spread of five runs of one engine, so read them as a tie, not a ranking.

On the iPhone, two engines deliberately hold back. ortho-earth treats every touch device as low-memory and cuts concurrent tile requests from 6 to 3. Navara makes its level of detail 8 times coarser up close, but that does not affect this initial view (more in "The same 60 fps, different work").

Result 2: while moving

Apart from MapLibre + deck.gl, the four engines held 60 fps in orbit and zoom on all three machines, with a 95th-percentile frame time of 18 to 21 ms. Finishing the drawing after the camera stopped took under 15 ms.

Orbit (500 m altitude, 1 km radius): fps / p95 / time to finish after stopping

EngineMacBook ProMac miniiPhone 16 Pro
ortho-earth60 / 18.7 ms / 4 ms60 / 19.0 ms / 4 ms60 / 20 ms / 6 ms
Cesium60 / 18.5 ms / 14 ms60 / 18.4 ms / 8 ms60 / 19 ms / 2 ms
Navara60 / 18.3 ms / 14 ms60 / 18.6 ms / 6 ms59 / 19 ms / 10 ms
TerriaJS60 / 18.3 ms / 12 ms59 / 18.4 ms / 14 ms60 / 19 ms / 7 ms
MapLibre + deck.gl50 / 15.6 ms / 2.9 s40 / 26.8 ms / 0.8 s38 / 28 ms / 3.3 s

MapLibre + deck.gl does not reach 60 fps in orbit even on the fastest MacBook, and keeps adding buildings for 1 to 3 s after the camera stops. Because deck.gl draws on a separate canvas over MapLibre, its displayed fps comes out at about twice the real rate; the table shows how often the screen actually updated (main-thread fps).

Zoom (10 km down to 300 m) has the same shape: 60 fps except MapLibre at 47 to 56 fps. But what "holding 60 fps" means differs between engines, which a later section covers.

Result 3: memory

ortho-earth's JS heap is an order of magnitude smaller at 15 MB; MapLibre + deck.gl is the largest at about 0.9 GB. Even on the 16 GB Mac mini, swap did not grow for any engine.

EngineJS heapRenderer processGPU process
ortho-earth15 MB1.4 GB0.20 GB
Cesium0.18–0.19 GB1.4 GB0.37–0.38 GB
Navara0.15–0.16 GB1.8–1.9 GB0.40–0.42 GB
TerriaJS0.24–0.32 GB1.5–1.6 GB0.40 GB
MapLibre + deck.gl0.91–0.94 GB2.2–2.3 GB0.37–0.40 GB

These are the maxima over the three scenarios in Chrome; ranges reflect the MacBook and the Mac mini. ortho-earth decodes and draws tiles in workers, so its JS heap (the main thread) comes out small. That memory is counted in the renderer process instead, where it is level with Cesium. Safari on the iPhone does not let me measure memory.

The same 60 fps, different work

Every engine delivers 60 fps, but they cut corners in different places. Cesium and TerriaJS hold back loading while moving, Navara coarsens the picture up close on phones, and ortho-earth and MapLibre draw while loading.

Tiles fetched during zoom (M1 Mac mini, vsync on, 10 km → 300 m, median of 5 runs)

MapLibre + deck.gl463 · 171 MB
Navara382 · 137 MB
Cesium (no culling, reference)350 · 129 MB
ortho-earth342 · 127 MB
Cesium37 · 5 MB
TerriaJS34 · 5 MB

All of these are each engine's default behaviour and work as designed. So, apart from the main tables at the defaults, I ran four reference measurements with that behaviour switched off.

ReferenceDefaultAligned conditionWhat it shows
Cesium's culling while moving (cullRequestsWhileMoving and two others), Mac mini37 tiles, 5 MB during zoom, 60 fpsNo culling: 350 tiles, 129 MB, 60 fps, p95 18.2 msSmooth on the M1 even without culling. First display 4.72 → 4.99 s
Navara's LOD multiplier on iPhone (8 on mobile)81 tiles, 31 MB in orbit, finishes in 10 msMultiplier 1: 299 tiles, 112 MB, finishes in 2.08 sFirst display (8.16 → 8.46 s) and zoom unchanged; only close views are coarsened
ortho-earth's concurrent requests on iPhone (3 on touch devices)First display 6.99 s (range 6.85–8.72)6 requests: 6.32 s (range 6.22–6.70)The limit of 3 is the bottleneck. Smoothness unchanged, ties with Cesium
Cesium without vsync (no frame cap)First display MacBook 7.31 s, Mac mini 12.2 svsync on: 5.91 s, 4.72 sThe uncapped render loop occupies the main thread and tile requests wait

Cesium's culling is insurance for keeping things smooth on weaker devices; on these three machines, 60 fps held even without it. Navara and ortho-earth are built to hold back on mobile: Navara gives up detail, ortho-earth gives up loading speed. Following this measurement, ortho-earth now lets you set the number of concurrent requests with the maxInflight option of add3DTiles.

Strengths and weaknesses of each engine

Once the numbers were laid out, my impression of each engine became clear. I start with the engines that are not mine.

Cesium: the engine that impressed me most. Even on the M1 Mac mini its 95th-percentile frame time stayed at 18.1 to 18.4 ms, the most even of the five, and it finishes drawing 2 to 14 ms after the camera stops. Its renderer process memory, 1.4 GB, is on the smallest side with ortho-earth. Its culling while moving turned out to be a clever default rather than a shortcut: during zoom it reads only 37 tiles, about a tenth of the others, but the moment it stops it reads what it needs and finishes at once. With culling off, reading the same 350 tiles as the others, it still held 60 fps on the M1. It is smooth without culling; the culling is headroom for the network and the battery. I felt the depth built up over many years by the company that created 3D Tiles showing directly in the numbers. Its weakness appears when the frame cap is removed: the render loop takes over the main thread, tile requests wait, and first display stretches to 12.2 s on the M1. Ordinary browsers have vsync, so the real harm is small, but it was where the limit of drawing on the main thread showed most clearly.

TerriaJS: underneath it is about Cesium 1.142, only four versions from Cesium's own 1.146. So on the Macs, I think its gap from Cesium is better read as the overhead of the framework, the catalogue and the UI, than as Cesium's own progress. Indeed, on the Macs its first tile arrives late, at 1.0 to 1.1 s (Cesium about 0.5 s), and its JS heap is 30 to 70% larger than Cesium's. On the iPhone, on the other hand, it tied for first with 5.85 s, faster than Cesium itself (6.32 s). Its default 30 fps cap is also the right, battery-friendly choice for a finished product that shows data.

MapLibre + deck.gl: its first tile arrives earliest (0.3 to 0.4 s), and on the iPhone it ties for first display. Its loading itself is fast. But MapLibre does not draw the 3D itself; deck.gl draws it on top. So on the M1 and the iPhone it manages only about 40 fps in orbit, takes up to 3.3 s to finish after stopping, and uses the most memory of the five. This is less a weakness of MapLibre than the weight of combining a 2D map with a second library for 3D. For richness of 2D cartography, I think MapLibre is the best of the five.

Navara: still young at 0.2.1, yet it held 60 fps on all three machines, and with the cap removed on the M1 it reached 153 fps in orbit, the most headroom of any. Drawing handed to Three.js is light. Its weakness is per-tile processing, a median of 665 to 911 ms, two to four times the others, which puts it a step behind on first display. Coarsening the picture up close on the iPhone is a deliberate trade to protect smoothness. It is an engine whose growth I look forward to.

Where ortho-earth stands, and what remains

My own assessment is that ortho-earth sits in the same speed band as the mature Cesium while keeping the main thread free and memory small. On both Macs it tied for first display (5.90 s and 4.66 s), held 60 fps while moving, and finished drawing 3 to 10 ms after stopping. Its JS heap of 15 MB and GPU process of about 0.2 GB are both the smallest, and its per-tile processing, 149 ms on the M1, is the fastest.

Its defining feature is that the whole of drawing runs in a worker; of these five, ortho-earth is the only one that does this by default. With the main thread almost idle, it is built so that heavy UI on top of the map does not drag the drawing down. That said, its renderer process memory as a whole is 1.4 GB, about the same as Cesium, so it is not overwhelmingly lighter in total. And since these test pages do nothing but draw buildings, the benefit of a free main thread is not yet shown in numbers.

To be honest, four problems remained at the time of measurement. First, a brief white flash when the camera stops: it cleared the area of tiles not yet arrived before redrawing, and that is now fixed. Second, the 6.99 s first display on iPhone, because every touch device is treated as low-memory and limited to 3 concurrent requests. With 6 it ties Cesium at 6.32 s, and the number can now be set. These two ship in the next release. Third, extra redraws while the fog catches up after the camera jumps (60 to 80 right after start-up). Fourth, a WebGL error on the page that draws clouds. Of the bugs this measurement found, the inability to draw b3dm and the excess redraws at start-up were already fixed in 2.0.2.

Pitfalls in measuring

Along the way I fell into several pitfalls that badly distorted the numbers. I note them for anyone making a similar comparison.

Summary

If all you need is to show PLATEAU buildings, the choice of engine makes no big difference to first display or smoothness. What to look at is less the speed numbers than where each engine cuts corners, and what you want to use it for.

If you want smoothness even on weak devices, Cesium, which culls loading while moving, is the surest choice. If you need a map application with a data catalogue and sharing, TerriaJS sits on top of it. If you already have a 2D MapLibre map, overlaying deck.gl is easy, but on an M1 or an iPhone it cannot hold 60 fps in orbit. If you want to keep loading detail while moving and keep the main thread and memory free, ortho-earth; for drawing headroom and future growth, Navara. That is my reading.

Looking back, this test was a little too light. With only untextured buildings of Chiyoda ward, four engines hit the 60 fps ceiling and their underlying strength was hard to see. Nor did I measure what each engine is truly good at: terrain and base maps, Cesium's time-dynamic data, MapLibre's 2D cartography. Next I plan to widen the area to several central wards, use textured buildings, and give the main thread work of its own.

Once more, at the end: I am the developer of ortho-earth, so I may well be too kind to my own engine in places. Please do not take my assessment on trust; check it against the published pages and data, and judge fairly.

The pages and scripts used for the measurement, and the raw data and screens of every run, are published as plateau-bench (MIT). Follow its instructions and you can measure your own machines the same way. If you notice bias or errors, please tell me in an Issue.