diff --git a/projecthearthstone.in/Projects/index.html b/projecthearthstone.in/Projects/index.html new file mode 100644 index 0000000..4ddd083 --- /dev/null +++ b/projecthearthstone.in/Projects/index.html @@ -0,0 +1,60 @@ + + + + + + Project Hearthstone - Projects + + + + + + + + + + + + +
+ + +
+
+

+ PROJECT HEARTHSTONE +

+

Bridging Worlds Through AI

+
+
+
+ + +
+ +
+
+ Project Preview +
+
+
+ + +
+
+ +
+
+
+ + + + diff --git a/projecthearthstone.in/Projects/script.js b/projecthearthstone.in/Projects/script.js new file mode 100644 index 0000000..45a301b --- /dev/null +++ b/projecthearthstone.in/Projects/script.js @@ -0,0 +1,309 @@ +// Project Data +const projects = [ + { + id: 2, + title: "Hearthstone AI", + shortDesc: + "Our flagship AI model with 99.7% accuracy, supporting fingerspelling and speech-to-text across Python and JavaScript frameworks, optimized for Raspberry Pi.", + image: "../src/hearthstone-ai.jpg", + specs: [ + "Accuracy: 99.7%", + "Frameworks: Python, JavaScript", + "Platform: Raspberry Pi Optimized", + "Features: Fingerspelling + Speech-to-Text", + ], + techStack: [ + "TensorFlow", + "MediaPipe", + "Python", + "JavaScript", + "Raspberry Pi", + ], + description: + "Hearthstone AI is the heart of our project — a high-accuracy model that understands both hand signs and spoken words. Built on TensorFlow and MediaPipe, it runs smoothly on a Raspberry Pi and works seamlessly in both Python backends and JavaScript frontends. We're actively developing word gesture recognition to move beyond isolated signs.", + }, + { + id: 1, + title: "Hearthstone Glove", + shortDesc: + "Early prototype using Arduino and flex sensors to translate ASL fingerspelling.", + image: "../src/hearthstone-glove.jpg", + specs: [ + "Hardware: Arduino Uno", + "Sensors: 5 Flex Sensors", + "Detection: Fingerspelling Only", + "Status: Legacy Prototype", + ], + techStack: ["Arduino C", "Flex Sensors", "Analog Circuits"], + description: + "Our first iteration — a hardware-based glove that captured finger bends to recognize fingerspelling letters. Though it laid the foundation, we moved on to a more powerful AI-driven approach.", + }, + { + id: 3, + title: "Hearthstone ASL Course", + shortDesc: + "Gamified learning platform that teaches ASL through interactive lessons, progression tracking, and a complete course system built with HTML, CSS, and JavaScript.", + image: "../src/hearthstone-asl-course.jpg", + specs: [ + "Tech Stack: HTML/CSS/JS", + "Gamification: XP, Achievements, Levels", + "Course Structure: Modular", + "Progress Tracking: Yes", + ], + techStack: ["HTML5", "CSS3", "JavaScript", "Gamification"], + description: + "Learning ASL should be fun. Our ASL Course platform turns education into a game — complete lessons, earn XP, unlock achievements, and track your progression as you build real signing skills. Built entirely with web technologies for maximum accessibility.", + }, +]; + +// Initialize on DOM Ready +document.addEventListener("DOMContentLoaded", () => { + initializeProgressBar(); + initializeProjects(); + initParticles(); // Add particle system +}); + +// ========== PROGRESS BAR ========== +function initializeProgressBar() { + const progressBar = document.getElementById("progressBar"); + if (!progressBar) return; + + window.addEventListener("scroll", () => { + const scrollTop = window.scrollY; + const docHeight = + document.documentElement.scrollHeight - window.innerHeight; + const progress = (scrollTop / docHeight) * 100; + progressBar.style.width = `${progress}%`; + }); +} + +// ========== PROJECTS INITIALIZATION ========== +function initializeProjects() { + const track = document.getElementById("projectsTrack"); + if (!track) return; + + // Generate project panels + projects.forEach((project, index) => { + const panel = createProjectPanel(project, index); + track.appendChild(panel); + }); + + // Initialize Intersection Observer after a slight delay to ensure DOM is ready + setTimeout(() => { + initializeIntersectionObserver(); + }, 100); +} + +// Create individual project panel DOM +function createProjectPanel(project, index) { + const panel = document.createElement("div"); + panel.className = "project-panel"; + panel.dataset.projectId = project.id; + + // Spec sheet items + const specsHtml = project.specs + .map((spec) => `
${spec}
`) + .join(""); + + // Tech pills + const techPillsHtml = project.techStack + .map((tech) => `${tech}`) + .join(""); + + panel.innerHTML = ` +

