← Mike Higgins Home
Custom Code Experiment •

Calabi-Yau 2

Source Code View HTML / CSS / JavaScript
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        body {
            background: #000;
            overflow: hidden;
            font-family: 'Courier New', monospace;
            touch-action: none;
            position: fixed;
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
        }
        canvas {
            display: block;
            width: 100%;
            height: 100%;
            position: fixed;
            top: 0;
            left: 0;
        }
        .controls {
            position: fixed;
            top: 20px;
            left: 50%;
            transform: translateX(-50%);
            display: flex;
            gap: 12px;
            z-index: 10;
            padding: 10px;
            background: rgba(0, 0, 0, 0.6);
            border-radius: 35px;
            backdrop-filter: blur(10px);
            -webkit-backdrop-filter: blur(10px);
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
        }
        .btn {
            width: 50px;
            height: 50px;
            border-radius: 50%;
            border: 2px solid rgba(255, 255, 255, 0.3);
            background: rgba(0, 0, 0, 0.5);
            cursor: pointer;
            transition: all 0.3s;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 20px;
            color: rgba(255, 255, 255, 0.7);
            -webkit-tap-highlight-color: transparent;
        }
        .btn:hover {
            border-color: rgba(255, 255, 255, 0.8);
            background: rgba(255, 255, 255, 0.1);
            transform: scale(1.1);
        }
        .btn.active {
            border-color: #0ff;
            background: rgba(0, 255, 255, 0.2);
            color: #0ff;
        }
        @media (max-width: 768px), (max-height: 600px) {
            .controls {
                top: 10px;
                gap: 8px;
                padding: 6px;
                background: rgba(0, 0, 0, 0.75);
                max-width: 95vw;
            }
            .btn {
                width: 40px;
                height: 40px;
                font-size: 16px;
            }
        }
        @media (max-width: 480px), (max-height: 500px) {
            .controls {
                top: 8px;
                gap: 6px;
                padding: 5px;
                background: rgba(0, 0, 0, 0.8);
                max-width: 98vw;
            }
            .btn {
                width: 36px;
                height: 36px;
                font-size: 14px;
            }
        }
        @media (max-height: 400px) {
            .controls {
                top: 5px;
                gap: 4px;
                padding: 4px;
            }
            .btn {
                width: 32px;
                height: 32px;
                font-size: 12px;
            }
        }
    </style>
