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();
};
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.