Skip to content
Documentation
Skill

/maplibre-terrain-rendering

Drawing elevation in MapLibre GL JS once a raster-dem source works — hillshade and `hillshade-method`, the `color-relief` layer and the `["elevation"]` expression, runtime contours with `maplibre-contour`, and 3D terrain with its camera and sky. Use when relief looks harsh,

BOOST
From plugin
maplibre-agent-skills
15511 skills
Install
$ npx -y skills add maplibre/maplibre-agent-skills --skill maplibre-terrain-rendering --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/maplibre-terrain-rendering

Context preview

The summary Claude sees to decide when to auto-load this skill.

Drawing elevation in MapLibre GL JS once a raster-dem source works — hillshade and `hillshade-method`, the `color-relief` layer and the `["elevation"]` expression, runtime contours with `maplibre-contour`, and 3D terrain with its camera and sky. Use when relief looks harsh,

SKILL.md

maplibre-terrain-rendering.SKILL.md
name: maplibre-terrain-rendering
description: Drawing elevation in MapLibre GL JS once a raster-dem source works — hillshade and `hillshade-method`, the `color-relief` layer and the `["elevation"]` expression, runtime contours with `maplibre-contour`, and 3D terrain with its camera and sky. Use when relief looks harsh, flat, or absent, or when the layer type or property for an elevation effect is unclear.
status: provisional

MapLibre Terrain Rendering

A `raster-dem` source is elevation, not a picture. Four different things draw it, each with its own layer type or API, and most of the wrong answers here are Mapbox GL JS properties that MapLibre never had.

When to Use This Skill

These are symptoms as you would observe them, before you know the cause:

  • **Harsh hillshade contrast:** black-and-white ridges, blown-out slopes, chalky valleys — or several hillshade layers stacked to soften them (one layer with `hillshade-method: "multidirectional"`).
  • **Dynamic elevation coloring:** the map needs coloring by height, client-side, with no pre-rendered tiles (a `color-relief` layer over `["elevation"]`, not `raster-color` on a `raster` layer).
  • **Runtime contour lines:** contour lines straight from a `raster-dem` source, with no pre-generated contour tileset (`maplibre-contour`, not a hand-rolled marching-squares pipeline).
  • **Flat 3D terrain:** `map.setTerrain()` is on and nothing extrudes (the camera is at `pitch: 0`, and raising `exaggeration` will not fix it).
  • **Blank horizon:** pitching the camera shows empty page background above the terrain (no root-level `sky` object or `map.setSky()`; there is no `type: "sky"` layer in MapLibre).
  • **Silent property failures:** a hillshade, sky, or terrain-coloring property does nothing and raises no error (a Mapbox GL JS property MapLibre never had).
  • **Terrain lag or frame drops:** stutter when panning or tilting with terrain on (stacked hillshade passes, dense draped layers).

**Not this skill.** Elevation values that are wrong, spiky, or inverted, choosing an elevation tileset, `encoding`, and generating DEM tiles — the style specification's `raster-dem` source page, <https://maplibre.org/maplibre-style-spec/sources/#raster-dem>. Where hillshade sits among the other layers of a style — [maplibre-cartography](../maplibre-cartography/SKILL.md).

Which one you want

| Goal | What draws it | | ----------------------------------------------- | --------------------------------------------------------------------- | | Shaded relief under a 2D map | A `hillshade` layer. No `setTerrain` needed — the two are independent | | Color by height (hypsometric tint) | A `color-relief` layer (GL JS 5.6) | | Contour lines, without pre-generating a tileset | The `maplibre-contour` library, feeding a `vector` source | | A tilted, extruded surface you fly over | `map.setTerrain()` plus camera pitch |

All four read the same `raster-dem` source; one source can serve all of them.

Hillshade, and the harshness fix that is not stacking

Every `hillshade-*` property is a **paint** property.[1] The defaults that matter: `hillshade-exaggeration` `0.5`, `hillshade-illumination-direction` `335`, `hillshade-illumination-anchor` `viewport` (the light is fixed to the top of the screen, so it does not turn with the map's bearing unless you set `map`), `hillshade-shadow-color` `#000000`, `hillshade-highlight-color` `#FFFFFF`.

Harsh output usually comes from the shading **method**, not the colors. `hillshade-method` (GL JS 5.5) takes `standard` (the default), `basic`, `combined`, `igor`, and `multidirectional`.[1] `multidirectional` lights the surface from several independent directions in one pass and `igor` minimizes the effect on the map features drawn beneath it; both soften terrain on a single layer.

{
  "id": "hillshade",
  "type": "hillshade",
  "source": "terrain-dem",
  "paint": {
    "hillshade-method": "multidirectional",
    "hillshade-exaggeration": 0.4,
    "hillshade-illumination-anchor": "map",
    "hillshade-shadow-color": "rgba(30,40,80,0.45)",
    "hillshade-highlight-color": "rgba(255,240,200,0.4)"
  }
}

**Do not stack several `hillshade` layers at different illumination directions.** It was the only way to get multi-directional shading before 5.5; since then it is one paint property against N extra draw passes over the same DEM. Pure black shadows and pure white highlights are the other harshness lever: give both an alpha, and use warm highlights with cool shadows over imagery.

Color-relief: coloring by height

`color-relief` (GL JS 5.6) colors a `raster-dem` source directly, client-side; there is no pre-rendering step and no `raster` layer involved. Its paint properties are `color-relief-color`, `color-relief-opacity` (default `1`), and, from GL JS 5.20, `resampling`.[1] The ramp is an `interpolate` expression over `["elevation"]`, which returns meters above the DEM's vertical datum and **is only valid inside `color-relief-color`**.[2]

{
  "id": "hypsometric",
  "type": "color-relief",
  "source": "terrain-dem",
  "paint": {
    "color-relief-color": [
      "interpolate",
      ["linear"],
      ["elevation"],
      0,
      "#2c7bb6",
      500,
      "#abd9e9",
      1500,
      "#ffffbf",
      2500,
      "#fdae61",
      4000,
      "#ffffff"
    ],
    "color-relief-opacity": 0.8
  }
}

MapLibre has no `raster-color`, `raster-value`, or `raster-color-mix`; those belong to another renderer and will be dropped as unknown properties. A `raster` layer pointed at a DEM shows packed RGB, not a tint.

Contours at runtime

[`maplibre-contour`](https://github.com/onthegomap/maplibre-contour) derives vector contour tiles in a Web Worker from the same DEM tiles and serves them through a regis

Read more
Ships withmaplibre-agent-skills

Curated guidance for AI assistants building MapLibre applications — covering ecosystem and open-source best practices. Agent skills are markdown files that AI coding assistants read as context.

Get the whole plugin

Other skills on maplibre-agent-skills.