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.
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
Expected Behavior
Terrain elevation is decoded identically regardless of how the page is served; no spurious spikes.
Steps to Reproduce
- Save the single-file HTML below.
- Serve it (e.g.
python -m http.server) and open http://localhost:8000/ → spikes everywhere (clearest over the sea, where elevation should be ≤ 0).
- 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
Description
Description
A
TerrainLayerusing 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 viafile://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.
Additional investigation
file://cannot spawn the loaders.gl worker, so terrain falls back to a main-thread decode → correct. Overhttp, 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.loadOptions:imagebitmap:{premultiplyAlpha:'none', colorSpaceConversion:'none'},image:{type:'image'|'imagebitmap'},terrain:{tesselator:'delatin'}, variousmeshMaxError— 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
Logs
Default (worker enabled): no console errors — only the visual spikes.
With
loadOptions:{worker:false}: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, theloadOptionsattempts, and theworker:falseerror) were verified manually by a human before filing. The minimal reproduction above was run and confirmed.Flavors
Expected Behavior
Terrain elevation is decoded identically regardless of how the page is served; no spurious spikes.
Steps to Reproduce
python -m http.server) and openhttp://localhost:8000/→ spikes everywhere (clearest over the sea, where elevation should be ≤ 0).file://→ clean terrain.Environment
Logs
No response