canvas#canvas
View Compiled
* {
	box-sizing: border-box;
}

html,
body {
	margin: 0;
	min-height: 100vh;
	overflow: hidden;

	background: repeating-radial-gradient(
		circle at center,
		#444 0 10%,
		#111 10% 20%
	);

	touch-action: none;
}

canvas {
	width: 100%;
	height: auto;
	object-fit: contain;
}
/*********
 * made by Matthias Hurrle (@atzedent)
 */

/** @type {HTMLCanvasElement} */
const canvas = window.canvas;
const gl = canvas.getContext("webgl2");
const dpr = Math.max(1, 0.5 * window.devicePixelRatio);
/** @type {Map<string,PointerEvent>} */
const touches = new Map();

const vertexSource = `#version 300 es
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif

in vec2 position;

void main(void) {
    gl_Position = vec4(position, 0., 1.);
}
`;
const fragmentSource = `#version 300 es
/*********
* made by Matthias Hurrle (@atzedent)
*/

#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif

uniform float time;
uniform vec2 resolution;
uniform vec2 touch;
uniform int pointerCount;

out vec4 fragColor;

#define T time
#define S smoothstep
#define PI 3.14159
#define PHI (.5+.5*sqrt(5.))
#define TAU 6.28318
#define PI12 1.57079
#define rot(a) mat2(cos(a), sin(a), -sin(a), cos(a))
#define hue(a) (.25+.4*cos(7.3*(a)+vec3(0,83,21)))
#define mouse (touch / resolution)
#define ftick tick(sin(3.+1.0*T*.25), 2.)

float dode(vec3 p, float s) {
    float d = 0.;
    d = max(d, abs(dot(p, normalize(vec3(0, PHI, 1)))));
    d = max(d, abs(dot(p, normalize(vec3(0, -PHI, 1)))));
    d = max(d, abs(dot(p, normalize(vec3(1, 0, PHI)))));
    d = max(d, abs(dot(p, normalize(vec3(-1, 0, PHI)))));
    d = max(d, abs(dot(p, normalize(vec3(PHI, 1, 0)))));
    d = max(d, abs(dot(p, normalize(vec3(-PHI, 1, 0)))));

    return d - s;
}

float tick(float t,float e) {
    return floor(t)+pow(S(.0,1.,fract(t)),e);
}

float box(vec3 p, vec3 s, float r) {
    p = abs(p) - s;

    return length(max(vec3(0), p)) +
        min(.0, max(max(p.x, p.y), p.z)) - r;
}

float disc(vec3 p, vec2 s, float r) {
    vec2 e = vec2(length(abs(p.xz)), abs(p.y)) - s;

    return length(max(e, .0)) +
        min(.0, max(e.x, e.y)) - r;
}

float map(vec3 p) {
    float d = 1e5;
    d = min(d, dode(p, .6));
    d = max(-d, box(p, vec3(1), .05));
    d = min(d, disc(p - vec3(0, -1.75, 0), vec2(1.5, .2), .05));

    return d;
}

vec3 norm(vec3 p) {
    vec2 e = vec2(1e-2, 0);
    float d = map(p);
    vec3 n = d - vec3(
        map(p - e.xyy),
        map(p - e.yxy),
        map(p - e.yyx)
    );

    return normalize(n);
}

void cam(inout vec3 p) {
    if(pointerCount > 0) {
        p.yz *= rot(mouse.y * PI - PI12);
        p.xz *= rot(mouse.x * TAU);
    } else {
        p.yz *= rot(ftick);
        p.xz *= rot(tick(T * .25, .25) * PI12);
    }
}

void main(void) {
    vec2 uv = (
        gl_FragCoord.xy - .5 * resolution.xy
    ) / min(resolution.x, resolution.y);

    float zoom = pointerCount > 0 ? .0 : -exp(ftick) * .4;
    vec3 col = vec3(0),
    ro = vec3(0, 0, zoom - 4.),
    rd = normalize(vec3(uv, 1));

    cam(ro);
    cam(rd);
    
    vec3 p = ro;

    vec3 tint = vec3(1, .5 + .5 * sin(tick(T, 5.)), 0);

    const float steps = 100., maxd = 40.;
    float i = .0, dd = .0, at = .0, side = 1., e = 1.;
    for(; i < steps; i++) {
        float d = map(p) * side;

        if(d < 1e-3) {
            vec3 n = norm(p) * side,
            l = normalize(vec3(0, 0, abs(rd.z)) - p),
            r = reflect(rd, n);

            if(dot(l, n) < .0) l = -l;

            vec3 h = normalize(l - r);

            float fres = pow(1. - max(.0, dot(-r, n)), 5.),
            diff = pow(max(.0, dot(l, n)), 4.),
            fog = e * S(1., 0., dd / maxd);

            col += e * S(.0, 1., i / 300.) +
                mix(
                    tint,
                    vec3(1, 1, 0),
                    fog * fres
                ) * diff * fres * (
                    5. * pow(max(.0, dot(h, n)), 32.) +
                    .5 * pow(max(.0, dot(r, n)), 64.)
                );
            col += .25 * diff * e;
            col += .07 * hue(diff) * e;
            
            const float aodist = .125;
            float ao = clamp(map(p + n * aodist) / (aodist * .5), .0, 1.);
            col -= ao * .07;
            
            e *= .95;
            side = -side;

            vec3 rdo = refract(rd, n, 1. + .45 * side);
            if(dot(rdo, rdo) == .0) {
                rdo = reflect(r, n);
            }

            rd = rdo;

            d = 3e-1;
        }

        if(dd > maxd) {
            dd = maxd;
            break;
        }

        p += rd * d;
        dd += d;
        at += .1 * (.1 / d);
    }
    col += pow(S(.0, 1., at * 3e-3), 4.);
    float iter = pow(abs(rd.z), 7.);
    vec3 atmocol = mix(vec3(0), tint, iter);
    col += atmocol+S(.5, .85, max(.0, i / (300.)));

    fragColor = vec4(col, 1);
}
`;
let time;
let buffer;
let program;
let touch;
let resolution;
let pointerCount;
let vertices = [];
let touching = false;