${project.title}

+

${project.description}

+ +
+ ${specsHtml} +
+ +
+ ${techPillsHtml} +
+ +
+ View Demo + GitHub +
+ `; + + return panel; +} + +// ========== INTERSECTION OBSERVER (Project Switch) ========== +function initializeIntersectionObserver() { + const panels = document.querySelectorAll(".project-panel"); + const stickyImage = document.getElementById("stickyImage"); + const imageGlow = document.querySelector(".image-glow"); + + if (panels.length === 0 || !stickyImage) return; + + // Set initial image + if (projects.length > 0) { + updateStickyImage(stickyImage, projects[0].image); + } + + const observerOptions = { + root: null, + rootMargin: "-30% 0px -30% 0px", + threshold: 0.1, + }; + + const observer = new IntersectionObserver((entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + const projectId = parseInt(entry.target.dataset.projectId); + const project = projects.find((p) => p.id === projectId); + + if (project) { + // Update active state + panels.forEach((p) => p.classList.remove("active")); + entry.target.classList.add("active"); + + // Trigger glitch-scale transition on image + triggerImageTransition(stickyImage, project.image); + } + } + }); + }, observerOptions); + + panels.forEach((panel) => observer.observe(panel)); +} + +// Update sticky image with glitch effect +function triggerImageTransition(imageElement, newSrc) { + imageElement.classList.remove("active"); + imageElement.classList.add("glitch-effect"); // Add glitch animation + + setTimeout(() => { + updateStickyImage(imageElement, newSrc); + imageElement.classList.remove("glitch-effect"); + imageElement.classList.add("active"); + }, 400); // Glitch duration 400ms +} + +// Helper: Update image src +function updateStickyImage(imageElement, src) { + // For now, use a gradient placeholder since images don't exist + // In production, this would set imageElement.src = src; + imageElement.style.background = generatePlaceholderGradient(); + imageElement.src = + 'data:image/svg+xml,%3Csvg width="500" height="300" xmlns="http://www.w3.org/2000/svg"%3E%3Crect width="500" height="300" fill="%230a0a0a"/%3E%3Ctext x="50%25" y="50%25" font-family="Arial, sans-serif" font-size="24" fill="%2300D4FF" text-anchor="middle"%3EProject Preview%3C/text%3E%3C/svg%3E'; +} + +// Generate a cool placeholder gradient +function generatePlaceholderGradient() { + const gradients = [ + "linear-gradient(135deg, #00D4FF 0%, #0066ff 100%)", + "linear-gradient(135deg, #ff00d4 0%, #00D4FF 100%)", + "linear-gradient(135deg, #00ffaa 0%, #00D4FF 100%)", + "linear-gradient(135deg, #ff0066 0%, #ff00d4 100%)", + "linear-gradient(135deg, #6600ff 0%, #ff00d4 100%)", + ]; + return gradients[Math.floor(Math.random() * gradients.length)]; +} + +// ========== NEURAL MESH PARTICLES ========== +function initParticles() { + const canvas = document.createElement("canvas"); + canvas.id = "particleCanvas"; + canvas.style.position = "fixed"; + canvas.style.top = "0"; + canvas.style.left = "0"; + canvas.style.width = "100%"; + canvas.style.height = "100%"; + canvas.style.zIndex = "-1"; + canvas.style.pointerEvents = "none"; + document.body.appendChild(canvas); + + const ctx = canvas.getContext("2d"); + let width, height; + const particles = []; + const particleCount = 70; // 60-80 nodes + const connectionDistance = 120; + const mouseRepelDist = 150; + const mouse = {x: null, y: null}; + + function resize() { + width = window.innerWidth; + height = window.innerHeight; + canvas.width = width; + canvas.height = height; + } + + window.addEventListener("resize", resize); + + window.addEventListener("mousemove", (e) => { + mouse.x = e.clientX; + mouse.y = e.clientY; + }); + + window.addEventListener("mouseout", () => { + mouse.x = null; + mouse.y = null; + }); + + // Initialize size BEFORE creating particles + resize(); + + // Create particles with valid positions + for (let i = 0; i < particleCount; i++) { + particles.push({ + x: Math.random() * width, + y: Math.random() * height, + vx: (Math.random() - 0.5) * 0.5, + vy: (Math.random() - 0.5) * 0.5, + size: Math.random() * 2 + 2, // 2-4px + }); + } + + function update() { + ctx.clearRect(0, 0, width, height); + ctx.strokeStyle = "rgba(0, 212, 255, 0.4)"; // line opacity + ctx.fillStyle = "rgba(0, 212, 255, 0.9)"; // particle opacity + + particles.forEach((p) => { + p.x += p.vx; + p.y += p.vy; + + if (p.x < 0 || p.x > width) p.vx *= -1; + if (p.y < 0 || p.y > height) p.vy *= -1; + + if (mouse.x != null) { + const dx = p.x - mouse.x; + const dy = p.y - mouse.y; + const dist = Math.sqrt(dx * dx + dy * dy); + if (dist < mouseRepelDist) { + const force = (mouseRepelDist - dist) / mouseRepelDist; + p.x += (dx / dist) * force * 5; + p.y += (dy / dist) * force * 5; + } + } + + ctx.beginPath(); + ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2); + ctx.fill(); + }); + + // Draw connections + for (let i = 0; i < particles.length; i++) { + for (let j = i + 1; j < particles.length; j++) { + const dx = particles[i].x - particles[j].x; + const dy = particles[i].y - particles[j].y; + const dist = Math.sqrt(dx * dx + dy * dy); + if (dist < connectionDistance) { + ctx.lineWidth = 1 - dist / connectionDistance; + ctx.beginPath(); + ctx.moveTo(particles[i].x, particles[i].y); + ctx.lineTo(particles[j].x, particles[j].y); + ctx.stroke(); + } + } + } + + requestAnimationFrame(update); + } + + update(); +} diff --git a/projecthearthstone.in/Projects/style.css b/projecthearthstone.in/Projects/style.css new file mode 100644 index 0000000..9352b51 --- /dev/null +++ b/projecthearthstone.in/Projects/style.css @@ -0,0 +1,593 @@ +:root { + --bg-primary: #020204; /* Deep Obsidian */ + --accent-blue: #00d4ff; /* Electric Cyan */ + --signal-green: #00ff41; /* Phosphor-Green */ + --text-primary: #ffffff; + --text-secondary: #a0a0a0; + --border-color: #1a1a1a; + --accent-glow: rgba(0, 212, 255, 0.4); + --signal-glow: rgba(0, 255, 65, 0.4); +} + +body { + background-color: var(--bg-primary); + color: var(--text-primary); + overflow-x: hidden; + font-family: "Montserrat", sans-serif; +} + +/* ========== PROGRESS BAR ========== */ +.progress-bar { + position: fixed; + top: 0; + left: 0; + height: 3px; + width: 0%; + background: linear-gradient(90deg, var(--accent-blue), #00ffaa); + z-index: 2000; + transition: width 0.1s ease-out; + box-shadow: 0 0 15px var(--accent-blue); +} + +/* ========== HERO SECTION ========== */ +.hero { + height: 100vh; + display: flex; + align-items: center; + justify-content: center; + position: relative; + overflow: hidden; +} + +.hero-content { + text-align: center; + z-index: 1; +} + +.hero-title { + font-size: clamp(2.5rem, 8vw, 7rem); + font-weight: 900; + letter-spacing: 0.1em; + margin-bottom: 1rem; + font-family: "Orbitron", sans-serif; +} + +.shimmer { + background: linear-gradient( + 90deg, + var(--accent-blue) 0%, + #ffffff 25%, + var(--accent-blue) 50%, + #ffffff 75%, + var(--accent-blue) 100% + ); + background-size: 200% auto; + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + animation: shimmerAnim 4s linear infinite; +} + +@keyframes shimmerAnim { + 0% { + background-position: 200% center; + } + 100% { + background-position: -200% center; + } +} + +.hero-subtitle { + font-size: clamp(1rem, 2.5vw, 1.5rem); + color: var(--text-secondary); + letter-spacing: 0.3em; + text-transform: uppercase; + margin-bottom: 2rem; +} + +.hero-divider { + width: 200px; + height: 2px; + background: linear-gradient( + 90deg, + transparent, + var(--accent-blue), + transparent + ); + margin: 0 auto; +} + +/* ========== PROJECTS SPLIT VIEW ========== */ +.projects-container { + display: grid; + grid-template-columns: 1fr 1fr; + min-height: 100vh; + position: relative; + background: linear-gradient( + to bottom, + rgba(0, 10, 30, 0) 0%, + rgba(0, 10, 30, 0.5) 20%, + rgba(0, 10, 30, 0.5) 100% + ); +} + +.image-column { + position: relative; + height: 100vh; + position: sticky; + top: 0; + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient( + 135deg, + rgba(0, 212, 255, 0.05) 0%, + transparent 50% + ); + border-right: 1px solid var(--border-color); + z-index: 10; +} + +.sticky-image-wrapper { + position: relative; + width: 80%; + max-width: 500px; + aspect-ratio: 16/9; + display: flex; + align-items: center; + justify-content: center; +} + +.sticky-image { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 12px; + box-shadow: 0 0 40px rgba(0, 212, 255, 0.3); + transition: + filter 0.3s ease, + transform 0.3s ease; +} + +.sticky-image.active { + box-shadow: 0 0 50px var(--accent-glow); +} + +.image-glow { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 120%; + height: 120%; + background: radial-gradient(ellipse, var(--accent-glow) 0%, transparent 70%); + opacity: 0.6; + filter: blur(30px); + z-index: -1; + animation: glowPulse 3s ease-in-out infinite; +} + +@keyframes glowPulse { + 0%, + 100% { + opacity: 0.4; + transform: translate(-50%, -50%) scale(1); + } + 50% { + opacity: 0.7; + transform: translate(-50%, -50%) scale(1.1); + } +} + +/* Glitch-Scale Transition */ +.glitch-target { + filter: blur(10px); + transform: scale(0.95); + opacity: 0.8; +} + +.glitch-target.active { + filter: blur(0); + transform: scale(1); + opacity: 1; + transition: + filter 0.4s cubic-bezier(0.34, 1.56, 0.64, 1), + transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); +} + +/* Glitch effect for data swap */ +.glitch-effect { + animation: glitch 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94) both; +} + +@keyframes glitch { + 0% { + clip-path: inset(5% 0 85% 0); + transform: translateX(-2px); + filter: contrast(150%) drop-shadow(-2px 0 rgba(0, 212, 255, 0.8)) + drop-shadow(2px 0 rgba(255, 0, 212, 0.8)); + } + 20% { + clip-path: inset(25% 0 60% 0); + transform: translateX(2px); + filter: contrast(150%) drop-shadow(2px 0 rgba(0, 212, 255, 0.8)) + drop-shadow(-2px 0 rgba(255, 0, 212, 0.8)); + } + 40% { + clip-path: inset(45% 0 40% 0); + transform: translateX(-1px); + filter: contrast(150%) drop-shadow(-1px 0 rgba(0, 212, 255, 0.9)) + drop-shadow(1px 0 rgba(255, 0, 212, 0.9)); + } + 60% { + clip-path: inset(55% 0 30% 0); + transform: translateX(1px); + filter: contrast(150%) drop-shadow(1px 0 rgba(0, 212, 255, 0.7)) + drop-shadow(-1px 0 rgba(255, 0, 212, 0.7)); + } + 80% { + clip-path: inset(65% 0 20% 0); + transform: translateX(-2px); + filter: contrast(150%) drop-shadow(-2px 0 rgba(0, 212, 255, 0.8)) + drop-shadow(2px 0 rgba(255, 0, 212, 0.8)); + } + 100% { + clip-path: inset(0 0 0 0); + transform: translateX(0); + filter: contrast(100%); + } +} + +/* ========== CONTENT COLUMN ========== */ +.content-column { + padding: 4rem 3rem 6rem; + background: transparent; + backdrop-filter: blur(20px); + display: flex; + flex-direction: column; + justify-content: flex-start; +} + +.projects-track { + display: flex; + flex-direction: column; + gap: 6rem; +} + +.project-panel { + min-height: 80vh; + display: flex; + flex-direction: column; + justify-content: center; + opacity: 0.3; + transition: + opacity 0.5s ease, + transform 0.5s ease; + transform: translateY(30px); +} + +.project-panel.active { + opacity: 1; + transform: translateY(0); +} + +/* Header */ +.project-panel.active h2 { + opacity: 0; + transform: translateY(20px); + animation: + fadeInUp 0.6s ease forwards, + breathe 4s ease-in-out infinite 0.6s; + color: var(--accent-blue); + letter-spacing: 0.02em; + margin-bottom: 1rem; +} + +.project-panel.active h2::after { + content: ""; + display: block; + width: 60px; + height: 3px; + background: var(--signal-green); + margin: 0.5rem 0 0; + border-radius: 2px; + box-shadow: 0 0 10px var(--signal-green); +} + +@keyframes fadeInUp { + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes breathe { + 0%, + 100% { + letter-spacing: 0.02em; + } + 50% { + letter-spacing: 0.05em; + } +} + +/* Description */ +.project-panel .description { + font-size: 1.1rem; + line-height: 1.7; + color: var(--text-secondary); + margin-bottom: 2rem; + max-width: 600px; + opacity: 0; + transform: translateY(10px); + animation: fadeInUp 0.6s ease forwards 0.2s; +} + +/* Spec sheet - glass effect */ +.spec-sheet { + margin: 2rem 0; + padding: 1.5rem; + background: rgba(255, 255, 255, 0.03); + backdrop-filter: blur(12px); + border-radius: 0 8px 8px 0; + border-left: 2px solid var(--signal-green); + position: relative; + overflow: hidden; + box-shadow: + inset 0 0 0 1px rgba(0, 212, 255, 0.2), + 0 0 15px rgba(0, 212, 255, 0.05); +} + +.spec-item { + font-family: "Courier New", monospace; + font-size: 0.95rem; + color: var(--text-secondary); + margin: 0.75rem 0; + padding-left: 1.5rem; + position: relative; + opacity: 0; + transform: translateX(-10px); +} + +/* Staggered slide-in from right */ +.project-panel.active .spec-item { + animation: slideInRight 0.4s ease forwards; +} + +.project-panel.active .spec-item:nth-child(1) { + animation-delay: 0.3s; +} +.project-panel.active .spec-item:nth-child(2) { + animation-delay: 0.4s; +} +.project-panel.active .spec-item:nth-child(3) { + animation-delay: 0.5s; +} +.project-panel.active .spec-item:nth-child(4) { + animation-delay: 0.6s; +} + +@keyframes slideInRight { + to { + opacity: 1; + transform: translateX(0); + } +} + +.spec-item::before { + content: ">"; + position: absolute; + left: 0; + color: var(--signal-green); + font-weight: bold; + animation: caretBlink 1.5s infinite; +} + +@keyframes caretBlink { + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.3; + } +} + +/* Tech stack pills */ +.tech-stack { + display: flex; + flex-wrap: wrap; + gap: 0.75rem; + margin: 1.5rem 0; +} + +.tech-pill { + padding: 0.5rem 1rem; + background: transparent; + border: 1px solid var(--accent-blue); + border-radius: 999px; + font-size: 0.85rem; + color: var(--accent-blue); + font-weight: 500; + position: relative; + overflow: hidden; + transition: all 0.3s ease; + cursor: default; +} + +/* Border-trace effect */ +.tech-pill::before { + content: ""; + position: absolute; + inset: 0; + padding: 1px; + border-radius: inherit; + background: conic-gradient( + from 0deg, + transparent 0deg, + var(--signal-green) 20deg, + transparent 40deg, + transparent 320deg, + var(--signal-green) 340deg, + transparent 360deg + ); + -webkit-mask: + linear-gradient(#fff 0 0) content-box, + linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + opacity: 0; + transition: opacity 0.3s; + animation: rotate 3s linear infinite; + pointer-events: none; +} + +.tech-pill:hover::before { + opacity: 1; +} + +@keyframes rotate { + to { + transform: rotate(360deg); + } +} + +.tech-pill:hover { + box-shadow: + 0 0 20px var(--accent-glow), + inset 0 0 10px rgba(0, 212, 255, 0.2); + transform: translateY(-2px); +} + +/* Buttons */ +.button-group { + display: flex; + gap: 1rem; + margin-top: 2rem; +} + +.project-btn { + padding: 0.875rem 2rem; + font-size: 1rem; + font-weight: 600; + border: none; + border-radius: 8px; + cursor: pointer; + transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); + text-decoration: none; + opacity: 0; + transform: translateY(20px); +} + +.project-panel.active .project-btn { + animation: springIn 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) forwards; +} + +.project-btn:nth-child(2) { + animation-delay: 0.3s !important; +} + +@keyframes springIn { + 0% { + opacity: 0; + transform: translateY(20px) scale(0.9); + } + 60% { + opacity: 1; + transform: translateY(-5px) scale(1.05); + } + 100% { + opacity: 1; + transform: translateY(0) scale(1); + } +} + +.btn-primary { + background: var(--accent-blue); + color: #000; +} + +.btn-primary:hover { + background: #00ffe0; + box-shadow: 0 0 30px var(--accent-glow); + transform: translateY(-3px); +} + +.btn-secondary { + background: transparent; + border: 2px solid var(--accent-blue); + color: var(--accent-blue); +} + +.btn-secondary:hover { + background: rgba(0, 212, 255, 0.1); + box-shadow: 0 0 20px var(--accent-glow); + transform: translateY(-3px); +} + +/* GitHub button border pulse when panel active */ +.project-panel.active .btn-secondary { + animation: borderPulse 2s ease-in-out infinite 0.6s; +} + +@keyframes borderPulse { + 0%, + 100% { + box-shadow: 0 0 5px var(--signal-glow); + border-color: var(--signal-green); + } + 50% { + box-shadow: 0 0 20px var(--signal-glow); + border-color: #00ff88; + } +} + +/* Responsive */ +@media (max-width: 1024px) { + .projects-container { + grid-template-columns: 1fr; + } + + .image-column { + height: 50vh; + position: relative; + top: 0; + border-right: none; + border-bottom: 1px solid var(--border-color); + } + + .sticky-image-wrapper { + width: 90%; + } + + .content-column { + padding: 2rem 1.5rem 4rem; + } +} + +@media (max-width: 600px) { + .hero-title { + letter-spacing: 0.05em; + } + + .button-group { + flex-direction: column; + } + + .project-btn { + width: 100%; + text-align: center; + } +} + +/* Particle canvas */ +#particleCanvas { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: -1; + opacity: 0.4; + pointer-events: none; +}