ARCANA | The Digital Alchemist’s Altar https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js :root { –color-obsidian: #050505; –color-ethereal-gold: #c5a059; –color-mystic-purple: #2d1b4e; } body, html { margin: 0; padding: 0; width: 100%; height: 100%; background-color: var(–color-obsidian); color: #ffffff; font-family: ‘Cinzel’, serif; overflow: hidden; display: flex; justify-content: center; align-items: center; } #canvas-container { width: 100%; height: 100%; position: absolute; z-index: 1; } .ui-overlay { position: relative; z-index: 10; text-align: center; pointer-events: none; display: flex; flex-direction: column; justify-content: space-between; height: 80vh; width: 100%; } h1 { font-size: 3rem; letter-spacing: 0.5em; color: var(–color-ethereal-gold); margin-top: 50px; opacity: 0; } .controls { margin-bottom: 50px; pointer-events: auto; } button { background: rgba(255, 255, 255, 0.05); backdrop-filter: blur(10px); border: 1px solid rgba(197, 160, 89, 0.3); color: var(–color-ethereal-gold); padding: 15px 40px; font-size: 0.8rem; letter-spacing: 0.3em; text-transform: uppercase; cursor: pointer; transition: all 0.5s ease; margin: 0 10px; } button:hover { background: rgba(197, 160, 89, 0.1); border-color: var(–color-ethereal-gold); box-shadow: 0 0 20px rgba(197, 160, 89, 0.2); } .loader { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: #050505; display: flex; justify-content: center; align-items: center; z-index: 100; transition: opacity 1s ease; } .loader.hidden { opacity: 0; pointer-events: none; } .status-text { color: var(–color-ethereal-gold); font-size: 0.7rem; letter-spacing: 1em; animation: pulse 2s infinite; } @keyframes pulse { 0%, 100% { opacity: 0.3; } 50% { opacity: 0.8; } }
INVOKING ARCANA…

ARCANA

// Three.js Scene Setup const container = document.getElementById(‘canvas-container’); const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 1000); const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); container.appendChild(renderer.domElement); camera.position.z = 8; // Lights const ambientLight = new THREE.AmbientLight(0xffffff, 0.4); scene.add(ambientLight); const pointLight = new THREE.PointLight(0xffffff, 1); pointLight.position.set(5, 5, 5); scene.add(pointLight); const spotLight = new THREE.SpotLight(0x8a2be2, 2); spotLight.position.set(-5, 5, 5); scene.add(spotLight); // Card Geometry const cardGeometry = new THREE.BoxGeometry(2.2, 3.8, 0.05); const materials = [ new THREE.MeshStandardMaterial({ color: 0x0a0a0a }), // Right new THREE.MeshStandardMaterial({ color: 0x0a0a0a }), // Left new THREE.MeshStandardMaterial({ color: 0x0a0a0a }), // Top new THREE.MeshStandardMaterial({ color: 0x0a0a0a }), // Bottom new THREE.MeshStandardMaterial({ color: 0x1a1a1a, emissive: 0x2d1b4e, emissiveIntensity: 0.2 }), // Front new THREE.MeshStandardMaterial({ color: 0x0a0a0a, emissive: 0xc5a059, emissiveIntensity: 0.1 }) // Back ]; const card = new THREE.Mesh(cardGeometry, materials); scene.add(card); // Particle System const particleCount = 1500; const particleGeometry = new THREE.BufferGeometry(); const positions = new Float32Array(particleCount * 3); const velocities = new Float32Array(particleCount * 3); for (let i = 0; i { mouseX = (e.clientX / window.innerWidth) – 0.5; mouseY = (e.clientY / window.innerHeight) – 0.5; }); // Animations let isFlipped = false; let isDissipated = false; document.getElementById(‘flip-btn’).addEventListener(‘click’, () => { isFlipped = !isFlipped; gsap.to(card.rotation, { y: isFlipped ? Math.PI : 0, duration: 1.2, ease: “back.out(1.7)” }); document.getElementById(‘flip-btn’).innerText = isFlipped ? “Reset” : “Reveal Fate”; }); document.getElementById(‘dissipate-btn’).addEventListener(‘click’, () => { if (isDissipated) return; isDissipated = true; // Fade out card gsap.to(card.material, { opacity: 0, duration: 1, stagger: 0.1, onComplete: () => { card.visible = false; } }); // Trigger Particles particleMaterial.opacity = 0.8; gsap.to(particleMaterial, { opacity: 0, duration: 2.5, delay: 1, ease: “power2.inOut”, onComplete: () => { card.visible = true; gsap.to(card.material, { opacity: 1, duration: 1 }); isDissipated = false; isFlipped = false; card.rotation.y = 0; document.getElementById(‘flip-btn’).innerText = “Reveal Fate”; } }); }); // Loop function animate() { requestAnimationFrame(animate); if (!isFlipped && !isDissipated) { card.rotation.x += (mouseY * 0.5 – card.rotation.x) * 0.1; card.rotation.y += (mouseX * 0.5 – card.rotation.y) * 0.1; } if (isDissipated) { const pos = particleGeometry.attributes.position.array; for (let i = 0; i { setTimeout(() => { document.getElementById(‘loader’).classList.add(‘hidden’); gsap.to(‘#main-title’, { opacity: 1, y: -20, duration: 1.5, delay: 0.5, ease: “power3.out” }); animate(); }, 1500); }; window.addEventListener(‘resize’, () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); });