093

Contour Landscape

£10

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

advanced
3 more details
webgl-shader3d-transformsclick-toggle
Contour Landscape - GSAP animation effect preview

This demo reads better at your own screen size than in the frame below:

About this effect

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.

Read the full effect overview

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.

What's included

6 items
  • 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

Perfect for

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

How it works

1 section

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.

Difficulty Advanced
Includes HTML + JS + CSS source, documentation, AI setup prompt, lifetime updates

Lighthouse, as measured

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

Accessibility
100
Best practices
100

No performance score, on purpose. That figure depends on how you deploy: your server's compression and caching, your CDN, the connection and the device doing the test, none of which the code controls. The same page can score very differently on two consecutive runs, so measure it where it will live.

Paid effect

Purchase to unlock the code.

Buying Contour Landscape opens the HTML, CSS and JavaScript source, the full documentation, an AI setup prompt for your editor, and every update we ship to it. Standard license: unlimited personal and commercial projects.

£10 Standard license, unlimited projects

Browse free effects

Documentation

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 02.5 1.5 Maximum additional elevation while pressed
data-contours 628 18 Contour bands per terrain-height unit
data-wave 01 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.

Worked examples, the events and programmatic API, and the class reference ship with the download, alongside the full source.

Your cart

Your cart is empty

The Vault £99

The Vault library, plus future additions to the library.