Grok Logo Animated using a Shader
I adapted one of XorDev's shaders so that it displays in a static HTML page. You can see the final result here.
If you are not following Xor you are missing big.
Code below:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Grok Logo Animated by blipr</title>
<style>
body {
margin: 0;
overflow: hidden;
background: #000;
}
canvas {
width: 100vw;
height: 100vh;
display: block;
}
</style>
</head>
<body>
<canvas id="shader"></canvas>
<script>
const canvas = document.getElementById('shader');
const gl = canvas.getContext('webgl');
if (!gl) {
console.error('WebGL not supported');
alert('Your browser does not support WebGL');
// No return here; just let the script stop naturally
} else {
// Wrap all WebGL-related code in this block
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, `
attribute vec2 position;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
}
`);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
console.error('Vertex Shader error:', gl.getShaderInfoLog(vertexShader));
}
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, `
precision highp float;
uniform vec2 resolution;
uniform float time;
void main() {
vec4 o = vec4(0.0);
vec2 r = resolution;
vec2 FC = gl_FragCoord.xy;
float t = time;
vec2 p = (FC.xy * 2.0 - r) / r.y / 0.7;
vec2 d = vec2(-1.0, 1.0);
vec2 c = p * mat2(1.0, 1.0, d / (0.1 + 5.0 / dot(5.0 * p - d, 5.0 * p - d)));
vec2 v = c;
v *= mat2(cos(log(length(v)) + t * 0.2 + vec4(0.0, 33.0, 11.0, 0.0))) * 5.0;
for(float i = 1.0; i <= 9.0; i += 1.0) {
v += 0.7 * sin(v.yx * i + t) / i + 0.5;
o += sin(vec4(v.x, v.y, v.y, v.x)) + 1.0;
}
o = 1.0 - exp(-exp(c.x * vec4(0.6, -0.4, -1.0, 0.0)) / o /
(0.1 + 0.1 * pow(length(sin(v / 0.3) * 0.2 + c * vec2(1.0, 2.0)) - 1.0, 2.0)) /
(1.0 + 7.0 * exp(0.3 * c.y - dot(c, c))) /
(0.03 + abs(length(p) - 0.7)) * 0.2);
gl_FragColor = clamp(o, 0.0, 1.0);
}
`);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
console.error('Shader error:', gl.getShaderInfoLog(fragmentShader));
}
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error('Program link error:', gl.getProgramInfoLog(program));
}
const vertices = new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]);
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
const positionLocation = gl.getAttribLocation(program, 'position');
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
const timeLocation = gl.getUniformLocation(program, 'time');
const resolutionLocation = gl.getUniformLocation(program, 'resolution');
function resize() {
const dpr = window.devicePixelRatio || 1;
canvas.width = window.innerWidth * dpr;
canvas.height = window.innerHeight * dpr;
canvas.style.width = window.innerWidth + 'px';
canvas.style.height = window.innerHeight + 'px';
gl.viewport(0, 0, canvas.width, canvas.height);
}
window.addEventListener('resize', resize);
resize();
function render(time) {
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(program);
gl.uniform1f(timeLocation, time * 0.001);
gl.uniform2f(resolutionLocation, canvas.width, canvas.height);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
</script>
</body>
</html>