<html><head>

<style>

  body, html {

    margin: 0;

    padding: 0;

    width: 100%;

    height: 100%;

    overflow: hidden;

    background: #000;

    font-family: Arial, sans-serif;

  }

  canvas {

    width: 100%;

    height: 100%;

  }

  #ui {

    position: absolute;

    top: 10px;

    left: 10px;

    color: white;

  }

  #btc-emoji {

    font-size: 48px;

    animation: pulse 2s infinite;

    position: absolute;

    top: 50%;

    left: 50%;

    transform: translate(-50%, -50%);

  }

  @keyframes pulse {

    0% { transform: translate(-50%, -50%) scale(1); }

    50% { transform: translate(-50%, -50%) scale(1.2); }

    100% { transform: translate(-50%, -50%) scale(1); }

  }

</style>

</head>

<body>

<canvas id="glCanvas"></canvas>

<div id="ui">

  <div>FPS: <span id="fps">0</span></div>

</div>

<div id="btc-emoji">₿</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.8.1/gl-matrix-min.js"></script>

<script>

const canvas = document.getElementById('glCanvas');

const gl = canvas.getContext('webgl2');


if (!gl) {

  console.error('WebGL 2 not supported');

  document.body.innerHTML = 'WebGL 2 is not supported in your browser.';

}


const vertexShaderSource = `#version 300 es

in vec4 a_position;

void main() {

  gl_Position = a_position;

}`;


const fragmentShaderSource = `#version 300 es

precision highp float;

out vec4 fragColor;


uniform vec3 iResolution;

uniform float iTime;

uniform vec4 iMouse;


vec2 rotate(vec2 a, float b)

{

    float c = cos(b);

    float s = sin(b);

    return vec2(

        a.x * c - a.y * s,

        a.x * s + a.y * c

    );

}


float sdBox( vec3 p, vec3 b )

{

    vec3 d = abs(p) - b;

    return min(max(d.x,max(d.y,d.z)),0.0) + length(max(d,0.0));

}


float scene(vec3 p)

{

    vec3 sp = p + vec3(1,3,2);


    // Add a rotating platform

    vec2 rotatedXZ = rotate(p.xz, iTime);

    float platform = sdBox(vec3(rotatedXZ.x, p.y + 0.5, rotatedXZ.y), vec3(2.0, 0.1, 2.0));


    return min(

        min(

            max(

                max(

                    sdBox(p, vec3(1.)),

                    -sdBox(p,vec3(.9))

                ),

                abs(mod(length(sp)-iTime*.2,.2)-.1)-.01

            ),

            length(p)-.8

        ),

        platform

    );

}


float sceneWithFloor(vec3 p)

{

    return min(

        scene(p),

        1.+p.y

    );

}


vec3 trace(vec3 cam, vec3 dir)

{

    vec3 accum = vec3(1);

    for (int bounce = 0; bounce < 20; bounce++)

    {

        float tfloor = (cam.y + 1.)/-dir.y;

        float t = 0.;

        float k = 0.;

        for(int i = 0; i < 100; i++)

        {

            k = scene(cam + t * dir);

            t += k;

            if (k < 0.001 || (tfloor > 0. && t > tfloor))

                break;

        }

        if (tfloor > 0.)

            t = min(t, tfloor);


        vec3 h = cam + t * dir;


        vec2 o = vec2(.005, 0);

        vec3 n = normalize(vec3(

            sceneWithFloor(h+o.xyy)-sceneWithFloor(h-o.xyy),

            sceneWithFloor(h+o.yxy)-sceneWithFloor(h-o.yxy),

            sceneWithFloor(h+o.yyx)-sceneWithFloor(h-o.yyx)

        ));


        if (h.y < -.999)

        {

            // floor

            float A = .5;

            float B = max(0.,sceneWithFloor(h+n*A));

            float w = clamp(1.-length(h.xz) * .01, 0., 1.);

            w = w * .2 + .8;

            return accum * vec3(pow(B/A,.7)*.6+.4) * w;

        }

        else if (length(h) < .85)

        {

            // ball

            float fresnel = mix(.001,1.,pow(1.-dot(-dir, n),5.));

            accum *= fresnel;

            cam = h + n * .01;

            dir = reflect(dir, n);

        }

        else if (length(h) < 2.) // ew yucky hack

        {

            // cube

            accum *= vec3(.72,.576,.288);

            cam = h + n * .01;

            dir = reflect(dir, n);

        }

        else if (abs(h.y + 0.5) < 0.1 && length(h.xz) < 2.0)

        {

            // platform

            accum *= vec3(0.5, 0.5, 0.8); // Blue-ish color for the platform

            cam = h + n * .01;

            dir = reflect(dir, n);

        }

        else

        {

            // sky

            return accum * vec3(.8);

        }

    }

    return vec3(0);

}


void mainImage(out vec4 out_color, vec2 fragCoord)

{

    vec2 uv = fragCoord / iResolution.xy - .5;

    uv.x *= iResolution.x / iResolution.y;


    vec3 cam = vec3(0,0,-4);

    vec3 dir = normalize(vec3(uv, 1));


    cam.yz = rotate(cam.yz, sin(iTime*.1)*.25+.25);

    dir.yz = rotate(dir.yz, sin(iTime*.1)*.25+.25);


    cam.xz = rotate(cam.xz, iTime*.3);

    dir.xz = rotate(dir.xz, iTime*.3);


    out_color = vec4(pow(trace(cam,dir),vec3(.45)),1);

}


void main() {

    mainImage(fragColor, gl_FragCoord.xy);

}`;


