Custom Code Experiment •
Conformal Map 6
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;
}
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: false, depth: false });
if (!gl) {
canvas.parentElement.innerHTML = '<div style="color:white; padding:20px;">WebGL2 not supported<br>Try a modern browser.</div>';
return;
}
// --------------------------------------------------------------
// VERTEX SHADER (fullscreen triangle strip)
// --------------------------------------------------------------
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);
}
`;
// ========== SHADER A: RETRO CHAOS ENGINE ==========
const FS_GENERATOR = `#version 300 es
precision highp float;
in vec2 vUv;
out vec4 fragColor;
uniform float uTime;
uniform float uAspect;
uniform vec2 uRandomSeed;
float hash(vec2 p) {
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
}
float noise(vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);
vec2 u = f * f * (3.0 - 2.0 * f);
return mix( mix(hash(i + vec2(0.0,0.0)), hash(i + vec2(1.0,0.0)), u.x),
mix(hash(i + vec2(0.0,1.0)), hash(i + vec2(1.0,1.0)), u.x), u.y);
}
void main() {
vec2 uv = vUv;
uv.x *= uAspect;
float t = uTime * 1.7;
float chaos = sin(t * 0.37) * cos(t * 0.59) + sin(t * 1.13);
float chaos2 = fract(sin(t * 0.923) * 1245.7);
vec2 grid = uv * 8.0 + vec2(t * 0.5, t * 0.3);
vec2 igrid = floor(grid);
vec2 fgrid = fract(grid);
float logistic = 0.0;
for(int i = -1; i <= 1; i++) {
for(int j = -1; j <= 1; j++) {
vec2 neighbor = igrid + vec2(i, j);
float seed = hash(neighbor + floor(uTime * 0.23));
float x = seed;
for(int iter = 0; iter < 5; iter++) {
x = 3.9 * x * (1.0 - x);
}
logistic += x * exp(-2.0 * length(fgrid - vec2(i,j)));
}
}
logistic = fract(logistic * 10.0);
vec2 pos = uv * 3.0 - 1.5;
float r = length(pos + vec2(sin(t*0.71)*0.5, cos(t*0.83)*0.5));
float theta = atan(pos.y, pos.x) + t * 0.9;
float attractX = sin(theta * 4.0 + logistic * 12.0) * 0.8 / (r + 0.2);
float attractY = cos(theta * 3.7 - t) * 0.8 / (r + 0.2);
vec2 attractField = vec2(attractX, attractY);
vec2 warpUv = uv + attractField * 0.25 + vec2(noise(uv * 12.0 + t * 0.5), noise(uv.yx * 13.0 - t * 0.4)) * 0.1;
warpUv = fract(warpUv * 2.0) * 2.0 - 1.0;
warpUv.x *= uAspect;
vec3 col = vec3(0.0);
float chromaShift = sin(warpUv.y * 80.0 - t * 25.0) * 0.4 + cos(warpUv.x * 70.0 + t * 18.0) * 0.4;
vec2 ctr = vec2(0.5, 0.5);
float dist = length(warpUv - ctr);
float rings = sin(30.0 * dist - t * 12.0) * 0.5 + 0.5;
rings *= 1.0 - smoothstep(0.2, 0.8, dist);
rings = pow(rings, 1.5) * 1.2;
float scanline = sin(uv.y * 600.0 + t * 100.0) * 0.3 + 0.7;
float staticNoise = hash(uv * 400.0 + floor(t * 40.0)) * 0.5;
vec2 checkerUv = warpUv * 12.0;
float check = step(0.5, fract(checkerUv.x + checkerUv.y + logistic * 5.0));
vec3 baseColor = mix(vec3(0.05, 0.9, 0.2), vec3(0.9, 0.05, 0.5), check);
baseColor = mix(baseColor, vec3(0.2, 0.1, 0.8), fract(logistic * 8.0));
col.r = sin(baseColor.r * 15.0 + t * 7.0 + rings * 12.0) * 0.8 + 0.5;
col.g = cos(baseColor.g * 18.0 - t * 6.5 + rings * 9.0) * 0.8 + 0.5;
col.b = sin(baseColor.b * 22.0 + t * 8.0 + logistic * 10.0) * 0.8 + 0.5;
col += vec3(attractField.x * 0.8, attractField.y * 0.7, (attractField.x + attractField.y) * 0.5);
col += vec3(0.7, 0.3, 0.9) * rings * (0.8 + 0.5 * sin(t * 9.0));
float glitch = step(0.98, hash(vec2(floor(uv.y * 30.0 + t * 12.0), t)));
col = mix(col, vec3(1.0, 0.0, 1.0), glitch * 0.9);
vec2 tearUv = uv;
tearUv.x += sin(uv.y * 150.0 + t * 90.0) * 0.05 * glitch;
if (fract(uv.y * 20.0 + t * 5.0) < 0.03) col = vec3(1.0, 1.0, 1.0);
col.r *= scanline * (0.8 + staticNoise);
col.g *= scanline * (0.7 + staticNoise * 0.7);
col.b *= scanline * (0.6 + staticNoise * 0.9);
float vig = 1.0 - length(uv - 0.5) * 0.7;
col *= vig;
col = floor(col * 8.0) / 8.0;
fragColor = vec4(col, 1.0);
}
`;
// ========== SHADER B: FEEDBACK CHAOS LOOP ==========
const FS_FEEDBACK = `#version 300 es
precision highp float;
in vec2 vUv;
out vec4 fragColor;
uniform sampler2D uPrevTex;
uniform sampler2D uFreshTex;
uniform float uTime;
uniform float uAspect;
uniform float uFeedbackAmount;
float hash(vec2 p) {
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
}
void main() {
vec2 uv = vUv;
float t = uTime;
vec2 q = uv * 2.0 - 1.0;
q.x *= uAspect;
float angle = sin(t * 0.47) * 6.2831;
float s = sin(angle);
float c = cos(angle);
vec2 rot1 = vec2( c * q.x - s * q.y, s * q.x + c * q.y );
vec2 rot2 = vec2( c * q.x + s * q.y, -s * q.x + c * q.y );
float sel = fract(sin(t * 0.231) * 123.456);
vec2 warp;
if (sel < 0.33) {
warp = rot1 * 2.0 + vec2(0.5, 0.3);
} else if (sel < 0.66) {
warp = rot2 * 1.8 + vec2(-0.4, 0.6);
} else {
warp = (rot1 + rot2) * 0.9 + vec2(0.2, -0.5);
}
warp = abs(fract(warp * 1.5) - 0.5) * 2.0;
vec2 chaoticUv = warp * 0.5 + 0.5;
chaoticUv.x /= uAspect;
vec2 distort = vec2(
sin(uv.y * 40.0 + t * 15.0) * 0.03,
cos(uv.x * 35.0 - t * 12.0) * 0.03
);
vec2 finalUv = chaoticUv + distort;
vec3 prev = texture(uPrevTex, fract(finalUv)).rgb;
vec3 fresh = texture(uFreshTex, uv + distort * 0.5).rgb;
float feedback = uFeedbackAmount * (0.7 + 0.3 * sin(t * 2.3));
vec3 col = mix(fresh, prev, feedback);
col = max(col, prev * 0.85);
col += fresh * 0.3;
col = clamp(col, 0.0, 1.2);
col = pow(col, vec3(1.0/1.2));
fragColor = vec4(col, 1.0);
}
`;
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 progGen = createProgram(VS, FS_GENERATOR);
const progFeedback = createProgram(VS, FS_FEEDBACK);
const progCopy = createProgram(VS, FS_COPY);
if (!progGen || !progFeedback || !progCopy) throw new Error("Shader compilation failed");
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(progGen, 'aPos');
gl.enableVertexAttribArray(aPos);
gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0);
let fboA, fboB, fboTemp;
function createFBOandTex(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 };
}
let w, h;
function resizeAndRecreate() {
w = canvas.clientWidth;
h = canvas.clientHeight;
canvas.width = w;
canvas.height = h;
if (fboA) {
gl.deleteFramebuffer(fboA.fbo);
gl.deleteTexture(fboA.tex);
gl.deleteFramebuffer(fboB.fbo);
gl.deleteTexture(fboB.tex);
gl.deleteFramebuffer(fboTemp.fbo);
gl.deleteTexture(fboTemp.tex);
}
fboA = createFBOandTex(w, h);
fboB = createFBOandTex(w, h);
fboTemp = createFBOandTex(w, h);
}
window.addEventListener('resize', () => { resizeAndRecreate(); });
resizeAndRecreate();
const locGenTime = gl.getUniformLocation(progGen, 'uTime');
const locGenAspect = gl.getUniformLocation(progGen, 'uAspect');
const locGenSeed = gl.getUniformLocation(progGen, 'uRandomSeed');
const locFbTime = gl.getUniformLocation(progFeedback, 'uTime');
const locFbAspect = gl.getUniformLocation(progFeedback, 'uAspect');
const locFbPrev = gl.getUniformLocation(progFeedback, 'uPrevTex');
const locFbFresh = gl.getUniformLocation(progFeedback, 'uFreshTex');
const locFbAmount = gl.getUniformLocation(progFeedback, 'uFeedbackAmount');
const locCopyTex = gl.getUniformLocation(progCopy, 'uTex');
function render(nowMs) {
requestAnimationFrame(render);
let t = nowMs * 0.001;
let aspect = w / h;
gl.bindVertexArray(vao);
gl.viewport(0, 0, w, h);
// fresh chaotic frame
gl.bindFramebuffer(gl.FRAMEBUFFER, fboTemp.fbo);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(progGen);
gl.uniform1f(locGenTime, t);
gl.uniform1f(locGenAspect, aspect);
gl.uniform2f(locGenSeed, Math.sin(t)*1245.7, Math.cos(t*0.71)*982.3);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
// feedback pass
gl.useProgram(progFeedback);
gl.uniform1f(locFbTime, t);
gl.uniform1f(locFbAspect, aspect);
gl.uniform1i(locFbPrev, 0);
gl.uniform1i(locFbFresh, 1);
let fbAmount = 0.65 + 0.3 * Math.sin(t * 0.63) * Math.cos(t * 0.91);
gl.uniform1f(locFbAmount, fbAmount);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, fboA.tex);
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, fboTemp.tex);
gl.bindFramebuffer(gl.FRAMEBUFFER, fboB.fbo);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
// swap A and B
let tempFbo = fboA;
fboA = fboB;
fboB = tempFbo;
// output 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.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, fboA.tex);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
requestAnimationFrame(render);
})();
</script>
</body>
</html>