Loss Landscape
A 2D loss surface drawn as a grid heatmap, with an optional optimizer trajectory overlaid on top. Pick one of the four built-in surfaces (convex bowl, saddle, double-well, Rosenbrock) or pass a custom function — the component samples a gridSize x gridSize heatmap, draws axes through the origin when visible, and springs a highlighted dot to whichever trajectory step you scrub to.
Loss landscape: bowl.Loss landscape bowl. Step 13 of 13. Position (-0.01, 0.01). Loss 0.000.
Customize
Landscape
bowl
Trajectory
12
Resolution
32
Installation
npx shadcn@latest add https://craftbits.dev/r/loss-landscape.jsonUsage
import { LossLandscape } from "@craft-bits/core";
<LossLandscape landscape="bowl" />Drop in a trajectory and scrub through it:
<LossLandscape
landscape="rosenbrock"
trajectory={[
{ x: -1.5, y: 1.5 },
{ x: -1.2, y: 1.2 },
{ x: -0.6, y: 0.4 },
{ x: 0.2, y: 0.0 },
{ x: 1.0, y: 1.0 },
]}
currentStep={2}
/>Pass a custom function and a different colour ramp:
<LossLandscape
landscape={{ fn: (x, y) => Math.sin(x) * Math.cos(y) + 0.1 * (x * x + y * y) }}
xRange={[-3, 3]}
yRange={[-3, 3]}
colorScheme="viridis"
/>Understanding the component
- Heatmap, not contours. Each of
gridSize x gridSizecells samples the surface at its centre, then maps the value to a fill color. The defaultaccentscheme tints--cb-accentby intensity;viridisandplasmause three-stop perceptual ramps inoklchso the gradient stays legible under light and dark themes. - Four built-in surfaces.
bowlis the convex baseline.saddlehas zero gradient at the origin — SGD stalls there.double-wellhas two minima at(plus or minus 1, 0)— gradient descent is trapped by whichever basin it starts in.rosenbrockis the classic narrow curved valley. - Custom surfaces via
{ fn }. Any pure(x, y) -> numberplugs in directly; the component normalises the visible range automatically across the sampled grid. - Trajectory is fully optional. When omitted, the heatmap stands alone. When present, the trajectory renders as a polyline + dots, and the dot at
currentStepis enlarged and springs into position withSPRINGS.smoothfrom@craft-bits/core/motion. - Cell-centre sampling. Each grid cell samples the surface once at its centre — clean enough for 32x32 without flickering edges. Bump
gridSizeto 48 for smoother gradients (and a slightly heavier DOM). - Reduced-motion fallback. With
prefers-reduced-motion: reduce, the highlighted dot snaps to position with no spring; no per-step motion is shown.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
landscape | 'bowl' | 'saddle' | 'double-well' | 'rosenbrock' | { fn } | 'bowl' | Loss surface to draw. |
xRange | readonly [number, number] | [-2, 2] | Visible math-space x-range. |
yRange | readonly [number, number] | [-2, 2] | Visible math-space y-range. |
gridSize | number | 32 | Heatmap resolution in cells per side. |
trajectory | readonly LossLandscapePoint[] | — | Optional optimizer trajectory to overlay. |
currentStep | number | — | Controlled highlighted step. Pair with onCurrentStepChange. |
defaultCurrentStep | number | 0 | Uncontrolled initial step. |
onCurrentStepChange | (step: number) => void | — | Fires when the highlighted step changes. |
colorScheme | 'accent' | 'viridis' | 'plasma' | 'accent' | Heatmap color ramp. |
size | number | 360 | SVG side length in pixels. |
transition | Transition | SPRINGS.smooth | Spring for the current-dot. |
className | string | — | Merged onto the root via cn(). |
Accessibility
- The figure is
role="figure"with a labelled title that names the active surface. - An
aria-live="polite"summary announces the surface, the current step index, the math-space position, and the loss value whenever they change. - Color is never the only signal — when a trajectory is present, the summary spells out the position and loss textually, so the figure remains readable without color.
prefers-reduced-motion: reducesnaps the highlighted dot with no spring.
Credits
- Extracted from:
craftingattention(app/src/lessons/primitives/math/LossLandscape.tsx). Stripped the lesson-specific Observe/Predict modes, prediction-quiz scoreboard, click-to-place-start interaction, transport bar, undo/redo plumbing, and the built-in three-optimizer simulation (now handled byOptimizerRacetrack). Generalised to a pure 2D-surface primitive: four built-in landscapes, custom{ fn }support, three color ramps, and an optional trajectory overlay driven bycurrentStep.