Custom Code Experiment •
WebGL2 Hex Tiles 1
Source Code View HTML / CSS / JavaScript
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebGL2 Hex Warp Tiles</title>
<style>
html { height: 100%; }
body {
background: #000;
overflow: hidden;
padding: 0;
margin: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
}
canvas { height: 100%; width: 100%; }
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script id="vertexShader" type="x-shader/x-vertex">#version 300 es
precision highp float;
in vec4 position;
void main() {
gl_Position = vec4(position);
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">#version 300 es
precision highp float;
out vec4 fragColor;
uniform vec2 u_resolution;
uniform float u_time;
#define R u_resolution
#define T u_time
#define PI 3.141592653
#define PI2 6.283185307
#define N 7.0
mat2 rot(float a) { return mat2(cos(a), sin(a), -sin(a), cos(a)); }
float hash21(vec2 a) {
a.y = mod(a.y, N);
return fract(sin(dot(a, vec2(27.609, 57.583))) * 43758.5453);
}
float hash11(float a) {
return fract(sin(a * 127.1) * 43758.5453);
}
float hexSDF(vec2 p, float r) {
const vec2 k = vec2(-0.866025404, 0.5);
p = abs(p);
p -= 2.0 * min(dot(k, p), 0.0) * k;
p -= vec2(clamp(p.x, -k.y * r, k.y * r), r);
return length(p) * sign(p.y);
}
vec4 hexGrid(vec2 uv) {
vec2 s = vec2(1.0, sqrt(3.0));
vec2 p = uv / s;
vec2 a = mod(p, 1.0) - 0.5;
vec2 b = mod(p - 0.5, 1.0) - 0.5;
vec2 gv = (dot(a,a) < dot(b,b)) ? a : b;
vec2 id = uv - gv * s;
return vec4(gv * s, id);
}
vec3 hsv(vec3 c) {
vec3 rgb = clamp(abs(mod(c.x * 6.0 + vec3(0,4,2), 6.0) - 3.0) - 1.0, 0.0, 1.0);
return c.z * mix(vec3(1), rgb, c.y);
}
// Crisp step — one pixel wide AA using screen-space derivative
float edge(float d) {
float w = fwidth(d);
return smoothstep(w, 0.0, d);
}
void main() {
vec2 F = gl_FragCoord.xy;
vec2 uv = (2.0 * F - R) / max(R.x, R.y);
uv *= rot(-T * 0.07);
uv = -vec2(log(length(uv)), atan(uv.y, uv.x));
uv /= PI;
uv *= N;
uv.x += T * 0.35;
vec3 C = vec3(0.0);
vec4 hx = hexGrid(uv);
vec2 localP = hx.xy;
vec2 cellID = hx.zw;
float h1 = hash21(cellID);
float h2 = hash11(h1 * 3791.0 + floor(T * 0.3));
float h3 = fract(h1 * 4785.312);
// --- Outer hex ---
float hexR = 0.90;
float d = hexSDF(localP, hexR);
float hue = fract(h1 * 0.7 + T * 0.018);
vec3 baseCol = hsv(vec3(hue, 1.0, 1.0));
vec3 darkCol = baseCol * 0.2;
vec3 cellCol = mix(baseCol, darkCol, smoothstep(-0.2, 0.6, localP.x));
C = mix(C, cellCol, edge(d));
// --- Inner hex (hard edge) ---
float innerR = hexR * 0.58 + 0.08 * sin(h1 * PI2 + T * h1 * 1.5);
float di = hexSDF(localP, innerR);
vec3 glowCol = hsv(vec3(fract(h3 * 0.6 + T * 0.025), 1.0, 1.0));
C = mix(C, glowCol, edge(di));
// --- Spinning accent hex (hard edge) ---
float angle = T * (0.4 + h2 * 0.6) * (h1 > 0.5 ? 1.0 : -1.0);
vec2 rp = rot(angle) * localP;
float da = hexSDF(rp, innerR * 0.42);
vec3 accentCol = hsv(vec3(fract(hue + 0.33), 1.0, 1.0));
C = mix(C, accentCol, edge(da));
// --- Hard border gap between cells ---
float borderW = 0.055;
float borderMask = edge(borderW - d); // 1 inside border zone
C = mix(C, vec3(0.0), borderMask);
// --- Thin bright border line ---
float lineMask = edge(abs(d + borderW * 0.3) - borderW * 0.15);
C = mix(C, baseCol * 1.2, lineMask);
// Gamma
C = pow(clamp(C, 0.0, 1.0), vec3(0.4545));
fragColor = vec4(C, 1.0);
}
</script>
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
<script>
const glcanvas = document.getElementById("canvas");
const gl = glcanvas.getContext("webgl2");
if (!gl) alert("WebGL2 not supported!");
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);
const render = (time) => {
twgl.resizeCanvasToDisplaySize(gl.canvas, 1.0);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
const uniforms = {
u_time: time * 0.001,
u_resolution: [gl.canvas.width, gl.canvas.height]
};
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo, uniforms);
twgl.drawBufferInfo(gl, bufferInfo);
requestAnimationFrame(render);
};
window.addEventListener("DOMContentLoaded", () => {
requestAnimationFrame(render);
});
</script>
</body>
</html>