function createShader(gl, type, source) {

    const shader = gl.createShader(type);

    gl.shaderSource(shader, source);

    gl.compileShader(shader);

    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {

        console.error('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader));

        gl.deleteShader(shader);

        return null;

    }

    return shader;

}


const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);

const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);


const program = gl.createProgram();

gl.attachShader(program, vertexShader);

gl.attachShader(program, fragmentShader);

gl.linkProgram(program);


if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {

    console.error('Unable to initialize the shader program: ' + gl.getProgramInfoLog(program));

}


const positionAttributeLocation = gl.getAttribLocation(program, "a_position");

const positionBuffer = gl.createBuffer();

gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

const positions = [

    -1, -1,

     1, -1,

    -1,  1,

    -1,  1,

     1, -1,

     1,  1,

];

gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);


const vao = gl.createVertexArray();

gl.bindVertexArray(vao);

gl.enableVertexAttribArray(positionAttributeLocation);

gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0);


const resolutionUniformLocation = gl.getUniformLocation(program, "iResolution");

const timeUniformLocation = gl.getUniformLocation(program, "iTime");

const mouseUniformLocation = gl.getUniformLocation(program, "iMouse");


function resizeCanvasToDisplaySize(canvas) {

    const displayWidth  = canvas.clientWidth;

    const displayHeight = canvas.clientHeight;

 

    const needResize = canvas.width  !== displayWidth ||

                       canvas.height !== displayHeight;

 

    if (needResize) {

        canvas.width  = displayWidth;

        canvas.height = displayHeight;

    }

 

    return needResize;

}


let mouseX = 0, mouseY = 0;

canvas.addEventListener('mousemove', (event) => {

    mouseX = event.clientX;

    mouseY = event.clientY;

});


const fpsElement = document.getElementById('fps');

let lastTime = 0;

let frameCount = 0;


function render(time) {

    const deltaTime = time - lastTime;

    lastTime = time;

    frameCount++;


    if (deltaTime >= 1000) {

        const fps = Math.round((frameCount * 1000) / deltaTime);

        fpsElement.textContent = fps;

        frameCount = 0;

    }


    time *= 0.001;  // convert to seconds


    resizeCanvasToDisplaySize(gl.canvas);

    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);


    gl.useProgram(program);

    gl.bindVertexArray(vao);


    gl.uniform3f(resolutionUniformLocation, gl.canvas.width, gl.canvas.height, 1);

    gl.uniform1f(timeUniformLocation, time);

    gl.uniform4f(mouseUniformLocation, mouseX, mouseY, 0, 0);


    gl.drawArrays(gl.TRIANGLES, 0, 6);


    requestAnimationFrame(render);

}


requestAnimationFrame(render);


// Rotate the BTC emoji

const btcEmoji = document.getElementById('btc-emoji');

function rotateBtcEmoji() {

    const time = Date.now() * 0.001;

    const x = Math.cos(time) * 100;

    const y = Math.sin(time) * 100;

    btcEmoji.style.transform = `translate(calc(-50% + ${x}px), calc(-50% + ${y}px)) scale(${1 + Math.sin(time * 2) * 0.2})`;

    requestAnimationFrame(rotateBtcEmoji);

}

rotateBtcEmoji();

</script>

</body>

</html>