SVG, canvas & parallax

SVG

import { toSvg } from "ridgescape";

const svg = toSvg({ seed: 1, palette: "dusk" }, { width: 1440, height: 900 });

One <svg> with one closed path per layer, coordinates rounded to two decimals: the same input always gives the same bytes. It's transparent above the ridges. Mist gradients get ids from their content, so two landscapes inlined in one page don't clash; idPrefix changes the prefix.

<Ridgescape> renders SVG by default, on the server too, with each layer in its own <svg> so parallax can move it.

Canvas

<Ridgescape scene={scene} renderer="canvas" />

Draws each layer on its own canvas in the browser, at up to twice the CSS size on high-density screens. Nothing is drawn on the server. Without React:

import { drawToCanvas, render } from "ridgescape";

const rendered = render(scene, { width: 1440, height: 900 });
canvas.width = 1440 * 2;
canvas.height = 900 * 2;
drawToCanvas(canvas.getContext("2d"), rendered, { pixelRatio: 2 });

render() returns plain data (each layer's path data, fill and depth); toSvg() and drawToCanvas() both draw from it, so the two renderers always match. drawLayer() paints one layer.

Parallax

<Ridgescape scene={scene} parallax />                      // strength 0.3
<Ridgescape scene={scene} parallax={{ strength: 0.6 }} />

As the page scrolls, each layer lags by strength × depth: the front layer moves with the page, the furthest the least. The landscape looks exactly as designed whenever its element is fully in view (for an element taller than the window: when its bottom edge is at the bottom of the window). Only transforms change, so nothing is regenerated or redrawn while scrolling.

Parallax is off under prefers-reduced-motion and with attach="fixed". Without React, attachParallax(element, { strength }) moves the elements inside element that have a data-depth attribute, and returns a function that stops it.

Building blocks

ExportWhat it does
resolve(scene)Fills every default: the complete scene as plain JSON.
buildProfile(layer)One period of a layer's crest, as heights from 0 to 1 every ~4px.
buildOutline(layer, size)The crest across the element, with anchors, as points.
periodicNoise(), octaves()The seamless noise underneath.
mix(a, b, t), layerColor()OKLab mixing, and a layer's default colour.
parallaxDelta(), parallaxOffset()The parallax maths, for your own scroll handling.

Stability

Output is a pure function of the scene, the element size and the package version. From 1.0, any change to generated output (presets included) is a major release; before 1.0, a minor version may change it and a patch never does.