# Contour Landscape

> Sculpt a lit three-dimensional landscape with a press or drag, as charcoal contours wrap around the rising terrain.

Canonical: https://gsapvault.com/effects/contour-landscape
Live demo: https://gsapvault.com/demos/contour-landscape/index.html

| Property | Value |
|----------|-------|
| Type | effect |
| Tier | paid |
| Price | £10 |
| Difficulty | advanced |
| Plugins | Core GSAP only |
| Techniques | webgl-shader, 3d-transforms, click-toggle |
| Uses Lenis | No |

## Lighthouse, as measured

Google Lighthouse on the demo, 15 September 2026. A measurement of the demo as shipped, not a promise for your page.

| Category | Score |
|----------|-------|
| Accessibility | 100 |
| Best practices | 100 |

No performance score is published: it depends on the deployment (server compression, caching, CDN, connection and device) rather than on the code, so measure it where it will live.

## Overview

A chalk landscape with real depth. Fine charcoal contours trace every ridge, while a single rust-coloured height band follows the shape of the surface. Directional light and soft terrain shadows give the sheet a sculptural presence.

Press and drag to raise an elevation beneath your hand. The contour lines re-form around the mound and a softer wave follows behind it. Release to let the landscape settle, or use the keyboard-operable Raise terrain control to hold the sculpted state.

## Features

- A perspective terrain sheet with lit ridges, directional shadows and a thin physical edge
- Pointer and touch sculpting with contour lines that follow the changing elevation
- A restrained rust height band and a delayed secondary wave
- A native Raise terrain / Reset terrain control for keyboard use
- Designed portrait framing, lower mobile geometry and optional gentle touch-device drift
- Static terrain images for reduced motion, unavailable WebGL or JavaScript

## Use Cases

- Architectural and landscape design portfolios
- Outdoor product launches and exploration-themed websites
- Editorial section backgrounds with sculptural material character

## Vibe-Code Ready Setup

This effect includes `START-HERE-AI.md`, a product-specific copy-paste setup prompt for Cursor, Claude Code, ChatGPT, GitHub Copilot, Windsurf, and other coding assistants. It tells the assistant to inspect the existing stack, integrate the supplied files, preserve the design, scope selectors, retain accessibility and responsive behaviour, add framework-appropriate GSAP cleanup, and report what it tested.

