Tiled Models

Patrick Morrison, updated July 18, 2026

BelowJS supports tiled models to handle massive scenes

BelowJS supports both single-file GLB models and streamed 3D Tiles datasets. Use a standard model entry (default type: 'gltf') for single-file delivery, or set type: 'tileset' when you need hierarchical streaming from a root tileset.json. For Quest-targeted GLBs, a practical upper limit is around 1.2 million polygons; if you cannot stay near that after optimisation, use tilesets.

At this scale, a single full-resolution model would be very slow to load over the internet, and trying to render all detail at once on a standalone Quest headset can freeze the device. With tilesets, BelowJS streams only the detail needed for the current view. During VR sessions, BelowJS prioritises rendering, then throttles and schedules tile queue work with small per-frame budgets to reduce locomotion hitches while streaming.

The complete dataset can be much larger than device memory because only a working set is downloaded, decoded, cached, and drawn. Practical scale still depends on a sound tile hierarchy, useful levels of detail, texture sizes, host throughput, and how quickly the viewer moves into uncached areas.

Denton Holme and Macedon model loading in BelowJS with progressive 3D Tiles streaming

Denton Holme and Macedon 2023 example: 455.25 MB total converted dataset size, 6,336,051 vertices at finest-detail leaf tiles, and texture area equivalent to about 77 x 4K textures. Tile detail streams in progressively as you move.

Metashape to BelowJS

Metashape exports tiled datasets as .3tz, which is an archive. If you are using Metashape, this corresponds to its Cesium 3D Tiles v1.1 (*.3tz) export option. BelowJS does not load the archive directly; it loads the root tileset.json inside a converted folder. The workflow is: export *.3tz, run the convert-3tz.sh conversion script, then load the resulting tileset.json URL in the tiled model example.

The script is a wrapper around 3d-tiles-tools: it runs convert, then upgrade --targetVersion (1.0 or 1.1, default 1.1), verifies tileset.json exists after each step, and removes the extra App/ wrapper folder. If you prefer your own pipeline, run equivalent steps and load the resulting tileset.json.

http://localhost:5173/examples/tileset/index.html?url=http://localhost:5173/examples/tileset/wreck-tiles/tileset.json&up=+Z

Live example: BelowJS 3D Tiles streaming demo.

up supports +Y, -Y, +Z, -Z, +X, and -X (default +Y). Geospatial reorientation defaults to 'auto'; set geospatialReorientation: 'force' to always level a tileset, or false to disable it.

Viewer Config

{
  type: 'tileset',
  url: '.../tileset.json',
  up: '+Z',
  geospatialReorientation: 'auto',
  autoCenter: true,
  maxTriangles: 1000000,
  loadAncestors: true
}

url must point to the root tileset.json. BelowJS follows the current 3d-tiles-renderer defaults unless you override them: errorTarget: 16, unlimited maxDepth, loadSiblings: true, loadAncestors: true, and maxTilesProcessed: 250. Keeping ancestors loaded preserves a coarse fallback until detailed children are ready, which avoids visible holes while moving. Set loadAncestors: false only after measuring the memory and visual-continuity tradeoff on your own tileset.

At runtime, BelowJS updates traversal cameras and resolution, then adjusts quality adaptively by changing errorTarget and maxTilesProcessed based on camera motion and rendered triangle counts. Automatic VR profiles use two KTX2 texture-transcoding workers on standalone headsets to limit decode bursts and four on PCVR to use the extra CPU capacity; ktxWorkerLimit can override this per model.

Troubleshooting

Note: 3D Tiles support was released in 1.7 and is currently experimental. It is planned to stabilise in BelowJS v2.

References

Next Steps

After loading works, deploy the tileset folder as static files.