Interactive Animated Landscape

An exploration of an animated interactive landscape built with three.js.

Today we are going to explore a playful animated landscape with a psychedelic look. The idea is to show how an experimentation on art and design with a generative process can lead to interesting interactive visuals which can be used in a variety of mediums like web, print, illustration, VJing, installations, games and many others. We made 3 variants of the landscape to show you how small changes in parameters can change a lot in visuals.

The demos are made with three.js and the animations and colors are controlled in a custom GLSL shader. For the letter animations we are using TweenMax.

The cool thing about doing this with WebGL is that it’s widely supported and with GLSL shaders we can animate thousands, even millions of vertices at 60 FPS on the major desktop and mobile web browsers.

If you’re not familiar with three.js and GLSL shaders, you can start by creating a scene and reading this introduction to Shaders.

Let’s go through the main build up of the demo.

Breaking down the demo

1. Creating terrain with a plane

Let’s make a basic three.js scene, place a plane with a nice amount of vertices, rotate it 90 degrees is the x-axis, and lift the camera a little bit:

interactive_landscape1_plane

Create custom vertex and fragment shaders and bind them to a ShaderMaterial. The objective is to displace vertices up in the vertex shader with a perlin noise and multiply it with a height value:

// pseudo-code for noise implementation

vec3 coord = vec3(uv, 1.0)*10.0;
float noise = 1 + pnoise( vec3( coord.x, coord.y + time, coord.z ));

float height = h * noise;

// we apply height to z because the plane is rotated on x-axis
vec4 pos = vec4( position.x, position.y, height, 1.0 );

// output the final position
gl_Position = projectionMatrix * modelViewMatrix * pos;

interactive_landscape2_terrain

2. Create a road with some math

Now we’ll use a little bit of math. We’ll implement the formula below, where x is the vertex x-coordinate, h is the maximum height of terrain, c is the center of road and w is the width of road:

interactive_lanscape_formula

Playing with those variables, we can get different results, as we can see in the graphs:

interactive_lanscape_graph_h

interactive_lanscape_graph_c

interactive_lanscape_graph_w

Now, applied in vertex-shader code, multiplied by the previously calculated noise it looks as follows:

// pseudo-code for formula implementation
float height = h * pow( abs( cos( uv.x + c ) ), w ) * noise;

// we apply height to z because the plane is rotated on x-axis
vec4 pos = vec4( position.x, position.y, height, 1.0 );

// output the final position
gl_Position = projectionMatrix * modelViewMatrix * pos;

interactive_landscape1_road

To make a curved road, we use uv.y as angle and take the sin of it to oscillate the center along the y-axis (the plane is rotated on the x-axis, remember?).

interactive_landscape3_road_curve

Tiny break: 📬 Want to stay up to date with frontend and trends in web design? Subscribe and get our Collective newsletter twice a tweek.

3. Adding color layers

Let’s colorize the terrain with a nice trick. First, create a color pallete image like this one:

interactive_landscape_pallete1

And then we’ll use it as a lookup texture in the fragment shader, getting the color value from the height calculated in the vertex shader as texture uv.y coordinate:

// pseudo-code for getting the color
vec2 coord = vec2( 0.0, normalize( height ) );
vec4 color = texture2D( palleteTexture, coord );

gl_FragColor = color

interactive_landscape4_colors_layers

4. Having fun adding interactivity

Now we’ve done the heaviest part, it’s easy to use mouse, touch or whatever input you want, to control the formula’s variables and get interesting forms of interactivity:

// JS pseudo-code in the render loop for uniforms manipulation with mouse
terrain.material.uniforms.c.value = (mouseX / window.innerWidth - 0.5) * 0.1;
terrain.material.uniforms.w.value = (mouseY / window.innerHeight - 0.5) * 4;

5. Final touches

Let’s adjust the camera position, add a nice color pallete, fog, a sky background, and we are done!

interactive_landscape1_final

We hope you enjoy this walk-through and find the experiment inspirational!

References and Credits

André Mattos

André Mattos is a visual artist and developer with more than 10 years of experience with creative technologies, digital production and team management in advertising, internet and film production industries.

Stay in the loop: Get your dose of frontend twice a week

Fresh news, inspo, code demos, and UI animations—zero fluff, all quality. Make your Mondays and Thursdays creative!