function resize() {
	const { innerWidth: width, innerHeight: height } = window;

	canvas.width = width * dpr;
	canvas.height = height * dpr;

	gl.viewport(0, 0, width * dpr, height * dpr);
}

function compile(shader, source) {
	gl.shaderSource(shader, source);
	gl.compileShader(shader);

	if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
		console.error(gl.getShaderInfoLog(shader));
	}
}

function setup() {
	const vs = gl.createShader(gl.VERTEX_SHADER);
	const fs = gl.createShader(gl.FRAGMENT_SHADER);

	program = gl.createProgram();

	compile(vs, vertexSource);
	compile(fs, fragmentSource);

	gl.attachShader(program, vs);
	gl.attachShader(program, fs);
	gl.linkProgram(program);

	if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
		console.error(gl.getProgramInfoLog(program));
	}

	vertices = [-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0];

	buffer = gl.createBuffer();

	gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
	gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

	const position = gl.getAttribLocation(program, "position");

	gl.enableVertexAttribArray(position);
	gl.vertexAttribPointer(position, 2, gl.FLOAT, false, 0, 0);

	time = gl.getUniformLocation(program, "time");
	touch = gl.getUniformLocation(program, "touch");
	pointerCount = gl.getUniformLocation(program, "pointerCount");
	resolution = gl.getUniformLocation(program, "resolution");
}

function draw(now) {
	gl.clearColor(0, 0, 0, 1);
	gl.clear(gl.COLOR_BUFFER_BIT);

	gl.useProgram(program);
	gl.bindBuffer(gl.ARRAY_BUFFER, null);
	gl.bindBuffer(gl.ARRAY_BUFFER, buffer);

	gl.uniform1f(time, now * 0.001);
	gl.uniform2f(touch, ...getTouches());
	gl.uniform1i(pointerCount, touches.size);
	gl.uniform2f(resolution, canvas.width, canvas.height);
	gl.drawArrays(gl.TRIANGLES, 0, vertices.length * 0.5);
}

function getTouches() {
	if (!touches.size) {
		return [0, 0];
	}

	for (let [id, t] of touches) {
		const result = [dpr * t.clientX, dpr * (innerHeight - t.clientY)];

		return result;
	}
}

function loop(now) {
	draw(now);
	requestAnimationFrame(loop);
}

function init() {
	setup();
	resize();
	loop(0);
}

document.body.onload = init;
window.onresize = resize;
canvas.onpointerdown = (e) => {
	touching = true;
	touches.set(e.pointerId, e);
};
canvas.onpointermove = (e) => {
	if (!touching) return;
	touches.set(e.pointerId, e);
};
canvas.onpointerup = (e) => {
	touching = false;
	touches.clear();
};
canvas.onpointerout = (e) => {
	touching = false;
	touches.clear();
};

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.