Creating 3d Animated Gradient Effects with Javascript and WebGL

Johnny Simpson
JavaScript in Plain English
4 min readNov 15, 2020

--

I’ve been really interested lately in engaging gradient backgrounds. Most websites selling products are relatively static, and bringing something living can help improve conversions. Recently I was trying to create a compelling gradient effect as a background to a project website I am working on. The effect I was after should be a) simple, b) random and c) subtle. The final results can be found here.

A single frame from the gradient animation

So I started out, like anyone would with a basic CSS gradient. The CSS gradient used to be pretty new technology, but now is broadly supported by the major browsers, but it’s always good to have a background tag as a backup for anyone still using Internet Explorer.

background: rgba(0,0,0,1);
background: linear-gradient(315deg, rgba(0,0,0,1) 0%, rgba(255,255,255,1),1) 100%);

Easy then — I thought — I would write a simple JavaScript function to alternate the rgba numbers above. This is easy to do — but was such a disaster that I won’t share the outputs here. My second attempt involved using moving circles on an HTML canvas with a blend mode. This was more promising, but again, it wasn’t exactly what I was after.

Finally, enough was enough, I decided to move into the third dimension, and use three.js

Getting Started —three.js

I’m using three.js for this experiment because it makes WebGL very easy. To start I created a new file called index.html and another called script.js in the same folder. In index.html, paste the following hollow HTML structure.

<!DOCTYPE html><html><head>    <title>The best gradient effect of your life</title>    <script src="script.js"></script></head><body></body></html>

Concept

WebGL is a pretty overwhelming concept if you don’t know anything about it, as I didn’t before I started on my gradient fill journey. As I learned, there are essentially three parts to a successful WebGL — two shaders and a bit of JavaScript to manipulate the shapes produced.

What is a shader?

Shaders, as we describe here, are essentially functions which adjust the output of a 3D rendering. They’re part of a pipeline of tasks that happen when something is rendered in 3D on your screen.

One of these shaders is called the vertex shader — this will adjust every ‘vertex’ point on the page. It basically iterates over every point, and adjusts it based on what you have in the function. The other is the fragment shader. Think of this as adjusting the colour of each point on the page.

The shader is not in JavaScript, it is written GLSL, a C-like language which is passed straight to your GPU by three.js. You can paste these straight into the <body /> of your HTML page. As someone used to writing in JavaScript, these shaders look very foreign, but it basically breaks down into a few key pieces.

  • snoise() or simplex noise is a function within both of our shaders for generating noise or random vertices. You’ll see how this works, but in our vertex shader, we adjust the x and z positions of each point to create a cloth like effect. How did I come up with this? As it turns out, many have tried this before. See here for all your shader noise functions: https://gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83
  • uniform variables are variables we can manipulate straight from JavaScript in real time with three.js. We define them in our code below, and simply by updating these in JavaScript, we will update the 3D shape on our page (this was the coolest discovery from this experiment)
  • gl_Position and gl_FragColor are reserved variables which control the final output of our shaders.
  • vUv and uv are variables which carry information on the vertex we are currently adjusting.

The best way to learn about these more is to download the files in this article and try changing a few things in the shaders and JavaScript. Below is the final output of the shaders which you can paste straight into the body of your HTML:

The shader code is in the HTML, although can be pulled into Javascript through any means (for example, a variable containing the code within the Javascript itself)

JavaScript

I’ve imported the Three.js file in the codepen at the top, just remember, if you want to do that you must have your code on a server. You can’t just open the file in your browser. You can use your own computer’s localhost for this as well.

To understand a bit more about three, you can check out the three documentation. Essentially, we need to create a camera (for viewing), a renderer (for putting it all on the screen), and a scene (for placing the objects). We will then place a sheet or rectangle on the scene.

This is where uniform variables come in. We will define these in JavaScript, and we can then update them to alter the rendering process with JavaScript. We have a few other utility functions here, so please see the full code in the codepen or in the git at the end for more information.

If you adjust line 42, multiplyScalar(5) to multiplyScalar(1), you can see the object fully without the massive zoom in. This will give you an idea about how this is working, since you’ll see it is just a sheet being warped, and the sharp lines produced in the gradient are actually just parts of the sheet overlapping with itself.

Alright — now comes the animation. We will use JavaScript animation frames as this process can become quite CPU/GPU intensive. Again, the code is minimal here, we can re-render the scene and update its values for every frame produced by requestAnimationFrame().

Conclusion

When I started this, I knew the effect I wanted to produce, but had no idea how to do it. The applications and adjustments you can make to this effect give you a lot of flexibility for backgrounds to websites, header elements, or anything like that.

Relevant Links

--

--