Scenes, layers & anchors

A scene

{
  seed: "home",          // any string or number; the same seed draws the same landscape
  palette: "dusk",       // a preset name, or your own colours
  layers: 5,             // a count, or a list of layers, back to front
  height: "45%",         // the band the landscape fills, from the bottom
  haze: { strength: 0.5, curve: 1.2, color: "#f3b38a" },
}

Every field is optional. With nothing set you get four rolling layers at dusk.

Lengths (heights, baselines, anchor positions) are px as a number, or a percentage as a string: "45%" is 45% of the element's height for vertical lengths, and of its width for horizontal ones.

Layers

Layers are listed back to front. Each one has a depth from 1 (the horizon) to 0 (the front), spread evenly by default, and depth sets the rest of its defaults: further layers sit higher, are smoother, have narrower hills, repeat over a longer width, fade more into the haze and lag more in parallax. So layers: 5 already looks like a landscape, and setting a field on one layer changes just that.

{
  profile: "peaks",      // "rolling" (default), "peaks", "mesa" or "dunes"
  depth: 0.8,            // 0 front, 1 horizon
  baseline: "20%",       // height of the lowest valleys, from the bottom
  amplitude: "25%",      // how far the highest crest rises above the baseline
  roughness: 0.6,        // 0 smooth to 1 craggy
  wavelength: 500,       // typical hill width, px
  period: 2200,          // the layer repeats every this many px
  color: "#7a5a83",      // instead of the palette and haze
  mist: 0.4,             // fade toward the haze colour in the valleys
  seed: "back",          // instead of one derived from the scene's seed
  anchors: [ ... ],
  trees: true,           // a tree line: see below
  lakes: [ ... ],        // water: see below
}
layers: [{ profile: "peaks" }, { profile: "mesa" }, { profile: "dunes" }, { profile: "rolling" }]

Repeating: each layer is generated for one period and repeats without a seam, so any width works and nothing stretches: a wider screen shows more landscape. Periods differ between layers, so the repeats never line up.

Seeds: layer seeds are derived from the scene's seed and the layer's position in the list, so inserting a layer changes the ones after it. Give a layer its own seed to pin it.

Anchors

Put a peak, a dip or a flat stretch where you want it. The terrain around it blends in.

anchors: [
  { at: "70%", kind: "peak", height: "40%", width: 500 },
  { at: "25%", kind: "flat", width: "30%" },   // room for a heading
]
FieldDefaultMeaning
atx position: px, or % of the element's width.
kind"peak""peak" and "dip" lift or lower the terrain, keeping its detail; "flat" levels it over the middle half of its width.
heightpeak 115%, dip 0, flat 20% of the amplitudeHeight above the layer's baseline at at.
width"20%"How far its influence reaches, in total.
layers: [
  { profile: "peaks", anchors: [{ at: "70%", height: "45%", width: 600 }] },
  { anchors: [{ at: "25%", kind: "flat", width: "35%" }] },
  {},
]

Anchors repeat with the layer's period, like the rest of it. In % they follow the element's width; in px they stay put.

Trees

A tree line along a layer's crest, part of the same silhouette:

trees: true
trees: {
  kind: "conifer",       // "deciduous", "mixed", one shape, a list, or weights: see below
  height: 44,            // px or %; default by depth, 44px at the front down to 12px at the horizon
  density: 0.6,          // sparse to a solid forest
  cover: 1,              // share of the ridge that's forest rather than clearing
  variation: 0.3,        // how much heights vary
  color: "#24452c",      // default: the layer's colour
  seed: "woods",
}
{ seed: 9, palette: "forest", layers: [{ profile: "peaks" }, { trees: true }, { trees: true }, { trees: { density: 0.85 } }] }

Each tree picks a shape at random from the kind. "conifer" is mostly spruce and fir with some pine and cypress, "deciduous" is maple and oak with some poplar, and "mixed" is half of each. Or choose the shapes yourself:

ShapeSilhouette
spruceA narrow triangle with jagged, branchy sides.
firA smooth spire with gently curved sides.
pineBroad and round-shouldered, with flat-topped clumps along its top.
cypressA tall, narrow column with a rounded point.
mapleA round crown with two lower shoulders.
oakBroad and lumpy: a cluster of round crowns.
poplarTall and narrow, with a couple of bumps down its sides.
kind: "oak"                          // only oaks
kind: ["spruce", "poplar"]           // half and half
kind: { spruce: 3, fir: 1, oak: 1 }  // weights: 60% spruce, 20% each of the others
layers: [
  { profile: "peaks" },
  { trees: { kind: "mixed" } },
  { trees: { kind: { spruce: 2, pine: 1, oak: 1, poplar: 1 }, height: 56, density: 0.7 } },
]

When shapes are mixed, broad crowns stand a little lower than spires, scaled so the average is still height; a single shape is exactly height tall. Trees repeat with their layer, stand on the ground at their feet, and skip cliffs. Layers are heights, so there's nothing to see under a crown: a dense row reads as a forest edge, while a lone maple or oak looks more like a bush. With color, the trees are drawn as a second shape behind the bare ground, so they can be darker or lighter than the hill they stand on.

Lakes

A stretch of water on a layer, reflecting everything behind it:

lakes: [{
  at: "45%",             // centre: px or % of the width
  width: "30%",          // px or % of the width; "100%" is water all the way across
  level: "8%",           // water height above the layer's baseline; default 12% of its amplitude
  reflection: true,      // mirror what's behind; false for flat colour
  ripples: true,         // streaks on the surface
  color: "#7d97ab",      // default: the sky's bottom colour, toward the land's
}]
layers: [
  { profile: "peaks" },
  { profile: "peaks" },
  { trees: { density: 0.8 }, lakes: [{ at: "45%", width: "100%", level: "18%" }] },
]

The ground under the water is held just below it, with a small bank at each shore and no trees. Everything drawn before the lake's layer (sky, sun, clouds, the ridges behind) is mirrored across the waterline and clipped to the water, then tinted more deeply further down. Layers in front of a lake hide it like they hide anything else, so a lake on the front layer (water down to the bottom edge) shows the most. Lakes repeat with their layer; one as wide as the element, or too wide to leave land between its repeats, carries on all the way across with no shores. With parallax, each reflection moves the opposite way to what it mirrors, as a real one would, so it stays true while you scroll.

Palettes and haze

A palette is a near colour (the front layer), a far colour (the furthest layer), a haze colour, a sky gradient and an optional default mist. Each layer's colour runs from near to far by depth, then fades toward the haze by strength × depthcurve. Mixing is done in OKLab, so fades stay even. Presets: dusk, dawn, alpine, forest, desert, fog, night, mono.

import { palettes } from "ridgescape";

palette: { ...palettes.dusk, near: "#1d1330" }
palette: { near: "#173a2b", far: "#5f8a6a", haze: "#dfe9d8", sky: ["#bfd8d2", "#eef3e4"] }

Palette and haze colours are mixed ahead of time, so they must be hex, rgb(), hsl() or named. A layer's own color is used as is, so it can be anything CSS accepts, including var(--x).