<!-- Quick start shader template: https://codepen.io/pen/?template=ZEyrWNe -->
<div id="main">
<h1>Wavey Footer</h1>
<p>A GLSL shader inspired by <a href="https://twitter.com/marioecg/status/1455744243423645696">Mario Carrillo</a>.</p>
</div>
<canvas id="canvas"></canvas>
<footer>Made by <a href="https://twitter.com/jhancock532">@jhancock532</a></footer>
<script id="vertexShader" type="x-shader/x-vertex">
attribute vec4 position;
void main() {
gl_Position = vec4( position );
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
uniform vec2 u_resolution;
uniform float u_time;
uniform vec2 u_mouse;
//Taken from Book of Shaders
//https://thebookofshaders.com/edit.php#11/2d-gnoise.frag
vec2 random2(vec2 st){
st = vec2( dot(st,vec2(127.1,311.7)),
dot(st,vec2(269.5,183.3)) );
return -1.0 + 2.0*fract(sin(st)*43758.5453123);
}
// Gradient Noise by Inigo Quilez - iq/2013
// https://www.shadertoy.com/view/XdXGW8
float noise(vec2 st) {
vec2 i = floor(st);
vec2 f = fract(st);
vec2 u = f*f*(3.0-2.0*f);
return mix( mix( dot( random2(i + vec2(0.0,0.0) ), f - vec2(0.0,0.0) ),
dot( random2(i + vec2(1.0,0.0) ), f - vec2(1.0,0.0) ), u.x),
mix( dot( random2(i + vec2(0.0,1.0) ), f - vec2(0.0,1.0) ),
dot( random2(i + vec2(1.0,1.0) ), f - vec2(1.0,1.0) ), u.x), u.y);
}
void main() {
vec2 uv = gl_FragCoord.xy / u_resolution;
float waveOffset = 0.3;
float waveCenter = 1.;
float waveFocus = 0.25;
float waveSpeed = 2.; //Lower faster.
float wMin = waveCenter - waveFocus;
float wMax = waveCenter + waveFocus;
//Smoothly extend on the x axis without stretching the noise
uv.x *= u_resolution.x/u_resolution.y;
uv.x += 1787.74328; //random starting seed
uv.y *= 2.;
float rn = noise( vec2(uv.x, u_time / waveSpeed));
float ry = uv.y - rn;
float r = smoothstep(wMin, wMax, ry);
float bn = noise( vec2(uv.x, u_time / waveSpeed - waveOffset));
float by = uv.y - bn;
float b = smoothstep(wMin, wMax, by);
float gn = noise( vec2(uv.x, u_time / waveSpeed + waveOffset));
float gy = uv.y - gn;
float g = smoothstep(wMin, wMax, gy);
//float a = (r + g + b) / 3.0;
gl_FragColor = vec4(r, b, g, 1.0);
}
</script>
body {
margin: 0;
font-family: consolas;
}
footer {
position: absolute;
font-size: 22px;
bottom: 0;
left: 0;
transform: translate(calc(50vw - 50%), -50%);
color: white;
}
footer a {
color: white;
}
#main {
margin: 80px 80px 0 80px;
}
@media only screen and (max-width: 600px) {
#main {
margin: 20px 20px 0 20px;
}
}
p {
font-size: 22px;
}
label {
font-size: 22px;
font-weight: bold;
display: block;
}
input {
display: block;
margin-bottom: 10px;
width: 100%;
max-width: 300px;
}
canvas {
position: absolute;
bottom: 0;
pointer-events: none;
width: 100%;
height: 50vh;
}
// Following the twgl tiny tutorial, https://twgljs.org/
// Code adapted from https://github.com/greggman/twgl.js/blob/master/examples/tiny.html
const gl = document.getElementById("canvas").getContext("webgl");
const programInfo = twgl.createProgramInfo(gl, ["vertexShader", "fragmentShader"]);
const arrays = {
position: [-1, -1, 0, 1, -1, 0, -1, 1, 0, -1, 1, 0, 1, -1, 0, 1, 1, 0],
};
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
let mouseX = 0, mouseY = 0;
document.getElementById("canvas").addEventListener('mousemove', e => {
mouseX = e.clientX;
mouseY = e.clientY;
});
function render(time) {
//Paints only 25% pixels in the screen space,
//Slightly lower resolution but much better performance!
//See also: the zoom feature of https://glslsandbox.com/
twgl.resizeCanvasToDisplaySize(gl.canvas, 0.5);
//Paints 4x the number of pixels.
//Very computationally expensive in full screen on desktop devices
//twgl.resizeCanvasToDisplaySize(gl.canvas, 1.0);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
const uniforms = {
u_time: (time) * 0.002,
u_resolution: [gl.canvas.width, gl.canvas.height],
u_mouse: [mouseX, mouseY],
};
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo, uniforms);
twgl.drawBufferInfo(gl, bufferInfo);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
This Pen doesn't use any external CSS resources.