[How AI-assisted setup works](https://gsapvault.com/vibe-coding)

## How It Works

Three.js renders the sculpted surface, contour material and light response. GSAP controls the press and release transition and motion-preference lifecycle. The terrain responds continuously to pointer or touch movement, and returns smoothly to its resting landscape when released.

## Documentation

How this effect works and how it goes into a page. The reference you use once you own the files (worked examples, events, the programmatic API, the class list) ships with the download.

### Quick Start

**1. Copy `assets/` into your project.** Keep `terrain.webp` and `terrain-mobile.webp`: they are the designed static alternative, not WebGL textures. No photograph, texture download or model file is required by the live shader.

**2. Add the following to your HTML `<head>`, before the stylesheet:**

_Code snippet omitted: it ships with the download._

This synchronous WebGL2 probe prevents the static alternative flashing before the live terrain arrives. It does not hide anything for reduced-motion or unsupported browsers.

**3. Add the stage:**

_Code snippet omitted: it ships with the download._

Give the root a definite height and position. The demo's `.showcase-shell` supplies this; an embedded version can use `.contour { position: relative; height: 600px; }`.

**4. Add before `</body>`:**

_Code snippet omitted: it ships with the download._

The guarded module loader restores the static terrain if a dependency fails. If your project already imports Three.js, reuse that module instance as `window.THREE`, then load `script.js` after it; do not add a second copy. In a bundled project, import the effect after assigning both `window.THREE` and `window.gsap`. Keep your dependency failure handling and the pre-paint probe.

### Using It With Your Own Design

**Required hooks:** `[data-contour-landscape]` identifies each independent instance. Keep its `.contour__source` picture and useful alt text. `[data-contour-toggle]` is a native button for keyboard access; `[data-contour-cue]` receives the input-appropriate prompt. The script creates and removes its own canvas.

**Removable demo styling:** `.showcase-shell`, `.showcase-stage`, the font import, the control position and the button's visual treatment are presentation only. Replace them freely. For an embedded component, remove the demo's `body { overflow: hidden }` and full-viewport shell. The terrain colours, line density and camera composition are in the shipped shader and scene source; changing the CSS palette alone does not recolour the material.

**Required CSS:** preserve root positioning, a non-zero size, clipping, the canvas positioning and both fallback gates (`.gl .contour__source` and `.contour.is-live .contour__source`). Keep `touch-action: pan-y` so a vertical gesture can scroll the containing page; a touch press or horizontal drag sculpts the terrain. Preserve visible keyboard focus. Keep controls hidden until `.is-live` because the static version has no action to perform.

The ground is a neutral chalk grey. You can place ordinary content alongside the stage or in an overlay; keep it out of the ridges and provide enough contrast. The terrain is a decorative interaction, so no essential information should exist only in its motion.

### Options

Set attributes before initialization. Numeric zero is accepted.

| Attribute | Values | Default | Description |
|---|---|---|---|
| `data-strength` | `0`–`2.5` | `1.5` | Maximum additional elevation while pressed |
| `data-contours` | `6`–`28` | `18` | Contour bands per terrain-height unit |
| `data-wave` | `0`–`1` | `1` | Secondary trailing wave strength; `0` disables it |
| `data-drift` | `0` or `1` | `1` | Gentle autonomous movement on touch devices; `0` disables it |

Desktop resting terrain is still. On touch devices, optional low-amplitude drift gives the surface life between interactions. Pressing takes over immediately. Reduced motion always disables the live scene and ambient movement.

### Accessibility

- **Keyboard:** Tab to Raise terrain, then Enter or Space to hold the raised state. Activate Reset terrain to settle it. The button exposes `aria-pressed` and has a visible focus ring.
- **Touch:** press or drag horizontally to sculpt. Vertical gestures remain available to the containing page; a cancelled gesture releases the elevation.
- **Reduced motion:** no renderer or animation loop is created. A composed image of the same actual terrain is shown instead. Changing the preference while mounted disposes or rebuilds the scene safely.
- **No WebGL, no JavaScript, blocked dependencies:** the same source picture remains visible. No broken interactive control is offered.
- **Context loss:** the static terrain returns until WebGL can restore the scene.

### Performance

The scene is procedural: no texture uploads, model files, post-processing passes or shadow-map render targets. Desktop uses a 192 × 131 terrain grid; touch devices use 128 × 87. Both use one terrain draw plus the thin edge and soft contact-shadow plane. The directional terrain shadow is calculated from the height field.

**The first performance lever is pixel ratio:** lower the cap in `createContourScene()` before changing the geometry. It is capped at 2 for fine pointers and 1.5 for coarse pointers. Lower the segment count next if targeting older phones. The render loop runs only while the root is visible and the document is active. Pointer and trail smoothing use elapsed time, not a per-frame constant. A ResizeObserver tracks the root, including iframe layout changes.

### Browser Support

The live scene needs WebGL2, ES modules, import maps and modern pointer events. Current Chrome, Edge, Firefox and Safari support the required platform features when WebGL2 is enabled. Disabled GPU rendering, dependency failures and reduced motion retain the static terrain. The supplied WebP images are captures of this procedural scene and have no external licensing dependency.

### Dependencies

| Dependency | Role |
|---|---|
| GSAP 3.12+ (demo pin: GSAP 3.15.0) | Motion-preference lifecycle and the press/release envelope only |
| Three.js, pinned to 0.180.0 | Geometry, shader material, lighting, camera and WebGL renderer |

The render loop owns continuous pointer chasing, trail lag and time. The shader owns displacement, contours, colour and lighting. GSAP does not animate the geometry or the pointer coordinates. No GSAP plugins and no Lenis are required.

## What You Get

- `index.html`: working demo page
- `assets/script.js`: commented, readable source
- `assets/style.css`: effect styles
- `README.md`: full documentation with examples and framework integration notes
- `START-HERE-AI.md`: product-specific copy-paste prompt for AI-assisted setup
- `LICENSE.txt`: standard license terms
- Lifetime updates: re-download anytime from your library

## Get the Code

This is a premium effect. The standard license costs £10 one-time and covers unlimited personal and commercial projects with no attribution required for our code. Bundled third-party assets retain their own licences and attribution requirements. The only restrictions: no redistribution of the code itself and no competing effect libraries.

- [Buy Contour Landscape](https://gsapvault.com/effects/contour-landscape)
- [The Vault (£99 one-time, best value): every collection in the Vault library, plus future items added to those collections](https://gsapvault.com/effects)

---

From [GSAP Vault](https://gsapvault.com): production-ready GSAP animation effects. Full catalog for agents: https://gsapvault.com/llms-full.txt
