Skip to content

[Bug] TerrainLayer: terrarium tiles render spurious spikes when served over http (clean under file://) #10400

Description

@s-celles

Description

Description

A TerrainLayer using Mapzen / terrarium-encoded elevation tiles renders random vertical spikes all over the mesh — including over the ocean, where decoded elevation should be ≤ 0.

The spikes appear only when the page is served over http(s):// (local dev server, GitHub Pages). Opening the exact same HTML file via file:// renders clean terrain.

The raw terrarium PNGs are valid: decoding the same tiles independently (Python/Pillow and pure-JS UPNG) shows no out-of-range pixels. So the source data is fine — the corruption is introduced client-side, and only on the worker decode path.

Reproduced on Chrome (macOS, wide-gamut) and on NixOS, across deck.gl 8.9.x, 9.0.x and 9.1.0.

Image Image

Additional investigation

  • file:// cannot spawn the loaders.gl worker, so terrain falls back to a main-thread decode → correct. Over http, the worker path is used → spikes. This points at the worker's tile/image decode: a ±1 error on the terrarium R channel = ±256 m, which matches the spike magnitude.
  • Independent decode of the same PNG bytes (Python/Pillow, pure-JS UPNG) is clean → the source tiles are valid.
  • Could not work around it via loadOptions: imagebitmap:{premultiplyAlpha:'none', colorSpaceConversion:'none'}, image:{type:'image'|'imagebitmap'}, terrain:{tesselator:'delatin'}, various meshMaxError — all still spike.
  • loadOptions:{worker:false}black terrain with the console error below (no synchronous terrain parser available in the bundle).

A working workaround (for others hitting this): decode the terrarium tiles in pure JS (UPNG) and build the mesh manually with a custom TileLayer + SimpleMeshLayer (LNGLAT positions), bypassing the worker entirely.

Environment

  • Framework version: deck.gl@9.1.0 (also reproduced on 9.0.x and 8.9.x)
  • Browser: Chrome (latest)
  • OS: macOS (wide-gamut display) and NixOS

Logs

Default (worker enabled): no console errors — only the visual spikes.

With loadOptions:{worker:false}:

Error: terrain loader - no parser found and worker is disabled

AI disclosure

This investigation and write-up were done with the assistance of an AI coding agent (Claude) while working on https://s-celles.github.io/ogn-3d-viewer/ (code https://github.com/s-celles/ogn-3d-viewer ) . The behavior was reproduced and the findings (http vs file://, independent decoding of the raw tiles, the loadOptions attempts, and the worker:false error) were verified manually by a human before filing. The minimal reproduction above was run and confirmed.

Flavors

  • Script tag
  • React
  • Python/Jupyter notebook
  • MapboxOverlay
  • GoogleMapsOverlay
  • CARTO
  • ArcGIS

Expected Behavior

Terrain elevation is decoded identically regardless of how the page is served; no spurious spikes.

Steps to Reproduce

  1. Save the single-file HTML below.
  2. Serve it (e.g. python -m http.server) and open http://localhost:8000/ → spikes everywhere (clearest over the sea, where elevation should be ≤ 0).
  3. Open the same file via file:// → clean terrain.
<!doctype html>
<html>
<head><meta charset="utf-8"><style>html,body,#map{margin:0;height:100%}</style>
<script src="https://unpkg.com/deck.gl@9.3.5/dist.min.js"></script></head>
<body><div id="map"></div>
<script>
const {Deck, MapView, TerrainLayer} = deck;
const TERRAIN = 'https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png';
const TEXTURE = 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}';
const k = 2.0; // vertical exaggeration (spikes are visible without it too)
new Deck({
  parent: document.getElementById('map'),
  views: [new MapView({id:'main'})],
  initialViewState: {longitude:0.30667, latitude:46.5875, zoom:11.3, pitch:62, bearing:0, maxPitch:85},
  controller: true,
  layers: [new TerrainLayer({
    id:'terrain', minZoom:0, maxZoom:13, elevationData:TERRAIN, texture:TEXTURE,
    elevationDecoder:{rScaler:256*k, gScaler:1*k, bScaler:k/256, offset:-32768*k}
  })]
});
</script>
</body></html>

Environment

  • Framework version:
  • Browser:
  • OS:

Logs

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions