From our sponsor: Ready to show your plugin skills? Enter the Penpot Plugins Contest (Nov 15-Dec 15) to win cash prizes!
Today we want to show you some shader art made with WebGL. The idea is to create a decorative background effect by twisting images and hexagonal grid patterns smoothly on page scroll. The effect aims to be as light as possible on desktops or mobile devices.
Basic HTML / CSS structure
Let’s have a quick look at some HTML and CSS we need to define. We’ll use a fixed canvas element that we’ll size to be fullscreen:
<body>
<canvas id="webgl"></canvas>
...
</body>
canvas#webgl {
display: block;
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: -1;
}
The WebGL part
We’ll be using the regl library which is a light-weight WebGL wrapper. If you are getting started with WebGL, it’s a great way to begin writing your own shaders and custom graphical effects using this fast library that has a functional data-driven style inspired by React. If you want to get started with shaders, check out this article by Paul Lewis: An Introduction to Shaders.
Let’s have a look at the interesting parts of the code that we use throughout the demos.
GLSL
<script src="js/regl.min.js"></script>
<script type="x-shader/x-fragment" id="fragmentShader">
#define TWO_PI 6.2831853072
#define PI 3.14159265359
// On iOS, specifying highp works more smoothly.
precision highp float;
uniform float globaltime;
uniform vec2 resolution;
uniform float aspect;
uniform float scroll;
uniform float velocity;
...
</script>
Initialize regl from a canvas element
var canvas = document.querySelector('#webgl');
var regl = createREGL({
canvas: canvas,
onDone: function(error, regl) {
if (error) { alert(error); }
}
});
Tiny break: 📬 Want to stay up to date with frontend and trends in web design? Subscribe and get our Collective newsletter twice a tweek.
Create a regl draw command
var draw = regl({
// Fragment Shader
frag: document.querySelector('#fragmentShader').textContent,
// Vertex Shader
vert: 'attribute vec2 position;
void main() {
gl_Position = vec4(3.0 * position, 0.0, 1.0);
}',
attributes: { position: [ [-1, 0], [0, -1], [1, 1] ] },
count: 3,
uniforms: {
globaltime: regl.prop('globaltime'),
resolution: regl.prop('resolution'),
aspect: regl.prop('aspect'),
scroll: regl.prop('scroll'),
velocity: regl.prop('velocity')
}
});
Our fragment shader is the GLSL code in the script element #fragmentShader
.
Hooking in a per-frame callback function
The code that runs the regl processes for each frame of requestAnimationFrame is defined in the callback function. In addition, we also define variables that store the scroll status.
// Scroll variables
var scroll = 0.0, velocity = 0.0, lastScroll = 0.0;
regl.frame(function(ctx) {
// Resize a canvas element with the aspect ratio (100vw, 100vh)
var aspect = canvas.scrollWidth / canvas.scrollHeight;
canvas.width = 512 * aspect;
canvas.height = 512;
// Scroll amount (0.0 to 1.0)
scroll = window.pageYOffset / (document.documentElement.scrollHeight - window.innerHeight);
// Scroll velocity
velocity = velocity * 0.99 + (scroll - lastScroll);
lastScroll = scroll;
// Clear the draw buffer
regl.clear({ color: [0, 0, 0, 0] });
// Execute a REGL draw command
draw({
globaltime: ctx.time,
resolution: [ctx.viewportWidth, ctx.viewportHeight],
aspect: aspect,
scroll: scroll,
velocity: velocity
});
});
The important thing is that the size of the canvas is set smaller than the display size (100vw, 100vh). This will make the actual drawing size up to width
x height
, which improves speed.
Scroll state variables
We have created three variables for storing the scroll state: scroll
, velocity
and lastScroll
. scroll
contains the scroll position which is usually in the range of 0.0 to 1.0 (sometimes this value might fall out of range by operations such as swiping down).
The velocity
variable holds the scrolling taking the law of inertia into account. When scrolling down this will be a positive value and when scrolling up, it will be a negative value. The factor 0.99
means keeping the speed in the previous frame to 99%. The acceleration is calculated from the difference between scroll
and lastScroll
.
Hope you enjoy this WebGL experiment!
References and Credits
- Official regl website
- Images copyright by RuleByArt. Purchase on Creative Market: Flight and Surface Shapes
Amazing! So creative, but I only liked the first demo.
Sorry,no work in Android mobile device
Awesome!
You should’nt have changed of cigarettes’ brand… Weird and beautiful (but weird mainly).
Good stuff (I’m not talking about your cigarettes).
Can’t believe that, Amazing!
Ridiculously awesome!
so real so live, gorgeous ;))
We wanna more canvas and webgl tutorials, great job!
Ha! My comment was deleted. I guess you guys didn’t feel the same way about Arrival as i did :/
Hi Mark, no comment was deleted! Are you sure it was properly submitted? Yesterday we had a short downtime so it might also be related to that. Thanks for your feedback.
This. Is. Amazing.
So cool. I did (sadly) not understand much about the actual code, but still. This is wonderful. What will the web wizards come up with next.
@Pedro – It was indeed submitted correctly because it was on the site for a spell. Ah well, I guess it’s lost now, like tears in the rain. To give context to my comment above, the missing comment was full of praise for the demos especially number 5 which reminded me of the film Arrival.
I had way too much fun playing with this Demo LOL
10/10
Used it here – http://www.zolablood.com/