library / 3d & webgl / envato-infinite-mirror-room
live - 60fps 1920 x 1080
envato-infinite-mirror-room.html - self-contained
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Infinite Mirror Room</title>
    <style>
        :root {
            --bg-dark: #050510;
            --neon: #00ffff;
            --neon-secondary: #ff00ff;
        }
        body {
            margin: 0;
            height: 100vh;
            background-color: var(--bg-dark);
            overflow: hidden;
            display: flex;
            justify-content: center;
            align-items: center;
            perspective: 800px;
            /* Crucial for the 3D depth illusion */
        }
        /* The container holding the infinite sequence */
        .mirror-tunnel {
            position: relative;
            width: 300px;
            height: 300px;
            transform-style: preserve-3d;
            animation: tunnelDrift 10s linear infinite alternate;
        }
        @keyframes tunnelDrift {
            0% {
                transform: rotateY(-10deg) rotateX(5deg);
            }
            100% {
                transform: rotateY(10deg) rotateX(-5deg);
            }
        }
        /* 
           Using a clever trick: we create multiple nested divs.
           Each frame is exactly the same, but pushed back in Z-space.
        */
        .frame {
            position: absolute;
            inset: 0;
            border: 2px solid rgba(0, 255, 255, 0.5);
            /* Creating the neon door frame look */
            box-shadow:
                0 0 10px var(--neon),
                inset 0 0 10px var(--neon);
            border-radius: 20px;
            /* Inner glowing lights */
            background: linear-gradient(45deg,
                    transparent 49%,
                    rgba(255, 0, 255, 0.2) 50%,
                    transparent 51%);
        }
        /* 
           Javascript will inject 30 of these frames, each translating further into 
           the Z-axis. To create motion, we'll animate them flowing forward. 
        */
    </style>
</head>
<body>
    <div class="mirror-tunnel" id="tunnel">
        <!-- Frames generated by JS -->
    </div>
    <script>
        const tunnel = document.getElementById('tunnel');
        const frameCount = 40;
        const depthStep = 50; // Distance between frames
        for (let i = 0; i < frameCount; i++) {
            const frame = document.createElement('div');
            frame.className = 'frame';
            // Initial positioning
            const zPosition = i * -depthStep;
            // To make it look "infinite", the ones furthest back are darkest
            const opacity = 1 - (i / frameCount);
            frame.style.transform = `translateZ(${zPosition}px)`;
            frame.style.opacity = opacity;
            // Optional: Alternate colors 
            if (i % 3 === 0) {
                frame.style.borderColor = 'rgba(255, 0, 255, 0.5)';
                frame.style.boxShadow = `0 0 15px var(--neon-secondary), inset 0 0 15px var(--neon-secondary)`;
            }
            tunnel.appendChild(frame);
        }
        // Animation Loop shifting all frames forward
        let offset = 0;
        function animateFrames() {
            offset += 0.5; // Speed of forward motion
            if (offset >= depthStep) {
                offset = 0; // Reset loop for continuous illusion
            }
            const frames = document.querySelectorAll('.frame');
            frames.forEach((frame, index) => {
                const zPosition = (index * -depthStep) + offset;
                frame.style.transform = `translateZ(${zPosition}px)`;
            });
            requestAnimationFrame(animateFrames);
        }
        animateFrames();
        // Mouse Parallax Effect
        window.addEventListener('mousemove', (e) => {
            const x = (e.clientX / window.innerWidth - 0.5) * 20; // -10 to +10 deg
            const y = (e.clientY / window.innerHeight - 0.5) * -20; // Inverted Y
            tunnel.style.transition = 'transform 0.1s ease-out';
            // Override CSS animation temporarily or run alongside it
            // tunnel.style.transform = `rotateY(${x}deg) rotateX(${y}deg)`;
        });
    </script>
</body>
</html>

About this animation

A recursive CSS 3D transformation that creates an optical illusion of a never-ending mirrored corridor.

Under the hood

Recursive Z-Axis Translation

A Javascript loop spawns 40 empty frames, spacing each one precisely 50px further back on the Z-axis via CSS translateZ(). To create motion, the script continuously loops an offset variable simulating the camera passing infinitely through these neon doorways.

Source Code
Copied to clipboard