Custom Code Experiment •
Conformal Map 3
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 3</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.25, 0.10, 0.30); // purple
vec3 colorB = vec3(1.0, 0.90, 0.40); // gold
vec3 outlineCol = vec3(0.0, 0.0, 0.0); // black
float turtleSDF(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;
float d = 1e9;
d = min(d, length(p * vec2(1.0, 1.25)) - 0.37);
d = min(d, length((p - vec2(0.0, 0.54)) * vec2(1.0, 0.88)) - 0.135);
d = min(d, length((p - vec2(0.0, -0.50)) * vec2(0.65, 1.0)) - 0.085);
d = min(d, length(((p - vec2(-0.35, 0.22)) * vec2(0.55, 1.05))) - 0.082);
d = min(d, length(((p - vec2( 0.35, 0.22)) * vec2(0.55, 1.05))) - 0.082);
d = min(d, length(((p - vec2(-0.30,-0.20)) * vec2(0.55, 1.05))) - 0.082);
d = min(d, length(((p - vec2( 0.30,-0.20)) * vec2(0.55, 1.05))) - 0.082);
return d * s;
}
float eyeSDF(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;
float d1 = length(p - vec2(-0.06, 0.60)) - 0.05;
float d2 = length(p - vec2( 0.06, 0.60)) - 0.05;
return min(d1, d2) * s;
}
void main() {
vec2 uv = vUv;
float t = uTime * 0.15;
vec2 scrollUv = uv + vec2(t * 0.008, t * 0.004);
float cellSize = 0.32; // MUCH larger cells
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 turtleScale = 0.75; // larger turtle inside cell
float rot = checker * PI + t * 0.2;
float tSDF = turtleSDF(cellUv, turtleScale, rot);
float eyeVal = eyeSDF(cellUv, turtleScale, rot);
vec3 turtleCol = mix(colorB, colorA, checker);
float tMask = smoothstep(0.015, -0.015, tSDF);
col = mix(col, turtleCol, tMask);
float outline = smoothstep(0.025, 0.015, tSDF) - smoothstep(0.015, -0.015, tSDF);
col = mix(col, outlineCol, outline * 0.95);
float eMask = smoothstep(0.006, -0.006, eyeVal);
col = mix(col, vec3(1.0), eMask * tMask);
// Pupil
float c2 = cos(-rot), s2 = sin(-rot);
vec2 eyePos = vec2(c2*0.06 - s2*0.60, s2*0.06 + c2*0.60) * turtleScale;
float pupilDist = length(cellUv - eyePos) - 0.025;
if (pupilDist < 0.0 && tMask > 0.5) col = vec3(0.0);
// Thick grid lines
vec2 edgeDist = abs(fract(gridUv) - 0.5);
float lineW = 0.035;
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;
vec2 p = (uv * 2.0 - 1.0) * vec2(uAspect, 1.0);
float r = length(p);
if (r < 0.01) { fragColor = vec4(1.0, 0.9, 0.4, 1.0); return; }
float theta = atan(p.y, p.x);
float logr = log(r);
float b = 0.18;
float period = TAU * b;
float animPhase = uTime * 0.10;
float mappedLogR = logr - b * (theta + animPhase);
mappedLogR = mod(mappedLogR, period * 1.5) - period * 0.1;
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);
// Base pass
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);
// Single Droste pass for minimal recursion
gl.useProgram(progDroste);
gl.uniform1f(locDrosteTime, t);
gl.uniform1f(locDrosteAspect, asp);
gl.uniform1i(locDrosteTex, 0);
gl.activeTexture(gl.TEXTURE0);
// Only 1 feedback iteration
gl.bindFramebuffer(gl.FRAMEBUFFER, fboB.fbo);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.bindTexture(gl.TEXTURE_2D, fboA.tex);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
// Final blit
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, fboB.tex);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
draw();
})();
</script>
</body>
</html>