Custom Code Experiment •
Conformal Map 7
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 7</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
user-select: none;
}
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, depth: false });
if (!gl) {
console.warn("WebGL2 not supported");
return;
}
// ---------- VERTEX SHADER ----------
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);
}
`;
// ======================= BASE SHADER: MULTI-GEOMETRY COMPLEX =======================
const FS_BASE = `#version 300 es
precision highp float;
in vec2 vUv;
out vec4 fragColor;
uniform float uTime;
uniform float uAspect;
#define PI 3.141592653589793
#define TAU 6.283185307179586
#define PHI 1.618033988749895
#define SQRT2 1.414213562373095
const vec3 deepCosmos = vec3(0.04, 0.02, 0.18);
const vec3 midnight = vec3(0.08, 0.09, 0.28);
const vec3 tealOcean = vec3(0.05, 0.42, 0.48);
const vec3 emerald = vec3(0.12, 0.58, 0.42);
const vec3 coralFlame = vec3(0.96, 0.38, 0.28);
const vec3 goldenAmber = vec3(1.0, 0.78, 0.22);
const vec3 softLavender = vec3(0.68, 0.55, 0.82);
const vec3 hotMagenta = vec3(0.92, 0.18, 0.55);
const vec3 iceWhite = vec3(0.94, 0.92, 0.98);
const vec3 deepShadow = vec3(0.02, 0.01, 0.08);
float hash1(float n) {
return fract(sin(n) * 43758.5453123);
}
float hash2(vec2 p) {
float h = dot(p, vec2(127.1, 311.7));
return fract(sin(h) * 43758.5453123);
}
float hash3(vec3 p) {
float h = dot(p, vec3(127.1, 311.7, 74.7));
return fract(sin(h) * 43758.5453123);
}
float smin(float a, float b, float k) {
float h = max(k - abs(a - b), 0.0) / k;
return min(a, b) - h * h * k * 0.25;
}
float smax(float a, float b, float k) {
return -smin(-a, -b, k);
}
float distRoseSpiral(vec2 p, float seed, float t) {
float r = length(p);
float theta = atan(p.y, p.x);
float petalCount = 8.0 + floor(hash1(seed * 3.7) * 6.0);
float petalSharpness = 1.2 + hash1(seed * 7.1) * 1.8;
float amplitude = 0.28 + hash1(seed * 13.3) * 0.22;
float spiralPitch = 0.06 + hash1(seed * 19.7) * 0.1;
float ruffleFreq = 18.0 + hash1(seed * 23.1) * 24.0;
float ruffleAmp = 0.015 + hash1(seed * 29.3) * 0.03;
float petalRuffles = ruffleAmp * sin(theta * ruffleFreq + t * 2.1) * cos(r * 35.0 - t);
float roseArg = petalCount * theta + t * 0.7 * (1.0 + hash1(seed * 31.7));
float roseRadius = amplitude * abs(cos(roseArg * 0.5));
roseRadius = pow(roseRadius, petalSharpness);
roseRadius = 0.3 + roseRadius * 0.35 + petalRuffles;
float spiralArg = theta - log(r + 0.08) * spiralPitch * 12.0 + t * 1.3;
float spiralMod = 0.04 * sin(spiralArg * 3.0) * smoothstep(0.1, 0.7, r);
float dynamicRadius = roseRadius + spiralMod;
float dRose = abs(r - dynamicRadius) - 0.018;
float innerR = 0.14 + 0.04 * sin(theta * 8.0 + seed * TAU) * cos(r * 22.0);
float dInner = abs(r - innerR) - 0.01;
float wispR = 0.55 + 0.06 * sin(theta * 5.0 - log(r + 0.1) * 8.0 + t);
float dWisp = abs(r - wispR) - 0.012;
dWisp += 0.03 * smoothstep(0.5, 0.7, r);
float d = smin(dRose, dInner, 0.03);
d = smin(d, dWisp, 0.04);
return d;
}
float distStellarComplex(vec2 p, float seed, float t) {
float r = length(p);
float theta = atan(p.y, p.x);
float points = 5.0 + floor(hash1(seed * 41.1) * 8.0);
float starDepth = 0.35 + hash1(seed * 47.3) * 0.4;
float twist = 0.3 + hash1(seed * 53.7) * 1.2;
float layers = 2.0 + floor(hash1(seed * 59.1) * 3.0);
float d = 1e6;
for (float l = 0.0; l < 3.0; l += 1.0) {
if (l >= layers) break;
float layerOffset = l * 0.15;
float layerR = 0.22 + layerOffset;
float layerTwist = twist + l * 0.4;
float layerAmp = starDepth * (1.0 - l * 0.25);
float angleContrib = theta + layerTwist * sin(r * 6.0 + t * 0.8 + l);
float starShape = cos(angleContrib * points * 0.5);
starShape = abs(starShape);
starShape = pow(starShape, 0.6 + l * 0.3);
float starRadius = layerR + layerAmp * starShape * 0.3;
float dLayer = abs(r - starRadius) - 0.014 + l * 0.005;
d = smin(d, dLayer, 0.04);
}
float armAngle = theta * points / TAU;
float armFrac = fract(armAngle + t * 0.15);
float armRadial = 0.15 + armFrac * 0.55;
float dArm = abs(r - armRadial) - 0.02;
dArm *= 1.0 + 0.5 * abs(sin(theta * points + t));
d = smin(d, dArm, 0.03);
return d;
}
float distEpitrochoid(vec2 p, float seed, float t) {
float r = length(p);
float theta = atan(p.y, p.x);
float R1 = 0.35 + hash1(seed * 67.3) * 0.25;
float r1 = 0.12 + hash1(seed * 71.9) * 0.18;
float d1 = 0.2 + hash1(seed * 73.4) * 0.3;
float speed = 0.4 + hash1(seed * 79.1) * 1.2;
float d = 1e6;
for (float i = 0.0; i < 8.0; i += 1.0) {
float phi = TAU * i / 8.0 + t * speed * 0.3;
float cx = (R1 + r1) * cos(phi) - d1 * cos((R1 + r1) / r1 * phi);
float cy = (R1 + r1) * sin(phi) - d1 * sin((R1 + r1) / r1 * phi);
float distPoint = length(p - vec2(cx, cy));
d = smin(d, distPoint - 0.025, 0.06);
}
float curveArg = theta * 3.0 + sin(r * 12.0 - t * speed) * 2.0;
float curveR = R1 + d1 * cos(curveArg) * 0.7;
float dCurve = abs(r - curveR) - 0.016;
d = smin(d, dCurve, 0.04);
return d;
}
float distOrganicBlobs(vec2 p, float seed, float t) {
float r = length(p);
float theta = atan(p.y, p.x);
float numBlobs = 3.0 + floor(hash1(seed * 83.7) * 5.0);
float blobSoftness = 0.35 + hash1(seed * 89.1) * 0.5;
float d = 1e6;
for (float b = 0.0; b < 7.0; b += 1.0) {
if (b >= numBlobs) break;
float angle = TAU * b / numBlobs + hash1(seed * 97.3 + b) * 0.6;
float dist = 0.25 + hash1(seed * 101.9 + b) * 0.3;
float blobR = 0.08 + hash1(seed * 103.7 + b) * 0.12;
float wobble = sin(t * 1.4 + b * 2.7) * 0.06;
float bx = dist * cos(angle + wobble);
float by = dist * sin(angle + wobble);
float blobDist = length(p - vec2(bx, by)) - blobR;
float tendrilAngle = atan(by, bx);
float angleDiff = abs(mod(theta - tendrilAngle + PI, TAU) - PI);
float tendril = angleDiff * r - 0.03;
blobDist = smin(blobDist, tendril, 0.05);
d = smin(d, blobDist, blobSoftness);
}
return d;
}
float shapeDistance(vec2 p, float cellSeed, float timeShift) {
float r = length(p);
float theta = atan(p.y, p.x); // FIX: added missing declaration
float shapeType = hash1(cellSeed * 107.3);
float d = 1e6;
float dRose = distRoseSpiral(p, cellSeed, timeShift);
d = smin(d, dRose, 0.05);
if (shapeType < 0.33) {
float dStar = distStellarComplex(p, cellSeed + 0.1, timeShift);
d = smin(d, dStar, 0.06);
} else if (shapeType < 0.66) {
float dEpi = distEpitrochoid(p, cellSeed + 0.2, timeShift);
d = smin(d, dEpi, 0.06);
} else {
float dBlob = distOrganicBlobs(p, cellSeed + 0.3, timeShift);
d = smin(d, dBlob, 0.06);
}
float rayCount = 11.0 + floor(hash1(cellSeed * 113.7) * 10.0);
float rayPattern = sin(theta * rayCount + timeShift * 2.5) * cos(theta * 5.0 - timeShift);
float rayWidth = 0.05 * (1.0 - smoothstep(0.1, 0.65, r));
float dRay = abs(rayPattern) * rayWidth - 0.004;
d = smin(d, dRay, 0.02);
float ringCount = 6.0 + floor(hash1(cellSeed * 127.1) * 8.0);
float ringPattern = sin(r * ringCount * 8.0 - timeShift * 1.7);
float dRing = abs(ringPattern) * 0.03 - 0.006;
dRing *= smoothstep(0.15, 0.6, r);
d = smin(d, dRing, 0.025);
float boundary = r - 0.72;
d = min(d, boundary);
return d;
}
void main() {
vec2 uv = vUv;
float skew = 0.38 + 0.08 * sin(uTime * 0.31);
uv.x += uv.y * skew;
float t = uTime * 0.28;
float cellSize = 0.68;
vec2 scrollUv = uv + vec2(t * 0.015, t * 0.009);
vec2 gridUv = scrollUv / cellSize;
vec2 gridIdx = floor(gridUv);
vec2 cellUv = fract(gridUv) - 0.5;
float cellSeed = fract(sin(gridIdx.x * 127.1 + gridIdx.y * 311.7) * 43758.5453);
float rotSeed = fract(sin(gridIdx.y * 219.3 + gridIdx.x * 173.9) * 271.8);
float angleRot = (rotSeed * TAU + t * 0.55) + sin(gridIdx.x * 0.61) * 0.8;
float cRot = cos(angleRot);
float sRot = sin(angleRot);
vec2 rotatedUv = vec2(
cellUv.x * cRot - cellUv.y * sRot,
cellUv.x * sRot + cellUv.y * cRot
);
float morphScale = 0.85 + 0.3 * sin(cellSeed * TAU * 2.3 + t);
vec2 finalP = rotatedUv / morphScale;
float shapeDist = shapeDistance(finalP, cellSeed, t + cellSeed * 6.28);
float mask = smoothstep(0.026, -0.014, shapeDist);
float checker = mod(gridIdx.x + gridIdx.y, 2.0);
float bgShift = 0.15 * sin(gridIdx.x * 0.77 + gridIdx.y * 0.59 + t * 0.4);
vec3 bgCol = mix(deepCosmos, midnight, checker + bgShift);
bgCol = mix(bgCol, tealOcean, 0.25 + 0.2 * sin(gridIdx.x * 0.53 + gridIdx.y * 0.71 + t * 0.35));
float rLocal = length(finalP);
float thetaLocal = atan(finalP.y, finalP.x);
float colorPhase = thetaLocal / TAU + rLocal * 0.8 + cellSeed;
vec3 gradA = mix(coralFlame, goldenAmber, smoothstep(0.0, 0.4, fract(colorPhase + t * 0.13)));
vec3 gradB = mix(tealOcean, emerald, smoothstep(0.3, 0.7, fract(colorPhase - t * 0.09)));
vec3 gradC = mix(softLavender, hotMagenta, smoothstep(0.5, 0.9, fract(colorPhase + t * 0.17)));
vec3 shapeColor = mix(gradA, gradB, smoothstep(0.2, 0.5, fract(colorPhase)));
shapeColor = mix(shapeColor, gradC, smoothstep(0.5, 0.8, fract(colorPhase + 0.3)));
shapeColor = mix(shapeColor, iceWhite, 0.35 * exp(-rLocal * 6.0));
shapeColor = mix(shapeColor, goldenAmber, 0.25 * exp(-rLocal * 3.5));
vec3 col = mix(bgCol, shapeColor, mask);
float coreR = rLocal - 0.1;
float coreMask = smoothstep(0.02, -0.012, coreR);
vec3 coreCol = mix(goldenAmber, iceWhite, 0.6);
col = mix(col, coreCol, coreMask * mask * 0.85);
float rayHarmonic = sin(thetaLocal * 14.0 + t * 3.0) * cos(thetaLocal * 7.0 - t * 2.0);
float rayIntensity = smoothstep(0.18, 0.6, rLocal) * (0.7 + 0.5 * rayHarmonic);
rayIntensity *= (1.0 - smoothstep(0.0, 0.3, coreR)) * 0.75;
rayIntensity = clamp(rayIntensity, 0.0, 0.6);
col = mix(col, goldenAmber, rayIntensity * mask);
float edgeGlow = smoothstep(0.03, 0.012, shapeDist) - smoothstep(0.012, -0.014, shapeDist);
edgeGlow += 0.3 * abs(sin(thetaLocal * 28.0 - t * 8.0)) * (1.0 - mask);
col = mix(col, deepShadow, edgeGlow * 0.8);
col = mix(col, hotMagenta, edgeGlow * 0.35 * abs(sin(thetaLocal * 15.0 + t)));
vec2 edgeDist = abs(fract(gridUv) - 0.5);
float lineWidth = 0.04 + 0.014 * sin(gridIdx.x * 2.7 + gridIdx.y * 2.1 + t);
float lines = 1.0 - smoothstep(lineWidth - 0.005, lineWidth, min(edgeDist.x, edgeDist.y));
lines *= 0.7;
vec3 lineColor = mix(goldenAmber, softLavender, 0.5 + 0.5 * sin(gridIdx.x * 0.9 + gridIdx.y * 0.7 + t));
col = mix(col, lineColor, lines * 0.55);
float chromaRipple = sin(uv.y * 42.0 + t * 5.8) * 0.22 + cos(uv.x * 36.0 - t * 4.5) * 0.18;
col += vec3(0.07, 0.02, 0.1) * chromaRipple * (1.0 - mask) * 0.45;
col += vec3(0.03, 0.06, 0.02) * chromaRipple * mask * 0.3;
float vignette = 1.0 - smoothstep(0.4, 1.2, length(uv - 0.5) * 1.6);
col = mix(deepShadow, col, vignette);
fragColor = vec4(col, 1.0);
}
`;
// =========== RECURSIVE CONFORMAL MAP: KALEIDOSCOPIC DROSTE + MULTI-SPIRAL ===========
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.141592653589793
#define TAU 6.283185307179586
#define PHI 1.618033988749895
float sminK(float a, float b, float k) {
float h = max(k - abs(a - b), 0.0) / k;
return min(a, b) - h * h * k * 0.25;
}
void main() {
vec2 uv = vUv;
vec2 p = (uv * 2.0 - 1.0) * vec2(uAspect, 1.0);
float r = length(p);
if (r < 0.006) {
fragColor = vec4(1.0, 0.85, 0.5, 1.0);
return;
}
float theta = atan(p.y, p.x);
float symFold = 7.0;
float symAngle = TAU / symFold;
float foldedTheta = mod(theta + symAngle * 0.5, symAngle) - symAngle * 0.5;
float mirrorSign = sign(foldedTheta);
foldedTheta = abs(foldedTheta);
float logr = log(r + 0.001);
float spiralPitch = 0.32 + 0.07 * sin(uTime * 0.55);
float branchCount = 5.0;
float spiralTwist = spiralPitch * foldedTheta;
float branchSelector = floor((theta + PI) / TAU * branchCount);
float branchPhase = branchSelector * TAU / branchCount;
float period = 0.68 * (1.0 + 0.08 * sin(uTime * 0.25));
float animPhase = uTime * 0.19;
float mappedLogR = logr - spiralTwist + 0.08 * cos(foldedTheta * 6.0 - uTime * 1.1);
mappedLogR = mod(mappedLogR + animPhase + branchPhase * 0.15, period) - period * 0.48;
float newR = exp(mappedLogR + spiralTwist - 0.08 * cos(foldedTheta * 6.0 - uTime * 1.1));
newR = clamp(newR, 0.002, 1.8);
float newTheta = foldedTheta * mirrorSign;
newTheta += 0.14 * sin(5.0 * mappedLogR + uTime * 1.0) * cos(foldedTheta * 3.5);
newTheta += branchPhase * 0.08;
newTheta = mod(newTheta + PI, TAU) - PI;
vec2 newP = newR * vec2(cos(newTheta), sin(newTheta));
vec2 newUv = (newP / vec2(uAspect, 1.0)) * 0.5 + 0.5;
newUv = fract(newUv);
vec4 sampled = texture(uTex, newUv);
float depthIndicator = smoothstep(0.05, 0.7, r);
float recursionDepth = floor(abs(logr - mappedLogR) / period + 0.5);
float depthTint = fract(recursionDepth * 0.15 + uTime * 0.08);
vec3 warmShift = vec3(0.08, 0.03, -0.04) * depthTint;
sampled.rgb += warmShift;
float depthFade = clamp(1.3 - r * 0.65, 0.4, 1.0);
sampled.rgb *= depthFade;
float recursionEdge = abs(fract((logr - spiralTwist) / period + animPhase) - 0.48) * 20.0;
float edgeFade = smoothstep(0.2, 0.6, recursionEdge);
sampled.rgb *= 0.75 + 0.25 * edgeFade;
fragColor = sampled;
}
`;
// simple copy shader for final output
const FS_COPY = `#version 300 es
precision highp float;
in vec2 vUv;
out vec4 fragColor;
uniform sampler2D uTex;
void main() {
vec4 col = texture(uTex, vUv);
col.rgb = (col.rgb - 0.5) * 1.06 + 0.5;
col.rgb = clamp(col.rgb, 0.0, 1.0);
fragColor = col;
}
`;
function compileShader(type, src) {
const s = gl.createShader(type);
gl.shaderSource(s, src);
gl.compileShader(s);
if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) {
console.error(gl.getShaderInfoLog(s));
gl.deleteShader(s);
return null;
}
return s;
}
function createProgram(vsSrc, fsSrc) {
const vs = compileShader(gl.VERTEX_SHADER, vsSrc);
const fs = compileShader(gl.FRAGMENT_SHADER, fsSrc);
if (!vs || !fs) return null;
const prog = gl.createProgram();
gl.attachShader(prog, vs);
gl.attachShader(prog, fs);
gl.linkProgram(prog);
if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
console.error(gl.getProgramInfoLog(prog));
return null;
}
gl.deleteShader(vs);
gl.deleteShader(fs);
return prog;
}
const progBase = createProgram(VS, FS_BASE);
const progDroste = createProgram(VS, FS_DROSTE);
const progCopy = createProgram(VS, FS_COPY);
if (!progBase || !progDroste || !progCopy) throw new Error("Shader compilation failed");
// geometry setup
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);
const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
if (status !== gl.FRAMEBUFFER_COMPLETE) console.warn("FBO incomplete", status);
return { fbo, tex };
}
let screenW, screenH;
function resize() {
screenW = window.innerWidth;
screenH = window.innerHeight;
canvas.width = screenW;
canvas.height = screenH;
if (fboA) {
gl.deleteFramebuffer(fboA.fbo);
gl.deleteTexture(fboA.tex);
gl.deleteFramebuffer(fboB.fbo);
gl.deleteTexture(fboB.tex);
}
fboA = createFBO(screenW, screenH);
fboB = createFBO(screenW, screenH);
}
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 aspect = screenW / screenH;
gl.bindVertexArray(vao);
gl.viewport(0, 0, screenW, screenH);
// 1. Render base fractal tile into FBO A
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, aspect);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
// 2. Recursive conformal mapping (kaleidoscopic Droste)
gl.useProgram(progDroste);
gl.uniform1f(locDrosteTime, t);
gl.uniform1f(locDrosteAspect, aspect);
gl.uniform1i(locDrosteTex, 0);
gl.activeTexture(gl.TEXTURE0);
let src = fboA,
dst = fboB;
for (let iter = 0; iter < 3; iter++) {
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);
let temp = src;
src = dst;
dst = temp;
}
// 3. Final present to screen
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>