</head>
<body>
    <canvas id="canvas"></canvas>
    <div class="controls">
        <button class="btn active" data-manifold="0">●</button>
        <button class="btn" data-manifold="1">●</button>
        <button class="btn" data-manifold="2">●</button>
        <button class="btn" data-manifold="3">●</button>
        <button class="btn" data-manifold="4">●</button>
    </div>

    <script>
        const canvas = document.getElementById('canvas');
        const ctx = canvas.getContext('2d');
        let w, h;

        function resize() {
            w = canvas.width = window.innerWidth;
            h = canvas.height = window.innerHeight;
        }
        resize();
        window.addEventListener('resize', resize);

        let t = 0;
        let currentManifold = 0;
        let rotation = { x: 0, y: 0 };

        document.querySelectorAll('.btn').forEach(btn => {
            btn.addEventListener('click', () => {
                document.querySelectorAll('.btn').forEach(b => b.classList.remove('active'));
                btn.classList.add('active');
                currentManifold = parseInt(btn.dataset.manifold);
            });
        });

        function project(x, y, z, w4d) {
            const scale = 1125 / (4 - w4d * 0.5);
            const rx = x, ry = y, rz = z, rw = w4d;

            const c1 = Math.cos(rotation.y), s1 = Math.sin(rotation.y);
            const c2 = Math.cos(rotation.x), s2 = Math.sin(rotation.x);

            const x1 = rx * c1 - rz * s1;
            const z1 = rx * s1 + rz * c1;
            const y1 = ry * c2 - z1 * s2;
            const z2 = ry * s2 + z1 * c2;

            return {
                x: w / 2 + x1 * scale,
                y: h / 2 + y1 * scale,
                z: z2,
                brightness: (z2 + 2) / 4
            };
        }

        function manifold0(u, v, t) {
            const r1 = 0.5 + 0.3 * Math.cos(3 * v + t);
            const r2 = 0.5 + 0.3 * Math.sin(2 * u + t);
            return {
                x: r1 * Math.cos(u) * Math.cos(v),
                y: r1 * Math.cos(u) * Math.sin(v),
                z: r1 * Math.sin(u),
                w: r2 * Math.sin(u + v)
            };
        }

        function manifold1(u, v, t) {
            const k = 0.3;
            const a = Math.cos(u) + k * Math.cos(3 * u + t * 0.5);
            const b = Math.sin(u) + k * Math.sin(3 * u + t * 0.5);
            return {
                x: a * Math.cos(v),
                y: a * Math.sin(v),
                z: b * Math.cos(v + t),
                w: b * Math.sin(v + t)
            };
        }

        function manifold2(u, v, t) {
            const twist = Math.sin(u * 2 + t) * 0.3;
            return {
                x: Math.cos(u) * (1 + 0.3 * Math.cos(v + twist)),
                y: Math.sin(u) * (1 + 0.3 * Math.cos(v + twist)),
                z: 0.3 * Math.sin(v + twist),
                w: Math.sin(u * 3 + v * 2 + t)
            };
        }

        function manifold3(u, v, t) {
            const r = 0.8 + 0.2 * Math.sin(4 * u + t);
            const s = 0.8 + 0.2 * Math.cos(4 * v + t);
            return {
                x: r * Math.cos(u) * Math.cos(v),
                y: r * Math.cos(u) * Math.sin(v),
                z: s * Math.sin(u),
                w: Math.sin(2 * u) * Math.cos(2 * v)
            };
        }

        function manifold4(u, v, t) {
            const p = 2, q = 3;
            return {
                x: Math.cos(p * u + t) * Math.cos(v),
                y: Math.cos(p * u + t) * Math.sin(v),
                z: Math.sin(q * u + t * 0.7) * Math.cos(v),
                w: Math.sin(q * u + t * 0.7) * Math.sin(v)
            };
        }

        const manifolds = [manifold0, manifold1, manifold2, manifold3, manifold4];

        function draw() {
            ctx.fillStyle = 'rgba(0, 0, 0, 0.15)';
            ctx.fillRect(0, 0, w, h);

            const points = [];
            const res = 40;

            for (let i = 0; i <= res; i++) {
                for (let j = 0; j <= res; j++) {
                    const u = (i / res) * Math.PI * 2;
                    const v = (j / res) * Math.PI * 2;
                    const p = manifolds[currentManifold](u, v, t);
                    const proj = project(p.x, p.y, p.z, p.w);
                    points.push({ ...proj, u: i, v: j });
                }
            }

            points.sort((a, b) => a.z - b.z);

            for (const p of points) {
                if (p.brightness > 0) {
                    const hue = (p.u * 10 + p.v * 10 + t * 20) % 360;
                    const alpha = p.brightness * 0.8;
                    ctx.fillStyle = `hsla(${hue}, 70%, 60%, ${alpha})`;
                    ctx.beginPath();
                    ctx.arc(p.x, p.y, 2.5, 0, Math.PI * 2);
                    ctx.fill();
                }
            }

            for (let i = 0; i < res; i++) {
                for (let j = 0; j < res; j++) {
                    const idx = i * (res + 1) + j;
                    const p1 = points[idx];
                    const p2 = points[idx + 1];
                    const p3 = points[idx + res + 1];

                    if (p1 && p2 && p1.brightness > 0.2 && p2.brightness > 0.2) {
                        ctx.strokeStyle = `rgba(100, 200, 255, ${Math.min(p1.brightness, p2.brightness) * 0.3})`;
                        ctx.lineWidth = 0.5;
                        ctx.beginPath();
                        ctx.moveTo(p1.x, p1.y);
                        ctx.lineTo(p2.x, p2.y);
                        ctx.stroke();
                    }

                    if (p1 && p3 && p1.brightness > 0.2 && p3.brightness > 0.2) {
                        ctx.strokeStyle = `rgba(100, 200, 255, ${Math.min(p1.brightness, p3.brightness) * 0.3})`;
                        ctx.lineWidth = 0.5;
                        ctx.beginPath();
                        ctx.moveTo(p1.x, p1.y);
                        ctx.lineTo(p3.x, p3.y);
                        ctx.stroke();
                    }
                }
            }

            t += 0.01;
            rotation.y += 0.003;
            requestAnimationFrame(draw);
        }

        draw();
    </script>
</body>
</html>