blog

WebGL Scroll Spiral

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.

Attention: You’ll need to have WebGL support in your browser in order to see the effect.

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); }
	}
});

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

WebGL Scroll Spiral was written by Xoihazard and published on Codrops.

LEAVE A REPLY