library / 3d & webgl / tunnel
live - 60fps 1920 x 1080
tunnel.html - self-contained
<!DOCTYPE html>
<html>
<head>
<title>Tunnel</title>
<meta name="category" content="Space">
<style>
    body { margin: 0; background-color: #000; overflow: hidden; }
    canvas { display: block; }
</style>
</head>
<body>
<canvas id="tunnelCanvas"></canvas>
<script>
    const canvas = document.getElementById('tunnelCanvas');
    const ctx = canvas.getContext('2d');
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    const numLines = 200;
    const lines = [];
    let speed = 2;
    let rotation = 0;
    class Line {
        constructor() {
            this.reset();
        }
        reset() {
            this.x = 0;
            this.y = 0;
            this.z = Math.random() * canvas.width;
            this.angle = Math.random() * Math.PI * 2;
            this.radius = Math.random() * (canvas.width / 4) + (canvas.width / 8);
        }
        update() {
            this.z -= speed;
            if (this.z < 1) {
                this.reset();
            }
        }
        draw() {
            const scale = canvas.width / this.z;
            const x2d = Math.cos(this.angle + rotation) * this.radius * scale + canvas.width / 2;
            const y2d = Math.sin(this.angle + rotation) * this.radius * scale + canvas.height / 2;
            const prev_scale = canvas.width / (this.z + speed * 20);
            const prev_x2d = Math.cos(this.angle + rotation) * this.radius * prev_scale + canvas.width / 2;
            const prev_y2d = Math.sin(this.angle + rotation) * this.radius * prev_scale + canvas.height / 2;
            const brightness = 1 - this.z / canvas.width;
            ctx.beginPath();
            ctx.moveTo(prev_x2d, prev_y2d);
            ctx.lineTo(x2d, y2d);
            ctx.strokeStyle = `rgba(200, 220, 255, ${brightness * 0.7})`;
            ctx.lineWidth = scale * 1.5;
            ctx.stroke();
        }
    }
    function init() {
        for (let i = 0; i < numLines; i++) {
            lines.push(new Line());
        }
    }
    function animate() {
        ctx.fillStyle = 'rgba(0, 0, 0, 0.2)';
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        rotation += 0.001;
        lines.forEach(line => {
            line.update();
            line.draw();
        });
        requestAnimationFrame(animate);
    }
    window.addEventListener('resize', () => {
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
        lines.length = 0;
        init();
    });
    init();
        animate();
</script>
</body>
</html>

About this animation

A mesmerizing 3D tunnel effect that pulls the viewer in.

Under the hood

Fractal 3D Perspective

By drawing concentric rings consisting of multiple squares, and assigning deeper z-indices a smaller scale and darker color, the canvas creates an endless tunnel descent. Iterative loops draw the shape progressively smaller toward the vanishing point.

Source Code
Copied to clipboard