Custom Code Experiment •
Conformal Map 5
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 6</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: HYPERBOLIC ROSE + PHYLLOTACTIC FLORAL =======================
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
// luminous palette
vec3 palA = vec3(0.98, 0.45, 0.28); // coral dawn
vec3 palB = vec3(0.15, 0.18, 0.55); // deep indigo
vec3 coreGlow = vec3(1.0, 0.85, 0.35); // golden core
vec3 rayTint = vec3(1.0, 0.68, 0.22); // amber rays
vec3 darkOutline = vec3(0.04, 0.02, 0.12);
// signed distance-like field: generalized rose + radial resonance
float floralDistance(vec2 p, float cellSeed, float timeShift) {
float r = length(p);
float theta = atan(p.y, p.x);
// dynamic petal count based on cell seed
float petalCount = 7.0 + floor(mod(cellSeed * 17.0, 5.0)); // 7..11 petals
float petalAmp = 0.13 + 0.07 * sin(cellSeed * TAU + timeShift);
float roseRadius = 0.32 + petalAmp * cos(petalCount * theta + timeShift * 1.2);
// hyperbolic spiral twist (inverse radius modulation)
float spiralTwist = 0.05 * sin(6.0 * theta - timeShift * 2.3) / (r + 0.12);
float dynamicRadius = roseRadius + spiralTwist;
// distance to rose contour
float dRose = abs(r - dynamicRadius) - 0.022;
// inner mandala: sinc-inspired radial harmonics
float innerRim = 0.19 + 0.03 * sin(theta * 14.0 + timeShift) * cos(r * 28.0);
float dInner = abs(r - innerRim) - 0.012;
// outer Fibonacci spirals
float fibRing = 0.52 + 0.045 * sin(theta * 9.0 - timeShift * 1.6) * cos(r * 42.0 - timeShift);
float dFib = abs(r - fibRing) - 0.018;
float d = min(dRose, dInner);
d = min(d, dFib);
// ray tendrils: angular interference
float rayPattern = sin(petalCount * theta * 1.4 + timeShift) * cos(theta * 4.0 - timeShift);
float radialWidth = 0.07 * (1.0 - smoothstep(0.18, 0.68, r));
float rayDist = abs(rayPattern) * radialWidth - 0.006;
d = min(d, rayDist);
// outer boundary clipping
float horizon = r - 0.75;
d = min(d, horizon);
return d;
}
void main() {
vec2 uv = vUv;
// non-linear shear (time-variant)
float skew = 0.42 + 0.05 * sin(uTime * 0.27);
uv.x += uv.y * skew;
float t = uTime * 0.31;
// tiling with organic scroll
float cellSize = 0.72;
vec2 scrollUv = uv + vec2(t * 0.012, t * 0.008);
vec2 gridUv = scrollUv / cellSize;
vec2 gridIdx = floor(gridUv);
vec2 cellUv = fract(gridUv) - 0.5;
// per-cell pseudo-random seed for morphological variety
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);
// dynamic background: checkerboard with chromatic drift
float checker = mod(gridIdx.x + gridIdx.y, 2.0);
vec3 bgCol = mix(palA, palB, checker + 0.12 * sin(gridIdx.x * 0.83 + gridIdx.y * 0.64));
// per-cell rotation influenced by hyperbolic sine
float angleRot = (rotSeed * TAU + t * 0.48) + sin(gridIdx.x * 0.53) * 0.7;
float cRot = cos(angleRot);
float sRot = sin(angleRot);
vec2 rotatedUv = vec2(
cellUv.x * cRot - cellUv.y * sRot,
cellUv.x * sRot + cellUv.y * cRot
);
// scale morphing per tile
float morphScale = 0.88 + 0.28 * sin(cellSeed * TAU * 2.1 + t);
vec2 finalP = rotatedUv / morphScale;
// distance to dynamic floral shape
float shapeDist = floralDistance(finalP, cellSeed, t + cellSeed * 6.28);
float mask = smoothstep(0.028, -0.012, shapeDist);
// shape color: iridescent mixture
vec3 shapeColor = mix(palB, palA, checker + 0.25 * sin(cellSeed * TAU * 4.0));
shapeColor = mix(shapeColor, coreGlow, 0.45 + 0.3 * cos(gridIdx.x * 0.76 + gridIdx.y * 0.62 + t));
vec3 col = mix(bgCol, shapeColor, mask);
// inner golden core
float coreRadius = length(finalP) - 0.13;
float coreMask = smoothstep(0.024, -0.014, coreRadius);
col = mix(col, coreGlow, coreMask * mask);
// dynamic rays using radial cardinal sine
float angleLocal = atan(finalP.y, finalP.x);
float rayHarmonic = sin(angleLocal * 12.0 + t * 2.5) * cos(angleLocal * 5.0 - t * 1.6);
float rayIntensity = smoothstep(0.22, 0.58, length(finalP)) * (0.75 + 0.45 * rayHarmonic);
rayIntensity *= (1.0 - smoothstep(0.01, 0.38, coreRadius)) * 0.9;
rayIntensity = clamp(rayIntensity, 0.0, 0.68);
col = mix(col, rayTint, rayIntensity * mask);
// glowing outlines: Moiré edge enhancement
float edgeGlow = smoothstep(0.032, 0.014, shapeDist) - smoothstep(0.014, -0.012, shapeDist);
edgeGlow += 0.35 * abs(sin(angleLocal * 26.0 - t * 7.2)) * (1.0 - mask);
col = mix(col, darkOutline, edgeGlow * 0.85);
// relativistic grid lines
vec2 edgeDist = abs(fract(gridUv) - 0.5);
float lineWidth = 0.043 + 0.012 * sin(gridIdx.x * 2.4 + gridIdx.y * 1.9 + t);
float lines = 1.0 - smoothstep(lineWidth - 0.006, lineWidth, min(edgeDist.x, edgeDist.y));
lines *= 0.85;
col = mix(col, vec3(0.96, 0.91, 0.55), lines * 0.65);
// chromatic aberration (fixed: proper float scalar)
float chromaRipple = sin(uv.y * 38.0 + t * 5.2) * 0.25 + cos(uv.x * 32.0 - t * 4.1) * 0.2;
col += vec3(0.06, 0.03, 0.11) * chromaRipple * (1.0 - mask) * 0.5;
fragColor = vec4(col, 1.0);
}
`;
// =========== RECURSIVE CONFORMAL MAP: HYPERBOLIC TORSION + GOLDEN SPIRAL REMIX ===========
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
void main() {
vec2 uv = vUv;
vec2 p = (uv * 2.0 - 1.0) * vec2(uAspect, 1.0);
float r = length(p);
if (r < 0.008) {
fragColor = vec4(1.0, 0.82, 0.45, 1.0);
return;
}
float theta = atan(p.y, p.x);
// Non-Archimedean spiral with hyperbolic tangent modulation
float logr = log(r);
float dynamicTwist = 0.285 + 0.065 * sin(theta * 3.7 - uTime * 0.85);
float warpFactor = tanh(logr * 1.15) * 0.35;
// breathing period influenced by golden ratio
float period = 0.71 * (1.0 + 0.06 * sin(uTime * 0.21));
float animPhase = uTime * 0.175;
// Möbius-like recursive mapping
float mappedLogR = logr - dynamicTwist * theta + warpFactor * cos(theta * 6.0 - uTime);
mappedLogR = mod(mappedLogR + animPhase, period) - period * 0.485;
float newR = exp(mappedLogR + dynamicTwist * theta - warpFactor * cos(theta * 6.0 - uTime));
float newTheta = theta + 0.125 * sin(5.0 * mappedLogR + uTime * 0.9) * cos(theta * 2.3);
vec2 newP = newR * vec2(cos(newTheta), sin(newTheta));
vec2 newUv = (newP / vec2(uAspect, 1.0)) * 0.5 + 0.5;
vec4 sampled = texture(uTex, fract(newUv));
// subtle radial fade to increase depth
float depthFade = clamp(1.25 - r * 0.7, 0.45, 1.0);
sampled.rgb *= depthFade;
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() {
fragColor = texture(uTex, vUv);
}
`;
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 (Droste remix with hyperbolic torsion) -----
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 < 2; 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);
// swap
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>