Custom Code Experiment •
Conformal Map 4
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, user-scalable=no">
<title>Conformal Map 4</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { width: 100%; height: 100%; overflow: hidden; background: #000; }
canvas { display: block; width: 100%; height: 100%; }
</style>
</head>
<body>
<canvas id="c"></canvas>
<script>
(function(){
const canvas = document.getElementById('c');
const gl = canvas.getContext('webgl2', { antialias: true });
if (!gl) return;
const VS = `#version 300 es
in vec2 aPos;
out vec2 vUv;
void main() {
vUv = aPos * 0.5 + 0.5;
gl_Position = vec4(aPos, 0.0, 1.0);
}
`;
const FS_BASE = `#version 300 es
precision highp float;
in vec2 vUv;
out vec4 fragColor;
uniform float uTime;
uniform float uAspect;
#define PI 3.14159265359
vec3 colorA = vec3(0.95, 0.45, 0.15);
vec3 colorB = vec3(0.10, 0.08, 0.25);
vec3 sunColor = vec3(1.0, 0.95, 0.6);
vec3 rayColor = vec3(1.0, 0.7, 0.2);
vec3 outlineCol = vec3(0.0, 0.0, 0.0);
float sunriseSDF(vec2 p, float s, float rot) {
float c = cos(-rot), si = sin(-rot);
p = vec2(c*p.x - si*p.y, si*p.x + c*p.y);
p /= s;
vec2 sunCenter = vec2(0.0, -0.25);
float sunDist = length(p - sunCenter) - 0.28;
vec2 delta = p - sunCenter;
float angle = atan(delta.y, delta.x);
float radius = length(delta);
float numRays = 12.0;
float rayAngle = mod(angle + PI/numRays, 2.0*PI/numRays) - PI/numRays;
float rayWidth = 0.15;
float rayDist = abs(rayAngle) - rayWidth;
float rayRadial = radius - 0.35;
float combinedRay = max(rayDist, rayRadial);
float d = min(sunDist, combinedRay);
float horizon = p.y + 0.10;
d = min(d, horizon);
return d * s;
}
void main() {
vec2 uv = vUv;
float skew = 0.35;
uv.x += uv.y * skew;
float t = uTime * 0.35;
vec2 scrollUv = uv + vec2(t * 0.01, t * 0.005);
float cellSize = 0.65;
vec2 gridUv = scrollUv / cellSize;
vec2 gridIdx = floor(gridUv);
vec2 cellUv = fract(gridUv) - 0.5;
float checker = mod(gridIdx.x + gridIdx.y, 2.0);
vec3 bgCol = mix(colorA, colorB, checker);
vec3 col = bgCol;
float shapeScale = 0.85;
float rot = checker * PI + t * 0.5;
float sSDF = sunriseSDF(cellUv, shapeScale, rot);
vec3 shapeCol = mix(colorB, colorA, checker);
float tMask = smoothstep(0.015, -0.015, sSDF);
col = mix(col, shapeCol, tMask);
float c2 = cos(-rot), s2 = sin(-rot);
vec2 sunCenter = vec2(c2*0.0 - s2*(-0.25), s2*0.0 + c2*(-0.25)) * shapeScale;
float sunDist = length(cellUv - sunCenter) - 0.24 * shapeScale;
float sunMask = smoothstep(0.01, -0.01, sunDist);
col = mix(col, sunColor, sunMask * tMask);
vec2 delta = cellUv - sunCenter;
float angle = atan(delta.y, delta.x);
float rayPattern = sin(angle * 12.0 + t * 2.0) * 0.5 + 0.5;
float rayGlow = smoothstep(0.3, 0.1, abs(sin(angle * 12.0))) * (1.0 - smoothstep(0.2, 0.45, length(delta)));
col = mix(col, rayColor, rayGlow * tMask);
float outline = smoothstep(0.025, 0.015, sSDF) - smoothstep(0.015, -0.015, sSDF);
col = mix(col, outlineCol, outline * 0.95);
vec2 edgeDist = abs(fract(gridUv) - 0.5);
float lineW = 0.04;
float lines = 1.0 - smoothstep(lineW - 0.005, lineW, min(edgeDist.x, edgeDist.y));
col = mix(col, outlineCol, lines);
fragColor = vec4(col, 1.0);
}
`;
const FS_DROSTE = `#version 300 es
precision highp float;
in vec2 vUv;
out vec4 fragColor;
uniform sampler2D uTex;
uniform float uTime;
uniform float uAspect;
#define PI 3.14159265359
#define TAU 6.28318530718
void main() {
vec2 uv = vUv;
float skew = 0.35;
uv.x += uv.y * skew;
vec2 p = (uv * 2.0 - 1.0) * vec2(uAspect, 1.0);
float r = length(p);
if (r < 0.01) { fragColor = vec4(1.0, 0.95, 0.6, 1.0); return; }
float theta = atan(p.y, p.x);
float logr = log(r);
float b = 0.22;
float period = TAU * b;
float animPhase = uTime * 0.15;
float mappedLogR = logr - b * (theta + animPhase);
mappedLogR = mod(mappedLogR, period * 1.6) - period * 0.12;
float newR = exp(mappedLogR);
float newTheta = theta + (logr - mappedLogR) / b;
vec2 newP = newR * vec2(cos(newTheta), sin(newTheta));
vec2 newUv = (newP / vec2(uAspect, 1.0)) * 0.5 + 0.5;
fragColor = texture(uTex, fract(newUv));
}
`;
const FS_COPY = `#version 300 es
precision highp float;
in vec2 vUv;
out vec4 fragColor;
uniform sampler2D uTex;
void main() { fragColor = texture(uTex, vUv); }
`;
function compileShader(type, src) {
const s = gl.createShader(type);
gl.shaderSource(s, src);
gl.compileShader(s);
return s;
}
function createProgram(vs, fs) {
const p = gl.createProgram();
gl.attachShader(p, compileShader(gl.VERTEX_SHADER, vs));
gl.attachShader(p, compileShader(gl.FRAGMENT_SHADER, fs));
gl.linkProgram(p);
return p;
}
const progBase = createProgram(VS, FS_BASE);
const progDroste = createProgram(VS, FS_DROSTE);
const progCopy = createProgram(VS, FS_COPY);
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
const vbo = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,-1, 1,-1, -1,1, 1,1]), gl.STATIC_DRAW);
const aPos = gl.getAttribLocation(progBase, 'aPos');
gl.enableVertexAttribArray(aPos);
gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0);
let fboA, fboB;
function createFBO(w, h) {
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, w, h, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
const fbo = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
return { fbo, tex };
}
function resize() {
const w = window.innerWidth, h = window.innerHeight;
canvas.width = w; canvas.height = h;
if (fboA) { gl.deleteFramebuffer(fboA.fbo); gl.deleteTexture(fboA.tex); }
if (fboB) { gl.deleteFramebuffer(fboB.fbo); gl.deleteTexture(fboB.tex); }
fboA = createFBO(w, h);
fboB = createFBO(w, h);
}
window.addEventListener('resize', resize);
resize();
const locBaseTime = gl.getUniformLocation(progBase, 'uTime');
const locBaseAspect = gl.getUniformLocation(progBase, 'uAspect');
const locDrosteTime = gl.getUniformLocation(progDroste, 'uTime');
const locDrosteAspect = gl.getUniformLocation(progDroste, 'uAspect');
const locDrosteTex = gl.getUniformLocation(progDroste, 'uTex');
const locCopyTex = gl.getUniformLocation(progCopy, 'uTex');
const startTime = performance.now();
function draw() {
requestAnimationFrame(draw);
const t = (performance.now() - startTime) * 0.001;
const W = canvas.width, H = canvas.height;
const asp = W / H;
gl.bindVertexArray(vao);
gl.viewport(0, 0, W, H);
gl.bindFramebuffer(gl.FRAMEBUFFER, fboA.fbo);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(progBase);
gl.uniform1f(locBaseTime, t);
gl.uniform1f(locBaseAspect, asp);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
gl.useProgram(progDroste);
gl.uniform1f(locDrosteTime, t);
gl.uniform1f(locDrosteAspect, asp);
gl.uniform1i(locDrosteTex, 0);
gl.activeTexture(gl.TEXTURE0);
let src = fboA, dst = fboB;
for (let i = 0; i < 2; i++) {
gl.bindFramebuffer(gl.FRAMEBUFFER, dst.fbo);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.bindTexture(gl.TEXTURE_2D, src.tex);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
[src, dst] = [dst, src];
}
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(progCopy);
gl.uniform1i(locCopyTex, 0);
gl.bindTexture(gl.TEXTURE_2D, src.tex);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
draw();
})();
</script>
</body>
</html>