Particle backgrounds can transform a mundane interface into a visually captivating experience. These dynamic elements add depth and interactivity, making your design stand out.
In this article, we explore ten stunning particle background examples that can elevate your UI design. Each example showcases unique ways to integrate particles seamlessly into your projects.
CODE1
Here's the code:
CODETEXT1
CODE2
Here's the code:
CODETEXT2
CODE3
Here's the code:
CODETEXT3
CODE4
Here's the code:
CODETEXT4
CODE5
Here's the code:
CODETEXT5
Subframe's drag-and-drop interface and intuitive, responsive canvas make designing particle backgrounds a breeze. Loved by designers and developers alike, it ensures pixel-perfect UI every time.
Ready to elevate your designs? Start for free today!
CODE6
Here's the code:
CODETEXT6
CODE7
Here's the code:
CODETEXT7
CODE8
Here's the code:
CODETEXT8
CODE9
Here's the code:
CODETEXT9
CODE10
Here's the code:
CODETEXT10
Unlock the power of Subframe to design stunning UIs effortlessly. With its drag-and-drop editor and beautifully crafted components, you can create pixel-perfect designs, including particle backgrounds, in no time.
Experience the efficiency and precision of Subframe. Start for free and begin creating immediately!
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Business Analytics Dashboard</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; } body { width: 100%; height: 100vh; background: linear-gradient(135deg, #1a2436 0%, #2a3c56 100%); color: #f5f5f7; overflow: hidden; } #particle-container { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 1; } .dashboard-container { width: 100%; height: 100%; padding: 20px; display: flex; flex-direction: column; position: relative; z-index: 2; } .dashboard-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; } .dashboard-title { font-size: 24px; font-weight: 600; text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); } .date-selector { background: rgba(255, 255, 255, 0.08); border-radius: 8px; padding: 8px 12px; border: 1px solid rgba(255, 255, 255, 0.12); display: flex; align-items: center; cursor: pointer; transition: all 0.3s ease; } .date-selector:hover { background: rgba(255, 255, 255, 0.15); } .date-selector span { margin-right: 8px; font-size: 14px; opacity: 0.9; } .date-selector i { font-size: 16px; opacity: 0.7; } .metrics-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 20px; margin-bottom: 20px; } .metric-card { background: rgba(255, 255, 255, 0.06); border-radius: 12px; padding: 20px; border: 1px solid rgba(255, 255, 255, 0.08); transition: all 0.3s ease; cursor: pointer; position: relative; overflow: hidden; } .metric-card:hover { background: rgba(255, 255, 255, 0.1); transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); } .metric-card::after { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 3px; background: linear-gradient(90deg, #4a8cff, #73b0ff); transform: scaleX(0); transform-origin: left; transition: transform 0.4s ease; } .metric-card:hover::after { transform: scaleX(1); } .metric-label { font-size: 14px; color: rgba(255, 255, 255, 0.7); margin-bottom: 10px; } .metric-value { font-size: 28px; font-weight: 600; margin-bottom: 5px; } .metric-change { font-size: 14px; display: flex; align-items: center; } .positive { color: #5cd6a0; } .negative { color: #ff6b6b; } .charts-container { display: grid; grid-template-columns: 2fr 1fr; gap: 20px; flex-grow: 1; } .chart-card { background: rgba(255, 255, 255, 0.04); border-radius: 12px; padding: 20px; border: 1px solid rgba(255, 255, 255, 0.08); display: flex; flex-direction: column; transition: all 0.3s ease; } .chart-card:hover { background: rgba(255, 255, 255, 0.08); } .chart-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; } .chart-title { font-size: 16px; font-weight: 500; } .chart-legend { display: flex; gap: 15px; } .legend-item { display: flex; align-items: center; font-size: 12px; color: rgba(255, 255, 255, 0.7); } .legend-color { width: 10px; height: 10px; border-radius: 2px; margin-right: 6px; } .chart-container { flex-grow: 1; position: relative; overflow: hidden; display: flex; align-items: center; justify-content: center; } .actions-container { margin-top: auto; display: flex; justify-content: flex-end; gap: 10px; } .action-button { background: rgba(255, 255, 255, 0.1); border: none; border-radius: 6px; padding: 8px 15px; color: white; cursor: pointer; font-size: 14px; transition: all 0.3s ease; display: flex; align-items: center; } .action-button i { margin-right: 6px; font-size: 16px; } .action-button:hover { background: rgba(255, 255, 255, 0.2); } .primary-button { background: linear-gradient(90deg, #4a8cff, #73b0ff); } .primary-button:hover { background: linear-gradient(90deg, #5a95ff, #80b8ff); } #data-refresh-btn { position: relative; } #data-refresh-btn i { transition: transform 0.5s ease; } #data-refresh-btn:hover i { transform: rotate(180deg); } .bar-chart { width: 100%; height: 200px; display: flex; align-items: flex-end; gap: 15px; padding-top: 20px; } .bar { flex-grow: 1; background: linear-gradient(to top, rgba(74, 140, 255, 0.8), rgba(115, 176, 255, 0.3)); border-radius: 4px 4px 0 0; position: relative; transition: height 0.5s ease; cursor: pointer; } .bar:hover { background: linear-gradient(to top, rgba(90, 155, 255, 0.9), rgba(130, 186, 255, 0.4)); } .bar-label { position: absolute; bottom: -25px; left: 50%; transform: translateX(-50%); font-size: 12px; color: rgba(255, 255, 255, 0.7); white-space: nowrap; } .bar-value { position: absolute; top: -25px; left: 50%; transform: translateX(-50%); font-size: 12px; color: rgba(255, 255, 255, 0.9); opacity: 0; transition: opacity 0.3s ease; } .bar:hover .bar-value { opacity: 1; } .donut-chart { width: 160px; height: 160px; position: relative; } .donut-hole { width: 70%; height: 70%; border-radius: 50%; background: rgba(26, 36, 54, 0.7); position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); display: flex; align-items: center; justify-content: center; flex-direction: column; } .donut-value { font-size: 22px; font-weight: 600; margin-bottom: 5px; } .donut-label { font-size: 12px; color: rgba(255, 255, 255, 0.7); } .segment { position: absolute; top: 0; left: 0; width: 100%; height: 100%; border-radius: 50%; clip: rect(0px, 160px, 160px, 80px); } .insights-list { padding: 10px 0; } .insight-item { display: flex; padding: 10px 0; border-bottom: 1px solid rgba(255, 255, 255, 0.1); transition: all 0.3s ease; cursor: pointer; } .insight-item:last-child { border-bottom: none; } .insight-item:hover { background: rgba(255, 255, 255, 0.05); padding-left: 5px; } .insight-icon { width: 30px; height: 30px; background: rgba(74, 140, 255, 0.2); border-radius: 6px; display: flex; align-items: center; justify-content: center; margin-right: 12px; flex-shrink: 0; } .insight-icon i { color: #4a8cff; font-size: 14px; } .insight-content { flex-grow: 1; } .insight-title { font-size: 14px; font-weight: 500; margin-bottom: 4px; } .insight-desc { font-size: 12px; color: rgba(255, 255, 255, 0.6); } @media (max-width: 900px) { .charts-container { grid-template-columns: 1fr; } } @media (max-width: 600px) { .metrics-grid { grid-template-columns: 1fr 1fr; } .dashboard-header { flex-direction: column; align-items: flex-start; gap: 10px; } } @keyframes pulse { 0% { transform: scale(1); opacity: 0.7; } 50% { transform: scale(1.05); opacity: 0.9; } 100% { transform: scale(1); opacity: 0.7; } } .pulse-animation { animation: pulse 2s infinite ease-in-out; } </style> </head> <body> <div id="particle-container"></div> <div class="dashboard-container"> <div class="dashboard-header"> <h1 class="dashboard-title">Enterprise Performance Navigator</h1> <div class="date-selector"> <span>Last 30 Days</span> <i>↓</i> </div> </div> <div class="metrics-grid"> <div class="metric-card"> <div class="metric-label">Conversion Rate</div> <div class="metric-value">3.75%</div> <div class="metric-change positive">+0.6% <span style="margin-left: 5px;">↑</span></div> </div> <div class="metric-card"> <div class="metric-label">Avg. Session Duration</div> <div class="metric-value">4:23</div> <div class="metric-change positive">+1:12 <span style="margin-left: 5px;">↑</span></div> </div> <div class="metric-card"> <div class="metric-label">Customer Acquisition Cost</div> <div class="metric-value">$42.15</div> <div class="metric-change negative">+$3.28 <span style="margin-left: 5px;">↑</span></div> </div> <div class="metric-card"> <div class="metric-label">Revenue MoM Growth</div> <div class="metric-value">18.3%</div> <div class="metric-change positive">+2.7% <span style="margin-left: 5px;">↑</span></div> </div> </div> <div class="charts-container"> <div class="chart-card"> <div class="chart-header"> <div class="chart-title">Weekly Revenue Breakdown</div> <div class="chart-legend"> <div class="legend-item"> <div class="legend-color" style="background: rgba(74, 140, 255, 0.8);"></div> <span>Current Period</span> </div> <div class="legend-item"> <div class="legend-color" style="background: rgba(255, 255, 255, 0.2);"></div> <span>Previous Period</span> </div> </div> </div> <div class="chart-container"> <div class="bar-chart"> <div class="bar" style="height: 60%;"> <div class="bar-value">$23.4K</div> <div class="bar-label">Mon</div> </div> <div class="bar" style="height: 80%;"> <div class="bar-value">$31.2K</div> <div class="bar-label">Tue</div> </div> <div class="bar" style="height: 65%;"> <div class="bar-value">$25.1K</div> <div class="bar-label">Wed</div> </div> <div class="bar" style="height: 90%;"> <div class="bar-value">$35.8K</div> <div class="bar-label">Thu</div> </div> <div class="bar" style="height: 75%;"> <div class="bar-value">$29.5K</div> <div class="bar-label">Fri</div> </div> <div class="bar" style="height: 50%;"> <div class="bar-value">$19.7K</div> <div class="bar-label">Sat</div> </div> <div class="bar" style="height: 40%;"> <div class="bar-value">$15.2K</div> <div class="bar-label">Sun</div> </div> </div> </div> <div class="actions-container"> <button class="action-button" id="data-refresh-btn"><i>↻</i> Refresh Data</button> <button class="action-button primary-button"><i>↓</i> Export Report</button> </div> </div> <div class="chart-card"> <div class="chart-header"> <div class="chart-title">Key Performance Insights</div> </div> <div class="chart-container"> <div style="text-align: center; margin-bottom: 15px;"> <div class="donut-chart"> <div class="donut-hole"> <div class="donut-value">68%</div> <div class="donut-label">Goal Completion</div> </div> <div class="segment" style="transform: rotate(0deg); background: #4a8cff; opacity: 0.8;"></div> <div class="segment" style="transform: rotate(245deg); background: rgba(255, 255, 255, 0.1);"></div> </div> </div> <div class="insights-list"> <div class="insight-item"> <div class="insight-icon"><i>✓</i></div> <div class="insight-content"> <div class="insight-title">User Engagement Spike</div> <div class="insight-desc">Mobile sessions up 24% after latest feature launch</div> </div> </div> <div class="insight-item"> <div class="insight-icon"><i>⚠</i></div> <div class="insight-content"> <div class="insight-title">Conversion Drop on Enterprise Plan</div> <div class="insight-desc">12% decline in last week, pricing may be a factor</div> </div> </div> <div class="insight-item"> <div class="insight-icon"><i>↗</i></div> <div class="insight-content"> <div class="insight-title">Accelerating Market Growth</div> <div class="insight-desc">APAC region outperforming forecast by 31%</div> </div> </div> </div> </div> </div> </div> </div> <script> // Particle system configuration const particleConfig = { particles: [], numParticles: 50, maxConnections: 5, connectionThreshold: 150, baseSpeed: 0.2, canvas: null, ctx: null, width: 0, height: 0, density: 1, // Default density hoverArea: { x: null, y: null, active: false } }; // Initialize particle system function initParticles() { const container = document.getElementById('particle-container'); particleConfig.canvas = document.createElement('canvas'); container.appendChild(particleConfig.canvas); particleConfig.ctx = particleConfig.canvas.getContext('2d'); // Set canvas size resizeCanvas(); window.addEventListener('resize', resizeCanvas); // Create particles createParticles(); // Start animation loop animateParticles(); // Add mouse interaction document.addEventListener('mousemove', handleMouseMove); document.addEventListener('mouseleave', handleMouseLeave); // Add refresh button interaction document.getElementById('data-refresh-btn').addEventListener('click', handleDataRefresh); } function resizeCanvas() { particleConfig.width = window.innerWidth; particleConfig.height = window.innerHeight; particleConfig.canvas.width = particleConfig.width; particleConfig.canvas.height = particleConfig.height; } function createParticles() { particleConfig.particles = []; for (let i = 0; i < particleConfig.numParticles; i++) { particleConfig.particles.push({ x: Math.random() * particleConfig.width, y: Math.random() * particleConfig.height, radius: Math.random() * 2 + 1, color: `rgba(120, 180, 255, ${Math.random() * 0.2 + 0.1})`, speedX: (Math.random() - 0.5) * particleConfig.baseSpeed, speedY: (Math.random() - 0.5) * particleConfig.baseSpeed, connections: 0 }); } } function animateParticles() { particleConfig.ctx.clearRect(0, 0, particleConfig.width, particleConfig.height); // Update particle positions and draw connections updateAndDrawParticles(); requestAnimationFrame(animateParticles); } function updateAndDrawParticles() { // First pass: update positions particleConfig.particles.forEach(particle => { // Reset connection count particle.connections = 0; // Apply density factor to speed if near hover area let speedMultiplier = 1; if (particleConfig.hoverArea.active) { const dx = particle.x - particleConfig.hoverArea.x; const dy = particle.y - particleConfig.hoverArea.y; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < 100) { speedMultiplier = particleConfig.density * (1 + (100 - distance) / 100); } } // Update position particle.x += particle.speedX * speedMultiplier; particle.y += particle.speedY * speedMultiplier; // Bounce off walls if (particle.x < 0 || particle.x > particleConfig.width) { particle.speedX *= -1; } if (particle.y < 0 || particle.y > particleConfig.height) { particle.speedY *= -1; } // Draw particle particleConfig.ctx.beginPath(); particleConfig.ctx.arc(particle.x, particle.y, particle.radius, 0, Math.PI * 2); particleConfig.ctx.fillStyle = particle.color; particleConfig.ctx.fill(); }); // Second pass: draw connections particleConfig.ctx.strokeStyle = 'rgba(100, 170, 255, 0.05)'; particleConfig.ctx.lineWidth = 0.5; for (let i = 0; i < particleConfig.particles.length; i++) { const particleA = particleConfig.particles[i]; for (let j = i + 1; j < particleConfig.particles.length; j++) { const particleB = particleConfig.particles[j]; // Skip if either particle has too many connections if (particleA.connections >= particleConfig.maxConnections || particleB.connections >= particleConfig.maxConnections) { continue; } const dx = particleA.x - particleB.x; const dy = particleA.y - particleB.y; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < particleConfig.connectionThreshold) { // Calculate opacity based on distance const opacity = 1 - (distance / particleConfig.connectionThreshold); particleConfig.ctx.beginPath(); particleConfig.ctx.moveTo(particleA.x, particleA.y); particleConfig.ctx.lineTo(particleB.x, particleB.y); particleConfig.ctx.strokeStyle = `rgba(100, 170, 255, ${opacity * 0.1})`; particleConfig.ctx.stroke(); // Increment connection count particleA.connections++; particleB.connections++; } } } } function handleMouseMove(e) { particleConfig.hoverArea.x = e.clientX; particleConfig.hoverArea.y = e.clientY; particleConfig.hoverArea.active = true; } function handleMouseLeave() { particleConfig.hoverArea.active = false; } function handleDataRefresh() { // Increase particle density temporarily particleConfig.density = 3; // Animate the refresh button const refreshBtn = document.getElementById('data-refresh-btn'); refreshBtn.querySelector('i').classList.add('pulse-animation'); // Generate new data for charts updateChartData(); // Reset particle density after animation completes setTimeout(() => { particleConfig.density = 1; refreshBtn.querySelector('i').classList.remove('pulse-animation'); }, 2000); } function updateChartData() { // Update bar chart with new random data const bars = document.querySelectorAll('.bar'); bars.forEach(bar => { const newHeight = 30 + Math.random() * 60; // Between 30% and 90% const newValue = (Math.floor(Math.random() * 30) + 10) + '.' + Math.floor(Math.random() * 10) + 'K'; // Animate height change bar.style.height = newHeight + '%'; bar.querySelector('.bar-value').textContent = '$' + newValue; }); // Update donut chart const newValue = Math.floor(Math.random() * 30) + 60; // Between 60% and 90% const donutValue = document.querySelector('.donut-value'); donutValue.textContent = newValue + '%'; // Update donut segment rotation const segment = document.querySelectorAll('.segment')[1]; segment.style.transform = `rotate(${newValue * 3.6}deg)`; // Update metrics with new random data const metricValues = document.querySelectorAll('.metric-value'); const metricChanges = document.querySelectorAll('.metric-change'); // Conversion rate update const newConversionRate = (3 + Math.random() * 1.5).toFixed(2); metricValues[0].textContent = newConversionRate + '%'; const conversionChange = (Math.random() * 0.8).toFixed(1); const conversionDirection = Math.random() > 0.5; metricChanges[0].textContent = (conversionDirection ? '+' : '-') + conversionChange + '% '; metricChanges[0].innerHTML += `<span style="margin-left: 5px;">${conversionDirection ? '↑' : '↓'}</span>`; metricChanges[0].className = 'metric-change ' + (conversionDirection ? 'positive' : 'negative'); // Other metrics updated similarly // Could expand this for the remaining metrics with more specific business logic } // Initialize donut chart animation function initDonutChart() { const segments = document.querySelectorAll('.segment'); // Apply animations with slight delay setTimeout(() => { segments[0].style.transform = 'rotate(0deg)'; segments[1].style.transform = 'rotate(245deg)'; }, 300); } // Initialize when the DOM is loaded document.addEventListener('DOMContentLoaded', () => { initParticles(); initDonutChart(); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Nexus Gaming Hub</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } body { overflow: hidden; background: #0a0520; color: #fff; height: 100vh; width: 100%; } #particleCanvas { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 1; } .content-container { position: relative; width: 100%; height: 100%; z-index: 2; display: flex; flex-direction: column; justify-content: center; align-items: center; padding: 1.5rem; overflow-y: auto; } .main-container { background: rgba(14, 8, 36, 0.7); border-radius: 12px; border: 1px solid rgba(111, 76, 255, 0.3); backdrop-filter: blur(10px); padding: 2rem; max-width: 650px; width: 100%; margin: 0 auto; text-align: center; box-shadow: 0 8px 32px rgba(31, 38, 135, 0.2); } .logo { margin-bottom: 0.5rem; display: flex; justify-content: center; align-items: center; } .logo-text { font-size: 2.5rem; font-weight: 800; background: linear-gradient(90deg, #9c6fff, #4b8fff); -webkit-background-clip: text; -webkit-text-fill-color: transparent; text-shadow: 0 0 10px rgba(111, 76, 255, 0.3); } h1 { font-size: 1.8rem; margin-bottom: 1rem; color: #fff; text-shadow: 0 0 10px rgba(111, 76, 255, 0.5); } p { color: #b7a9ff; margin-bottom: 1.5rem; line-height: 1.6; } .features { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1.5rem; margin: 2rem 0; } .feature-card { background: rgba(16, 9, 40, 0.6); border-radius: 8px; padding: 1.5rem; transition: all 0.3s ease; border: 1px solid rgba(111, 76, 255, 0.1); cursor: pointer; } .feature-card:hover { transform: translateY(-5px); border-color: rgba(111, 76, 255, 0.5); box-shadow: 0 8px 20px rgba(111, 76, 255, 0.2); } .feature-icon { font-size: 2rem; margin-bottom: 1rem; color: #9c6fff; } .feature-title { font-size: 1.25rem; margin-bottom: 0.5rem; color: #e2deff; } .feature-desc { font-size: 0.9rem; color: #9c91c8; } .cta-button { background: linear-gradient(90deg, #7649fe, #4b8fff); color: white; border: none; padding: 0.8rem 2rem; border-radius: 50px; font-size: 1rem; font-weight: 600; cursor: pointer; transition: all 0.3s ease; margin-top: 1rem; box-shadow: 0 5px 15px rgba(111, 76, 255, 0.3); } .cta-button:hover { transform: scale(1.05); box-shadow: 0 8px 20px rgba(111, 76, 255, 0.5); } .progress-bar { width: 100%; height: 6px; background: rgba(111, 76, 255, 0.2); border-radius: 10px; margin-top: 2rem; overflow: hidden; } .progress { height: 100%; width: 0; background: linear-gradient(90deg, #7649fe, #4b8fff); border-radius: 10px; transition: width 0.5s ease; } .stats { display: flex; justify-content: center; margin: 1.5rem 0; flex-wrap: wrap; gap: 1rem; } .stat-item { text-align: center; padding: 0.5rem 1rem; border-radius: 8px; background: rgba(16, 9, 40, 0.6); } .stat-number { font-size: 1.5rem; font-weight: 700; color: #9c6fff; } .stat-label { font-size: 0.8rem; color: #9c91c8; } .pulse { animation: pulse 2s infinite; } @keyframes pulse { 0% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.05); opacity: 0.8; } 100% { transform: scale(1); opacity: 1; } } @media (max-width: 700px) { .main-container { padding: 1.5rem; } .logo-text { font-size: 2rem; } h1 { font-size: 1.5rem; } .features { grid-template-columns: 1fr; } } @media (max-height: 700px) { .content-container { justify-content: flex-start; padding-top: 1rem; } } </style> </head> <body> <canvas id="particleCanvas"></canvas> <div class="content-container"> <div class="main-container"> <div class="logo"> <div class="logo-text">NEXUS</div> </div> <h1>Your Gateway to the Gaming Universe</h1> <p>Connect with 10,000+ gamers across the galaxy. Join tournaments, share strategies, and level up your gaming experience in our immersive community hub.</p> <div class="stats"> <div class="stat-item"> <div class="stat-number">10K+</div> <div class="stat-label">Active Players</div> </div> <div class="stat-item"> <div class="stat-number">250+</div> <div class="stat-label">Monthly Tournaments</div> </div> <div class="stat-item"> <div class="stat-number">98%</div> <div class="stat-label">Player Satisfaction</div> </div> </div> <div class="features"> <div class="feature-card"> <div class="feature-icon">🎮</div> <div class="feature-title">Live Gaming Sessions</div> <div class="feature-desc">Join multiplayer lobbies with players matched to your skill level and preferences.</div> </div> <div class="feature-card"> <div class="feature-icon">🏆</div> <div class="feature-title">Competitive Leagues</div> <div class="feature-desc">Compete in ranked tournaments with real prizes and global leaderboards.</div> </div> <div class="feature-card"> <div class="feature-icon">🌐</div> <div class="feature-title">Social Universe</div> <div class="feature-desc">Build your gaming profile, connect with friends, and create your own gaming guilds.</div> </div> <div class="feature-card"> <div class="feature-icon">🔮</div> <div class="feature-title">Game Analytics</div> <div class="feature-desc">Track your progress with advanced stats and AI-powered performance insights.</div> </div> </div> <button class="cta-button pulse">Launch into Nexus</button> <div class="progress-bar"> <div class="progress" id="progress"></div> </div> </div> </div> <script> // Particle animation for the space background class Particle { constructor(canvas, ctx, options = {}) { this.canvas = canvas; this.ctx = ctx; this.x = options.x || Math.random() * canvas.width; this.y = options.y || Math.random() * canvas.height; this.size = options.size || 0.5 + Math.random() * 2; this.baseSize = this.size; this.speedX = options.speedX || (Math.random() - 0.5) * 0.3; this.speedY = options.speedY || (Math.random() - 0.5) * 0.3; this.color = options.color || this.getRandomColor(); this.alpha = options.alpha || (0.4 + Math.random() * 0.6); this.isNode = options.isNode || Math.random() > 0.97; this.nodeConnections = []; this.orbitSpeed = Math.random() * 0.01 + 0.005; this.orbitAngle = Math.random() * Math.PI * 2; this.orbitRadius = 0; this.pulseSpeed = 0.03 + Math.random() * 0.04; this.pulseDirection = 1; if (this.isNode) { this.size = 3 + Math.random() * 4; this.orbitRadius = 15 + Math.random() * 25; this.speedX = (Math.random() - 0.5) * 0.1; this.speedY = (Math.random() - 0.5) * 0.1; } } getRandomColor() { const colors = [ '#9c6fff', // Purple '#4b8fff', // Blue '#ff6fe9', // Pink '#00eaff' // Cyan ]; return colors[Math.floor(Math.random() * colors.length)]; } draw() { this.ctx.beginPath(); this.ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); this.ctx.fillStyle = this.color; this.ctx.globalAlpha = this.alpha; this.ctx.fill(); // Draw orbiting particles for nodes if (this.isNode) { this.drawOrbiters(); } // Connect nearby nodes if (this.isNode && this.nodeConnections.length > 0) { this.drawConnections(); } this.ctx.globalAlpha = 1; } drawOrbiters() { const orbiters = 2 + Math.floor(Math.random() * 3); for (let i = 0; i < orbiters; i++) { const angle = this.orbitAngle + (Math.PI * 2 / orbiters) * i; const x = this.x + Math.cos(angle) * this.orbitRadius; const y = this.y + Math.sin(angle) * this.orbitRadius; this.ctx.beginPath(); this.ctx.arc(x, y, 0.8, 0, Math.PI * 2); this.ctx.fillStyle = this.color; this.ctx.globalAlpha = this.alpha * 0.8; this.ctx.fill(); } } drawConnections() { this.nodeConnections.forEach(node => { const distance = Math.sqrt( Math.pow(this.x - node.x, 2) + Math.pow(this.y - node.y, 2) ); if (distance < 200) { const opacity = 1 - distance / 200; this.ctx.beginPath(); this.ctx.moveTo(this.x, this.y); this.ctx.lineTo(node.x, node.y); this.ctx.strokeStyle = this.color; this.ctx.globalAlpha = opacity * 0.2; this.ctx.lineWidth = 0.5; this.ctx.stroke(); } }); } update(mouseX, mouseY) { // Update position this.x += this.speedX; this.y += this.speedY; // Wrap around edges if (this.x > this.canvas.width) this.x = 0; if (this.x < 0) this.x = this.canvas.width; if (this.y > this.canvas.height) this.y = 0; if (this.y < 0) this.y = this.canvas.height; // Pulsating size effect if (this.isNode) { this.size += this.pulseDirection * this.pulseSpeed; if (this.size > this.baseSize * 1.5 || this.size < this.baseSize * 0.8) { this.pulseDirection *= -1; } this.orbitAngle += this.orbitSpeed; } // Interaction with mouse if (mouseX !== null && mouseY !== null) { const dx = mouseX - this.x; const dy = mouseY - this.y; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < 120) { const force = (120 - distance) / 1500; this.speedX += (dx > 0) ? force : -force; this.speedY += (dy > 0) ? force : -force; this.alpha = Math.min(1, this.alpha + 0.05); if (!this.isNode) { this.size = Math.min(this.baseSize * 2, this.size + 0.1); } } else { this.alpha = Math.max(this.alpha - 0.01, this.isNode ? 0.6 : 0.3); if (!this.isNode) { this.size = Math.max(this.baseSize, this.size - 0.05); } } } // Dampen speed over time this.speedX *= 0.99; this.speedY *= 0.99; } } // Main ParticleSystem class class ParticleSystem { constructor(canvasId) { this.canvas = document.getElementById(canvasId); this.ctx = this.canvas.getContext('2d'); this.particles = []; this.mouseX = null; this.mouseY = null; this.resizeTimer = null; // Initialize this.resize(); this.createParticles(); this.connectNodes(); this.setupEventListeners(); this.animate(); // Initialize progress bar this.initProgressBar(); } resize() { this.canvas.width = window.innerWidth; this.canvas.height = window.innerHeight; } createParticles() { const totalParticles = Math.min( 150, Math.floor((this.canvas.width * this.canvas.height) / 8000) ); for (let i = 0; i < totalParticles; i++) { this.particles.push(new Particle(this.canvas, this.ctx)); } } connectNodes() { const nodes = this.particles.filter(p => p.isNode); nodes.forEach(node => { // Connect to 1-3 other random nodes const connectionCount = 1 + Math.floor(Math.random() * 3); const otherNodes = nodes.filter(n => n !== node); for (let i = 0; i < Math.min(connectionCount, otherNodes.length); i++) { const randomIndex = Math.floor(Math.random() * otherNodes.length); node.nodeConnections.push(otherNodes[randomIndex]); otherNodes.splice(randomIndex, 1); } }); } setupEventListeners() { window.addEventListener('resize', () => { clearTimeout(this.resizeTimer); this.resizeTimer = setTimeout(() => { this.resize(); }, 250); }); document.addEventListener('mousemove', (e) => { this.mouseX = e.clientX; this.mouseY = e.clientY; }); document.addEventListener('touchmove', (e) => { if (e.touches.length > 0) { this.mouseX = e.touches[0].clientX; this.mouseY = e.touches[0].clientY; } }); document.addEventListener('mouseleave', () => { this.mouseX = null; this.mouseY = null; }); document.addEventListener('touchend', () => { this.mouseX = null; this.mouseY = null; }); // Add click handler for feature cards document.querySelectorAll('.feature-card').forEach(card => { card.addEventListener('click', (e) => { const x = e.clientX; const y = e.clientY; // Create explosion of particles for (let i = 0; i < 10; i++) { const speedX = (Math.random() - 0.5) * 3; const speedY = (Math.random() - 0.5) * 3; const color = card.querySelector('.feature-icon').style.color || '#9c6fff'; this.particles.push(new Particle(this.canvas, this.ctx, { x: x, y: y, size: 1 + Math.random() * 2, speedX: speedX, speedY: speedY, color: color, alpha: 0.8 })); } }); }); // CTA button interaction const ctaButton = document.querySelector('.cta-button'); ctaButton.addEventListener('click', () => { // Simulate loading this.animateProgress(); // Create explosion effect around button const rect = ctaButton.getBoundingClientRect(); const centerX = rect.left + rect.width / 2; const centerY = rect.top + rect.height / 2; for (let i = 0; i < 30; i++) { const angle = Math.random() * Math.PI * 2; const speed = 1 + Math.random() * 3; const distance = 10 + Math.random() * 30; const x = centerX + Math.cos(angle) * distance; const y = centerY + Math.sin(angle) * distance; const speedX = Math.cos(angle) * speed; const speedY = Math.sin(angle) * speed; this.particles.push(new Particle(this.canvas, this.ctx, { x: x, y: y, size: 1 + Math.random() * 3, speedX: speedX, speedY: speedY, color: Math.random() > 0.5 ? '#7649fe' : '#4b8fff', alpha: 0.8 + Math.random() * 0.2 })); } }); } animate() { this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); // Update and draw particles this.particles.forEach(particle => { particle.update(this.mouseX, this.mouseY); particle.draw(); }); // Remove particles that have faded out this.particles = this.particles.filter(p => p.alpha > 0.05); requestAnimationFrame(this.animate.bind(this)); } initProgressBar() { this.progress = document.getElementById('progress'); this.progress.style.width = '0%'; } animateProgress() { let width = 0; const interval = setInterval(() => { if (width >= 100) { clearInterval(interval); setTimeout(() => { this.progress.style.width = '0%'; }, 1000); } else { width += 1; this.progress.style.width = width + '%'; } }, 20); } } // Initialize the particle system when the DOM is loaded document.addEventListener('DOMContentLoaded', () => { const particleSystem = new ParticleSystem('particleCanvas'); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>FocusFlow Mobile App Onboarding</title> <style> /* Reset and Base Styles */ * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; } :root { --primary: #0052FF; --secondary: #121212; --accent: #FF3B30; --light: #F8F9FA; --dark: #1E1E1E; --transition-speed: 0.4s; } body { background-color: var(--secondary); color: var(--light); overflow: hidden; height: 100vh; position: relative; } /* Particle Background */ canvas { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 1; } /* Onboarding Container */ .onboarding-container { position: relative; width: 100%; height: 100%; max-width: 700px; max-height: 700px; margin: 0 auto; display: flex; flex-direction: column; justify-content: center; align-items: center; padding: 0 20px; z-index: 2; overflow: hidden; } /* Slides Container */ .slides-container { width: 100%; height: 100%; display: flex; flex-direction: column; justify-content: center; overflow: hidden; position: relative; } /* Individual Slides */ .slide { position: absolute; width: 100%; height: 100%; display: flex; flex-direction: column; justify-content: center; align-items: center; padding: 30px; opacity: 0; transition: transform 0.7s ease, opacity 0.7s ease; transform: translateX(100%); } .slide.active { opacity: 1; transform: translateX(0); } .slide.previous { transform: translateX(-100%); } /* Illustration Container */ .illustration { width: 200px; height: 200px; margin-bottom: 30px; position: relative; display: flex; justify-content: center; align-items: center; } .illustration svg { width: 100%; height: 100%; } /* Typography */ h1 { font-size: 32px; font-weight: 700; margin-bottom: 15px; text-align: center; letter-spacing: -0.5px; line-height: 1.2; color: var(--light); } p { font-size: 18px; line-height: 1.6; text-align: center; margin-bottom: 40px; color: rgba(248, 249, 250, 0.8); max-width: 400px; } /* Navigation */ .navigation { position: absolute; bottom: 40px; width: 100%; display: flex; flex-direction: column; align-items: center; gap: 20px; } .dots { display: flex; gap: 10px; } .dot { width: 10px; height: 10px; border-radius: 50%; background-color: rgba(248, 249, 250, 0.3); transition: all 0.3s ease; cursor: pointer; } .dot.active { background-color: var(--primary); width: 30px; border-radius: 5px; } .buttons { display: flex; gap: 15px; width: 100%; justify-content: center; max-width: 360px; } .btn { padding: 14px 20px; border-radius: 12px; font-weight: 600; font-size: 16px; cursor: pointer; transition: all 0.3s ease; border: none; outline: none; display: flex; align-items: center; justify-content: center; min-width: 120px; } .btn-primary { background-color: var(--primary); color: var(--light); } .btn-primary:hover { background-color: #0046D9; transform: translateY(-2px); box-shadow: 0 10px 20px rgba(0, 82, 255, 0.2); } .btn-secondary { background-color: transparent; color: var(--light); border: 1px solid rgba(248, 249, 250, 0.3); } .btn-secondary:hover { border-color: var(--light); transform: translateY(-2px); } .btn-icon { margin-left: 8px; } /* Skip Button */ .skip-btn { position: absolute; top: 20px; right: 20px; padding: 8px 16px; background: transparent; color: var(--light); border: none; font-size: 14px; font-weight: 500; cursor: pointer; opacity: 0.7; transition: all 0.3s ease; } .skip-btn:hover { opacity: 1; } /* Animation for illustration elements */ @keyframes float { 0% { transform: translateY(0); } 50% { transform: translateY(-10px); } 100% { transform: translateY(0); } } @keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.05); } 100% { transform: scale(1); } } @keyframes rotate { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } .float-animation { animation: float 3s ease-in-out infinite; } .pulse-animation { animation: pulse 2s ease-in-out infinite; } .rotate-animation { animation: rotate 8s linear infinite; } /* First slide animation */ .illustration-1 .circle { animation: pulse 2s ease-in-out infinite; } .illustration-1 .icon { animation: float 3s ease-in-out infinite; } /* Second slide animation */ .illustration-2 .data-point { animation: pulse 2s ease-in-out infinite; animation-delay: calc(var(--i) * 0.2s); } /* Third slide animation */ .illustration-3 .notification { animation: float 3s ease-in-out infinite; animation-delay: calc(var(--i) * 0.3s); } /* Get Started Button Animation */ @keyframes shine { 0% { background-position: 0% 50%; } 100% { background-position: 200% 50%; } } .btn-get-started { background: linear-gradient(90deg, var(--primary), #2979FF, var(--primary)); background-size: 200% 100%; animation: shine 3s infinite linear; color: white; font-weight: 700; } /* For smaller screens */ @media (max-width: 400px) { h1 { font-size: 28px; } p { font-size: 16px; margin-bottom: 30px; } .illustration { width: 160px; height: 160px; margin-bottom: 20px; } .navigation { bottom: 20px; } .btn { padding: 12px 16px; font-size: 14px; min-width: 100px; } } </style> </head> <body> <canvas id="particleCanvas"></canvas> <div class="onboarding-container"> <button class="skip-btn">Skip</button> <div class="slides-container"> <div class="slide active"> <div class="illustration illustration-1"> <svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg"> <circle class="circle" cx="100" cy="100" r="80" fill="#0052FF" opacity="0.2" /> <circle class="circle" cx="100" cy="100" r="60" fill="#0052FF" opacity="0.3" /> <g class="icon"> <rect x="70" y="70" width="60" height="60" rx="12" fill="#0052FF" /> <path d="M95 90L115 100L95 110V90Z" fill="white" /> </g> </svg> </div> <h1>Streamlined Focus</h1> <p>FocusFlow helps you eliminate distractions with intelligent notifications that matter only when you need them.</p> </div> <div class="slide"> <div class="illustration illustration-2"> <svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg"> <circle cx="100" cy="100" r="80" fill="#0052FF" opacity="0.1" /> <path d="M40,120 Q100,40 160,120" stroke="#0052FF" stroke-width="4" fill="none" /> <circle class="data-point" style="--i:0" cx="60" cy="100" r="6" fill="#0052FF" /> <circle class="data-point" style="--i:1" cx="80" cy="80" r="6" fill="#0052FF" /> <circle class="data-point" style="--i:2" cx="100" cy="60" r="6" fill="#0052FF" /> <circle class="data-point" style="--i:3" cx="120" cy="80" r="6" fill="#0052FF" /> <circle class="data-point" style="--i:4" cx="140" cy="100" r="6" fill="#0052FF" /> </svg> </div> <h1>Track Your Progress</h1> <p>Visualize your productivity patterns with lightweight analytics that don't drain your battery or mental space.</p> </div> <div class="slide"> <div class="illustration illustration-3"> <svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg"> <circle cx="100" cy="100" r="80" fill="#0052FF" opacity="0.1" class="rotate-animation" /> <g class="notification" style="--i:0"> <rect x="60" y="70" width="80" height="25" rx="8" fill="#0052FF" /> <circle cx="75" cy="82.5" r="7" fill="white" /> <line x1="90" y1="82.5" x2="130" y2="82.5" stroke="white" stroke-width="2" /> </g> <g class="notification" style="--i:1"> <rect x="70" y="105" width="80" height="25" rx="8" fill="#FF3B30" opacity="0.8" /> <circle cx="85" cy="117.5" r="7" fill="white" /> <line x1="100" y1="117.5" x2="140" y2="117.5" stroke="white" stroke-width="2" /> </g> </svg> </div> <h1>Battery Efficient</h1> <p>Our minimalist design isn't just beautiful—it's optimized to maximize your phone's battery life while running.</p> </div> </div> <div class="navigation"> <div class="dots"> <div class="dot active" data-index="0"></div> <div class="dot" data-index="1"></div> <div class="dot" data-index="2"></div> </div> <div class="buttons"> <button class="btn btn-secondary prev-btn" style="display: none;">Previous</button> <button class="btn btn-primary next-btn">Next <svg class="btn-icon" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M8 3L14 8L8 13M14 8H2" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> </button> </div> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { // Particle background setup const canvas = document.getElementById('particleCanvas'); const ctx = canvas.getContext('2d'); // Set canvas to full window size function resizeCanvas() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; } resizeCanvas(); window.addEventListener('resize', resizeCanvas); // Particle class class Particle { constructor() { this.reset(); } reset() { this.x = Math.random() * canvas.width; this.y = Math.random() * canvas.height; this.size = Math.random() * 3 + 1; this.speedX = Math.random() * 0.3 - 0.15; this.speedY = Math.random() * 0.3 - 0.15; this.opacity = Math.random() * 0.5 + 0.1; } update() { this.x += this.speedX; this.y += this.speedY; // Reset if particle goes off canvas if (this.x < 0 || this.x > canvas.width || this.y < 0 || this.y > canvas.height) { this.reset(); } } draw() { ctx.fillStyle = `rgba(255, 255, 255, ${this.opacity})`; ctx.beginPath(); ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); ctx.closePath(); ctx.fill(); } } // Create particles const particlesArray = []; const particleCount = 30; // Low number for better performance for (let i = 0; i < particleCount; i++) { particlesArray.push(new Particle()); } // Animation loop function animate() { requestAnimationFrame(animate); ctx.clearRect(0, 0, canvas.width, canvas.height); for (let i = 0; i < particlesArray.length; i++) { particlesArray[i].update(); particlesArray[i].draw(); } } animate(); // Onboarding slides functionality const slides = document.querySelectorAll('.slide'); const dots = document.querySelectorAll('.dot'); const nextBtn = document.querySelector('.next-btn'); const prevBtn = document.querySelector('.prev-btn'); const skipBtn = document.querySelector('.skip-btn'); let currentSlide = 0; function updateSlides() { slides.forEach((slide, index) => { if (index === currentSlide) { slide.classList.add('active'); slide.classList.remove('previous'); } else if (index < currentSlide) { slide.classList.remove('active'); slide.classList.add('previous'); } else { slide.classList.remove('active', 'previous'); } }); dots.forEach((dot, index) => { if (index === currentSlide) { dot.classList.add('active'); } else { dot.classList.remove('active'); } }); if (currentSlide === 0) { prevBtn.style.display = 'none'; } else { prevBtn.style.display = 'block'; } if (currentSlide === slides.length - 1) { nextBtn.textContent = 'Get Started'; nextBtn.classList.add('btn-get-started'); nextBtn.innerHTML = 'Get Started'; } else { nextBtn.classList.remove('btn-get-started'); nextBtn.innerHTML = 'Next <svg class="btn-icon" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M8 3L14 8L8 13M14 8H2" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>'; } } nextBtn.addEventListener('click', () => { if (currentSlide < slides.length - 1) { currentSlide++; updateSlides(); } else { // Handle get started button click (last slide) console.log('App started!'); // In a real app, this would navigate to the main app } }); prevBtn.addEventListener('click', () => { if (currentSlide > 0) { currentSlide--; updateSlides(); } }); skipBtn.addEventListener('click', () => { currentSlide = slides.length - 1; updateSlides(); }); dots.forEach(dot => { dot.addEventListener('click', () => { currentSlide = parseInt(dot.getAttribute('data-index')); updateSlides(); }); }); // Initialize slides updateSlides(); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Lumina - Elevated Essentials</title> <style> @import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600;700&family=Playfair+Display:wght@400;700&display=swap'); :root { --primary: #f8f4f1; --accent: #a28f78; --text: #333333; --light-accent: #e0d6cc; --transition: all 0.3s ease; } * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Montserrat', sans-serif; background-color: var(--primary); color: var(--text); overflow-x: hidden; position: relative; height: 100%; width: 100%; } #particle-canvas { position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: -1; opacity: 0.6; } .container { max-width: 100%; margin: 0 auto; padding: 0 20px; position: relative; z-index: 1; } nav { padding: 20px 0; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid rgba(162, 143, 120, 0.2); } .logo { font-family: 'Playfair Display', serif; font-size: 1.8rem; font-weight: 700; color: var(--text); text-decoration: none; position: relative; } .logo::after { content: ''; position: absolute; width: 6px; height: 6px; background-color: var(--accent); border-radius: 50%; bottom: 0; right: -8px; } .nav-links { display: flex; list-style: none; } .nav-links li { margin-left: 25px; } .nav-links a { text-decoration: none; color: var(--text); font-size: 0.9rem; font-weight: 500; letter-spacing: 0.5px; transition: var(--transition); position: relative; } .nav-links a::after { content: ''; position: absolute; bottom: -4px; left: 0; width: 0; height: 1px; background-color: var(--accent); transition: var(--transition); } .nav-links a:hover::after { width: 100%; } .icons { display: flex; align-items: center; } .icon { margin-left: 15px; cursor: pointer; transition: var(--transition); position: relative; } .icon:hover { color: var(--accent); } .cart-count { position: absolute; top: -8px; right: -8px; background-color: var(--accent); color: white; border-radius: 50%; width: 16px; height: 16px; font-size: 10px; display: flex; align-items: center; justify-content: center; } .hero { padding: 60px 0; display: flex; align-items: center; justify-content: space-between; } .hero-content { width: 50%; } .hero-images { width: 45%; position: relative; height: 350px; } .hero h1 { font-family: 'Playfair Display', serif; font-size: 2.6rem; line-height: 1.2; margin-bottom: 20px; position: relative; } .highlight { position: relative; display: inline-block; } .highlight::after { content: ''; position: absolute; bottom: 5px; left: 0; width: 100%; height: 8px; background-color: rgba(162, 143, 120, 0.2); z-index: -1; } .hero p { font-size: 0.95rem; line-height: 1.6; margin-bottom: 30px; color: #555; } .cta-btn { display: inline-block; background-color: var(--accent); color: white; padding: 12px 25px; border-radius: 30px; text-decoration: none; font-weight: 500; letter-spacing: 0.5px; transition: var(--transition); position: relative; overflow: hidden; } .cta-btn::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%; background: rgba(255, 255, 255, 0.2); transition: var(--transition); } .cta-btn:hover::before { left: 100%; } .cta-secondary { margin-left: 15px; color: var(--text); border-bottom: 1px solid var(--accent); padding-bottom: 2px; text-decoration: none; transition: var(--transition); } .cta-secondary:hover { color: var(--accent); } .product-img { position: absolute; border-radius: 10px; box-shadow: 0 15px 35px rgba(0, 0, 0, 0.1); transition: var(--transition); } .product-img.main { width: 250px; height: 320px; top: 0; right: 0; object-fit: cover; z-index: 2; } .product-img.secondary { width: 200px; height: 260px; top: 60px; left: 0; object-fit: cover; z-index: 1; } .product-img:hover { transform: translateY(-5px); box-shadow: 0 20px 35px rgba(0, 0, 0, 0.15); } .categories { padding: 50px 0; } .section-title { font-family: 'Playfair Display', serif; font-size: 1.8rem; margin-bottom: 30px; text-align: center; position: relative; } .section-title::after { content: ''; position: absolute; width: 60px; height: 2px; background-color: var(--accent); bottom: -10px; left: 50%; transform: translateX(-50%); } .category-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; margin-top: 30px; } .category-card { position: relative; height: 180px; border-radius: 10px; overflow: hidden; cursor: pointer; } .category-card img { width: 100%; height: 100%; object-fit: cover; transition: var(--transition); } .category-card:hover img { transform: scale(1.05); } .category-overlay { position: absolute; bottom: 0; left: 0; width: 100%; background: linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent); padding: 15px; color: white; } .category-overlay h3 { font-size: 1.1rem; margin-bottom: 5px; } .category-overlay span { font-size: 0.8rem; opacity: 0.8; } .trending { padding: 50px 0; } .product-slider { display: flex; gap: 20px; overflow-x: auto; scrollbar-width: none; padding: 10px 0; -ms-overflow-style: none; } .product-slider::-webkit-scrollbar { display: none; } .product-card { flex: 0 0 220px; border-radius: 10px; background-color: white; overflow: hidden; box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05); transition: var(--transition); } .product-card:hover { transform: translateY(-5px); box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1); } .product-card img { width: 100%; height: 220px; object-fit: cover; } .product-info { padding: 15px; } .product-info h3 { font-size: 1rem; margin-bottom: 8px; } .product-info p { font-size: 0.9rem; color: var(--accent); font-weight: 600; } .product-info .price { display: flex; justify-content: space-between; align-items: center; margin-top: 10px; } .add-to-cart { background-color: var(--light-accent); color: var(--text); border: none; width: 30px; height: 30px; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: var(--transition); } .add-to-cart:hover { background-color: var(--accent); color: white; } .newsletter { padding: 40px; background-color: var(--light-accent); border-radius: 10px; margin: 50px 0; text-align: center; } .newsletter h2 { font-family: 'Playfair Display', serif; font-size: 1.5rem; margin-bottom: 15px; } .newsletter p { font-size: 0.9rem; max-width: 500px; margin: 0 auto 20px; line-height: 1.6; } .newsletter-form { display: flex; max-width: 450px; margin: 0 auto; } .newsletter-form input { flex: 1; padding: 12px 15px; border: none; border-radius: 30px 0 0 30px; font-family: 'Montserrat', sans-serif; outline: none; } .newsletter-form button { background-color: var(--accent); color: white; border: none; padding: 0 25px; border-radius: 0 30px 30px 0; cursor: pointer; transition: var(--transition); } .newsletter-form button:hover { background-color: #8a795f; } /* Mobile styles */ @media (max-width: 768px) { .hero { flex-direction: column; padding: 40px 0; } .hero-content, .hero-images { width: 100%; } .hero-content { margin-bottom: 40px; } .hero h1 { font-size: 2rem; } .hero-images { height: 300px; } .product-img.main { width: 200px; height: 260px; right: 30px; } .product-img.secondary { width: 160px; height: 220px; left: 30px; } .category-grid { grid-template-columns: repeat(2, 1fr); } .nav-links { display: none; } .mobile-menu { display: block; } } /* Menu button for mobile */ .mobile-menu { display: none; background: none; border: none; cursor: pointer; } .mobile-menu div { width: 25px; height: 2px; background-color: var(--text); margin: 6px 0; transition: var(--transition); } @media (max-width: 480px) { .hero h1 { font-size: 1.8rem; } .category-grid { grid-template-columns: 1fr; } .hero-images { height: 250px; } .product-img.main { width: 180px; height: 230px; right: 20px; } .product-img.secondary { width: 140px; height: 190px; left: 20px; } } /* Animation for newsletter */ @keyframes pulse { 0% { box-shadow: 0 0 0 0 rgba(162, 143, 120, 0.4); } 70% { box-shadow: 0 0 0 10px rgba(162, 143, 120, 0); } 100% { box-shadow: 0 0 0 0 rgba(162, 143, 120, 0); } } .newsletter-form button { animation: pulse 2s infinite; } </style> </head> <body> <canvas id="particle-canvas"></canvas> <div class="container"> <nav> <a href="#" class="logo">Lumina</a> <ul class="nav-links"> <li><a href="#">Home</a></li> <li><a href="#">Shop</a></li> <li><a href="#">Collections</a></li> <li><a href="#">About</a></li> <li><a href="#">Contact</a></li> </ul> <button class="mobile-menu"> <div></div> <div></div> <div></div> </button> <div class="icons"> <div class="icon"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <circle cx="11" cy="11" r="8"></circle> <line x1="21" y1="21" x2="16.65" y2="16.65"></line> </svg> </div> <div class="icon"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path> </svg> </div> <div class="icon"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M6 2L3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"></path> <line x1="3" y1="6" x2="21" y2="6"></line> <path d="M16 10a4 4 0 0 1-8 0"></path> </svg> <span class="cart-count">3</span> </div> </div> </nav> <section class="hero"> <div class="hero-content"> <h1>Modern essentials for <span class="highlight">mindful living</span></h1> <p>Discover our curated collection of sustainable products that bring elegance to everyday moments. Crafted with care for both you and the planet.</p> <a href="#" class="cta-btn">Shop New Arrivals</a> <a href="#" class="cta-secondary">View collections</a> </div> <div class="hero-images"> <img src="https://images.unsplash.com/photo-1616486338812-3dadae4b4ace?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80" alt="Ceramic mug and plate set" class="product-img main"> <img src="https://images.unsplash.com/photo-1589782182703-2aaa69037b5b?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80" alt="Linen table cloth" class="product-img secondary"> </div> </section> <section class="categories"> <h2 class="section-title">Shop by Category</h2> <div class="category-grid"> <div class="category-card"> <img src="https://images.unsplash.com/photo-1584589167171-541ce45f1eea?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80" alt="Kitchen Essentials"> <div class="category-overlay"> <h3>Kitchen Essentials</h3> <span>24 products</span> </div> </div> <div class="category-card"> <img src="https://images.unsplash.com/photo-1616594039964-ae9021a400a0?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80" alt="Home Textiles"> <div class="category-overlay"> <h3>Home Textiles</h3> <span>18 products</span> </div> </div> <div class="category-card"> <img src="https://images.unsplash.com/photo-1560448204-603b3fc33ddc?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80" alt="Bath & Body"> <div class="category-overlay"> <h3>Bath & Body</h3> <span>16 products</span> </div> </div> <div class="category-card"> <img src="https://images.unsplash.com/photo-1533090161767-e6ffed986c88?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80" alt="Decor & Accents"> <div class="category-overlay"> <h3>Decor & Accents</h3> <span>32 products</span> </div> </div> </div> </section> <section class="trending"> <h2 class="section-title">Trending Now</h2> <div class="product-slider"> <div class="product-card"> <img src="https://images.unsplash.com/photo-1523293182086-7651a899d37f?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80" alt="Ceramic Vase"> <div class="product-info"> <h3>Ceramic Vase</h3> <div class="price"> <p>$48.00</p> <button class="add-to-cart">+</button> </div> </div> </div> <div class="product-card"> <img src="https://images.unsplash.com/photo-1551907234-a38162cd6b8f?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80" alt="Linen Throw"> <div class="product-info"> <h3>Linen Throw</h3> <div class="price"> <p>$65.00</p> <button class="add-to-cart">+</button> </div> </div> </div> <div class="product-card"> <img src="https://images.unsplash.com/photo-1552374196-1ab2a1c593e8?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80" alt="Wool Cushion"> <div class="product-info"> <h3>Wool Cushion</h3> <div class="price"> <p>$35.00</p> <button class="add-to-cart">+</button> </div> </div> </div> <div class="product-card"> <img src="https://images.unsplash.com/photo-1608571423902-eed4a5ad8108?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80" alt="Ceramic Mug Set"> <div class="product-info"> <h3>Ceramic Mug Set</h3> <div class="price"> <p>$42.00</p> <button class="add-to-cart">+</button> </div> </div> </div> <div class="product-card"> <img src="https://images.unsplash.com/photo-1586201375761-83865001e8ac?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80" alt="Scented Candle"> <div class="product-info"> <h3>Scented Candle</h3> <div class="price"> <p>$28.00</p> <button class="add-to-cart">+</button> </div> </div> </div> </div> </section> <section class="newsletter"> <h2>Join Our Community</h2> <p>Subscribe to receive early access to new collections, exclusive offers, and insights on sustainable living.</p> <form class="newsletter-form"> <input type="email" placeholder="Your email address" required> <button type="button">Subscribe</button> </form> </section> </div> <script> // Particle background document.addEventListener('DOMContentLoaded', function() { const canvas = document.getElementById('particle-canvas'); const ctx = canvas.getContext('2d'); let particles = []; const particleCount = 70; const colors = ['#e0d6cc', '#dacfc4', '#d4c7bb', '#c2b5a5', '#a28f78']; // Set canvas size function resizeCanvas() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; } resizeCanvas(); window.addEventListener('resize', resizeCanvas); // Create particles function createParticles() { particles = []; for (let i = 0; i < particleCount; i++) { particles.push({ x: Math.random() * canvas.width, y: Math.random() * canvas.height, radius: Math.random() * 3 + 1, color: colors[Math.floor(Math.random() * colors.length)], speed: Math.random() * 0.5 + 0.1, direction: Math.random() * Math.PI * 2, opacity: Math.random() * 0.5 + 0.2 }); } } createParticles(); // Animate particles function animate() { ctx.clearRect(0, 0, canvas.width, canvas.height); particles.forEach(particle => { // Move particles particle.x += Math.cos(particle.direction) * particle.speed; particle.y += Math.sin(particle.direction) * particle.speed; // Wrap around canvas if (particle.x < 0) particle.x = canvas.width; if (particle.x > canvas.width) particle.x = 0; if (particle.y < 0) particle.y = canvas.height; if (particle.y > canvas.height) particle.y = 0; // Draw particle ctx.beginPath(); ctx.arc(particle.x, particle.y, particle.radius, 0, Math.PI * 2); ctx.fillStyle = particle.color; ctx.globalAlpha = particle.opacity; ctx.fill(); ctx.globalAlpha = 1; }); requestAnimationFrame(animate); } animate(); // React to scroll let scrollY = 0; window.addEventListener('scroll', function() { const newScrollY = window.scrollY; const delta = newScrollY - scrollY; scrollY = newScrollY; // Slightly move particles on scroll particles.forEach(particle => { particle.y += delta * 0.03 * particle.speed; // Wrap around canvas if (particle.y < 0) particle.y = canvas.height; if (particle.y > canvas.height) particle.y = 0; }); }); }); // Add to cart interaction document.querySelectorAll('.add-to-cart').forEach(button => { button.addEventListener('click', function() { const cartCount = document.querySelector('.cart-count'); cartCount.textContent = parseInt(cartCount.textContent) + 1; this.textContent = '✓'; setTimeout(() => { this.textContent = '+'; }, 1000); }); }); // Mobile menu toggle document.querySelector('.mobile-menu').addEventListener('click', function() { const navLinks = document.querySelector('.nav-links'); navLinks.style.display = navLinks.style.display === 'flex' ? 'none' : 'flex'; }); // Newsletter form interaction document.querySelector('.newsletter-form button').addEventListener('click', function() { const input = document.querySelector('.newsletter-form input'); if (input.value.trim() !== '') { input.value = ''; this.textContent = 'Thanks!'; setTimeout(() => { this.textContent = 'Subscribe'; }, 2000); } }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Knowledge Network</title> <style> :root { --primary-blue: #184e77; --secondary-blue: #1a759f; --tertiary-blue: #34a0a4; --primary-green: #2a9d8f; --secondary-green: #52b788; --highlight: #d9ed92; --background: #f0f7f4; --text-dark: #2c3e50; --text-light: #f0f7f4; } * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Inter', 'Segoe UI', sans-serif; } body { width: 100%; height: 100vh; background-color: var(--background); overflow: hidden; position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center; } .container { width: 700px; height: 700px; position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center; } canvas { position: absolute; top: 0; left: 0; z-index: 1; } .content { position: relative; z-index: 2; max-width: 600px; padding: 2rem; background-color: rgba(240, 247, 244, 0.85); border-radius: 12px; box-shadow: 0 8px 32px rgba(24, 78, 119, 0.1); backdrop-filter: blur(5px); text-align: center; transition: all 0.3s ease; } h1 { color: var(--primary-blue); font-size: 2.2rem; margin-bottom: 1rem; line-height: 1.2; } p { color: var(--text-dark); margin-bottom: 1.2rem; line-height: 1.6; font-size: 1rem; } .legend { display: flex; justify-content: center; margin-top: 1.5rem; gap: 1.5rem; flex-wrap: wrap; } .legend-item { display: flex; align-items: center; gap: 0.5rem; } .legend-icon { width: 12px; height: 12px; border-radius: 50%; } .legend-text { font-size: 0.85rem; color: var(--text-dark); } .topic-preview { position: absolute; padding: 0.75rem 1.25rem; background-color: var(--primary-blue); color: var(--text-light); border-radius: 8px; font-size: 0.85rem; opacity: 0; transition: opacity 0.3s ease; max-width: 200px; text-align: center; box-shadow: 0 4px 12px rgba(24, 78, 119, 0.15); z-index: 3; pointer-events: none; } .interactive-btn { margin-top: 1.2rem; padding: 0.75rem 1.5rem; background-color: var(--tertiary-blue); color: var(--text-light); border: none; border-radius: 6px; font-weight: 600; cursor: pointer; transition: all 0.2s ease; box-shadow: 0 4px 6px rgba(24, 78, 119, 0.1); } .interactive-btn:hover { background-color: var(--secondary-blue); transform: translateY(-2px); box-shadow: 0 6px 12px rgba(24, 78, 119, 0.15); } .interactive-btn:active { transform: translateY(0); } .focus-mode .content { background-color: rgba(240, 247, 244, 0.95); } @media (max-width: 700px) { .content { padding: 1.5rem; max-width: 90%; } h1 { font-size: 1.8rem; } p { font-size: 0.9rem; } } </style> </head> <body> <div class="container"> <canvas id="network"></canvas> <div class="content"> <h1>Interconnected Learning Networks</h1> <p>Our platform visualizes knowledge connections across disciplines, revealing how concepts build upon one another. Each node represents a learning topic, with brighter nodes indicating key foundational concepts.</p> <p>Hover over illuminated nodes to discover essential subjects that interconnect multiple fields of study.</p> <div class="legend"> <div class="legend-item"> <div class="legend-icon" style="background-color: var(--primary-blue);"></div> <span class="legend-text">Foundational Concepts</span> </div> <div class="legend-item"> <div class="legend-icon" style="background-color: var(--primary-green);"></div> <span class="legend-text">Applied Knowledge</span> </div> <div class="legend-item"> <div class="legend-icon" style="background-color: var(--highlight);"></div> <span class="legend-text">Key Connecting Topics</span> </div> </div> <button class="interactive-btn" id="focusBtn">Focus Mode</button> </div> </div> <div class="topic-preview" id="topicPreview"></div> <script> document.addEventListener('DOMContentLoaded', function() { const canvas = document.getElementById('network'); const ctx = canvas.getContext('2d'); const container = document.querySelector('.container'); const topicPreview = document.getElementById('topicPreview'); const focusBtn = document.getElementById('focusBtn'); let focusMode = false; // Educational topics array with connections const topics = [ { name: "Linear Algebra", category: "Mathematics", connections: [1, 3, 7, 9, 12] }, { name: "Quantum Physics", category: "Physics", connections: [0, 2, 6, 10] }, { name: "Statistical Methods", category: "Mathematics", connections: [0, 1, 4, 8, 13] }, { name: "Machine Learning", category: "Computer Science", connections: [0, 2, 5, 8, 11] }, { name: "Climate Modeling", category: "Environmental Science", connections: [2, 6, 9, 14] }, { name: "Neural Networks", category: "Computer Science", connections: [3, 7, 10] }, { name: "Thermodynamics", category: "Physics", connections: [1, 4, 9, 13] }, { name: "Computer Vision", category: "Computer Science", connections: [0, 5, 11, 14] }, { name: "Data Analysis", category: "Statistics", connections: [2, 3, 13, 15] }, { name: "Fluid Dynamics", category: "Physics", connections: [0, 4, 6, 12] }, { name: "Quantum Computing", category: "Computer Science", connections: [1, 5, 15] }, { name: "Artificial Intelligence", category: "Computer Science", connections: [3, 7, 15] }, { name: "Structural Engineering", category: "Engineering", connections: [0, 9, 14] }, { name: "Probability Theory", category: "Mathematics", connections: [2, 6, 8] }, { name: "Sustainable Design", category: "Engineering", connections: [4, 7, 12] }, { name: "Algorithm Design", category: "Computer Science", connections: [8, 10, 11] } ]; // Set canvas dimensions function setCanvasDimensions() { canvas.width = container.offsetWidth; canvas.height = container.offsetHeight; } setCanvasDimensions(); window.addEventListener('resize', setCanvasDimensions); // Create particles for each topic const particles = []; const keyTopics = [0, 2, 3, 8, 11]; // Indices of important topics that will light up // Initialize particles topics.forEach((topic, index) => { const isKeyTopic = keyTopics.includes(index); const categoryColors = { "Mathematics": { r: 24, g: 78, b: 119 }, // Primary blue "Physics": { r: 26, g: 117, b: 159 }, // Secondary blue "Computer Science": { r: 42, g: 157, b: 143 }, // Primary green "Statistics": { r: 52, g: 160, b: 164 }, // Tertiary blue "Engineering": { r: 82, g: 183, b: 136 }, // Secondary green "Environmental Science": { r: 26, g: 117, b: 159 } // Secondary blue }; const color = categoryColors[topic.category] || { r: 24, g: 78, b: 119 }; particles.push({ x: Math.random() * canvas.width, y: Math.random() * canvas.height, radius: isKeyTopic ? 4 : 3, color: color, originalColor: color, highlightColor: { r: 217, g: 237, b: 146 }, // Highlight color velocityX: (Math.random() - 0.5) * 0.5, velocityY: (Math.random() - 0.5) * 0.5, isKeyTopic: isKeyTopic, isActive: false, activeTime: 0, activationInterval: 5000 + Math.random() * 10000, // Random interval between 5-15 seconds lastActivation: Math.random() * 5000, // Stagger initial activations topic: topic, index: index }); }); // Animation variables let lastTime = 0; let mouseX = -1000; let mouseY = -1000; let hoveredParticle = null; // Mouse event listeners canvas.addEventListener('mousemove', (e) => { const rect = canvas.getBoundingClientRect(); mouseX = e.clientX - rect.left; mouseY = e.clientY - rect.top; // Check if mouse is over a particle hoveredParticle = null; for (const particle of particles) { const dx = mouseX - particle.x; const dy = mouseY - particle.y; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < particle.radius * 2) { hoveredParticle = particle; break; } } // Update topic preview position and content if (hoveredParticle && (hoveredParticle.isActive || hoveredParticle.isKeyTopic)) { topicPreview.style.opacity = '1'; topicPreview.style.left = (mouseX + 15) + 'px'; topicPreview.style.top = (mouseY + 15) + 'px'; topicPreview.innerHTML = `<strong>${hoveredParticle.topic.name}</strong><br>${hoveredParticle.topic.category}`; } else { topicPreview.style.opacity = '0'; } }); canvas.addEventListener('mouseleave', () => { mouseX = -1000; mouseY = -1000; topicPreview.style.opacity = '0'; hoveredParticle = null; }); // Focus mode toggle focusBtn.addEventListener('click', () => { focusMode = !focusMode; document.body.classList.toggle('focus-mode'); focusBtn.textContent = focusMode ? 'Standard Mode' : 'Focus Mode'; }); // Animation loop function animate(currentTime) { const deltaTime = currentTime - lastTime; lastTime = currentTime; ctx.clearRect(0, 0, canvas.width, canvas.height); // Update and draw connections first (so they're behind particles) ctx.lineWidth = 0.5; particles.forEach(particle => { const connections = particle.topic.connections; connections.forEach(connectionIndex => { const connectedParticle = particles[connectionIndex]; // Calculate opacity based on distance and activation state const dx = particle.x - connectedParticle.x; const dy = particle.y - connectedParticle.y; const distance = Math.sqrt(dx * dx + dy * dy); const maxDistance = 150; if (distance < maxDistance) { let opacity = 0.1 * (1 - distance / maxDistance); // Increase opacity when either particle is active if (particle.isActive || connectedParticle.isActive || particle.isKeyTopic || connectedParticle.isKeyTopic) { opacity += 0.2; } // Further increase opacity in focus mode if (focusMode && (particle.isKeyTopic || connectedParticle.isKeyTopic)) { opacity += 0.1; } ctx.beginPath(); ctx.moveTo(particle.x, particle.y); ctx.lineTo(connectedParticle.x, connectedParticle.y); ctx.strokeStyle = `rgba(52, 160, 164, ${opacity})`; ctx.stroke(); } }); }); // Update and draw particles particles.forEach(particle => { // Update position particle.x += particle.velocityX; particle.y += particle.velocityY; // Boundary check if (particle.x < 0 || particle.x > canvas.width) { particle.velocityX *= -1; } if (particle.y < 0 || particle.y > canvas.height) { particle.velocityY *= -1; } // Update activation state if (particle.isKeyTopic) { particle.lastActivation += deltaTime; if (particle.lastActivation > particle.activationInterval) { particle.isActive = !particle.isActive; particle.lastActivation = 0; particle.activeTime = 0; } if (particle.isActive) { particle.activeTime += deltaTime; if (particle.activeTime > 3000) { // Active for 3 seconds particle.isActive = false; } } } // Determine particle color based on state let finalColor; if (hoveredParticle === particle) { // Hovered particle finalColor = particle.highlightColor; } else if (particle.isActive || particle.isKeyTopic) { // Active particles or key topics const progress = Math.sin((currentTime % 2000) / 2000 * Math.PI) * 0.5 + 0.5; finalColor = { r: particle.originalColor.r * (1 - progress) + particle.highlightColor.r * progress, g: particle.originalColor.g * (1 - progress) + particle.highlightColor.g * progress, b: particle.originalColor.b * (1 - progress) + particle.highlightColor.b * progress }; } else { // Regular particles finalColor = particle.originalColor; } // Draw particle ctx.beginPath(); ctx.arc(particle.x, particle.y, particle.radius, 0, Math.PI * 2); ctx.fillStyle = `rgb(${finalColor.r}, ${finalColor.g}, ${finalColor.b})`; ctx.fill(); // Add glow effect for active particles if (particle.isActive || particle.isKeyTopic || hoveredParticle === particle) { const glowRadius = particle.radius * 2; const gradient = ctx.createRadialGradient( particle.x, particle.y, particle.radius, particle.x, particle.y, glowRadius ); gradient.addColorStop(0, `rgba(${finalColor.r}, ${finalColor.g}, ${finalColor.b}, 0.8)`); gradient.addColorStop(1, `rgba(${finalColor.r}, ${finalColor.g}, ${finalColor.b}, 0)`); ctx.beginPath(); ctx.arc(particle.x, particle.y, glowRadius, 0, Math.PI * 2); ctx.fillStyle = gradient; ctx.fill(); } }); requestAnimationFrame(animate); } requestAnimationFrame(animate); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>FinFlow Dashboard</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Inter', 'Segoe UI', sans-serif; } :root { --primary-dark: #0a1929; --primary: #173857; --primary-light: #2a5684; --accent: #4a8cca; --text-primary: #e8f0f7; --text-secondary: #b2c6d8; --success: #34c759; --danger: #ff3b30; --neutral: #8a8a8a; } body { width: 100%; height: 700px; background-color: var(--primary-dark); color: var(--text-primary); overflow: hidden; position: relative; } #particle-canvas { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 1; } .container { position: relative; width: 100%; max-width: 700px; height: 700px; margin: 0 auto; padding: 20px; z-index: 2; display: flex; flex-direction: column; } .header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem; } .logo { display: flex; align-items: center; gap: 10px; } .logo-icon { width: 32px; height: 32px; background: linear-gradient(135deg, var(--accent), var(--primary-light)); border-radius: 8px; display: flex; align-items: center; justify-content: center; } .logo-text { font-weight: 700; font-size: 1.4rem; background: linear-gradient(to right, var(--text-primary), var(--accent)); -webkit-background-clip: text; background-clip: text; color: transparent; } .profile { display: flex; align-items: center; gap: 12px; } .notification-bell { position: relative; cursor: pointer; transition: transform 0.2s; } .notification-bell:hover { transform: scale(1.1); } .notification-dot { position: absolute; top: 0; right: 0; width: 8px; height: 8px; background-color: var(--danger); border-radius: 50%; } .avatar { width: 38px; height: 38px; background-color: var(--primary-light); border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; position: relative; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.2); transition: transform 0.3s ease; } .avatar:hover { transform: scale(1.05); } .avatar-initial { font-weight: 600; font-size: 16px; color: var(--text-primary); } .dashboard-grid { display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: auto auto auto; gap: 20px; flex-grow: 1; } .card { background-color: rgba(23, 56, 87, 0.5); backdrop-filter: blur(10px); border-radius: 12px; padding: 18px; box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15); display: flex; flex-direction: column; overflow: hidden; transition: transform 0.3s ease, box-shadow 0.3s ease; position: relative; } .card::before { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 3px; background: linear-gradient(90deg, transparent, var(--accent), transparent); opacity: 0; transition: opacity 0.3s ease; } .card:hover { transform: translateY(-5px); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2); } .card:hover::before { opacity: 1; } .market-overview { grid-column: span 2; grid-row: span 1; } .portfolio-value { grid-row: span 1; } .recent-transactions { grid-column: span 2; grid-row: span 2; } .alerts { grid-row: span 2; } .card-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; } .card-title { font-weight: 600; font-size: 1rem; color: var(--text-primary); } .card-actions { display: flex; gap: 10px; } .btn-icon { background: none; border: none; width: 28px; height: 28px; border-radius: 6px; display: flex; align-items: center; justify-content: center; cursor: pointer; color: var(--text-secondary); transition: all 0.2s ease; } .btn-icon:hover { background-color: rgba(255, 255, 255, 0.1); color: var(--text-primary); } .market-chart { width: 100%; height: 160px; margin-top: 10px; position: relative; } .chart-legend { display: flex; align-items: center; justify-content: center; gap: 20px; margin-top: 10px; } .legend-item { display: flex; align-items: center; gap: 5px; font-size: 0.75rem; color: var(--text-secondary); } .legend-color { width: 8px; height: 8px; border-radius: 50%; } .sp500 { background-color: var(--accent); } .nasdaq { background-color: var(--success); } .dow { background-color: var(--neutral); } .price-data { display: flex; flex-direction: column; justify-content: center; align-items: center; height: 100%; } .portfolio-value-amount { font-size: 2rem; font-weight: 700; margin-bottom: 5px; background: linear-gradient(to right, var(--text-primary), var(--accent)); -webkit-background-clip: text; background-clip: text; color: transparent; } .portfolio-change { display: flex; align-items: center; gap: 5px; font-size: 0.9rem; } .positive { color: var(--success); } .transaction-list { list-style: none; margin-top: 10px; overflow-y: auto; max-height: 270px; scrollbar-width: thin; scrollbar-color: var(--primary-light) var(--primary-dark); } .transaction-list::-webkit-scrollbar { width: 5px; } .transaction-list::-webkit-scrollbar-track { background: var(--primary-dark); } .transaction-list::-webkit-scrollbar-thumb { background-color: var(--primary-light); border-radius: 10px; } .transaction-item { padding: 10px 0; border-bottom: 1px solid rgba(255, 255, 255, 0.05); display: flex; align-items: center; gap: 12px; transition: background-color 0.2s ease; } .transaction-item:hover { background-color: rgba(255, 255, 255, 0.03); } .transaction-icon { width: 36px; height: 36px; border-radius: 8px; display: flex; align-items: center; justify-content: center; background-color: var(--primary); } .transaction-details { flex-grow: 1; } .transaction-name { font-weight: 500; font-size: 0.9rem; margin-bottom: 3px; } .transaction-date { font-size: 0.75rem; color: var(--text-secondary); } .transaction-amount { font-weight: 600; font-size: 0.9rem; } .sell { color: var(--danger); } .buy { color: var(--success); } .alert-list { list-style: none; margin-top: 10px; overflow-y: auto; max-height: 270px; scrollbar-width: thin; scrollbar-color: var(--primary-light) var(--primary-dark); } .alert-list::-webkit-scrollbar { width: 5px; } .alert-list::-webkit-scrollbar-track { background: var(--primary-dark); } .alert-list::-webkit-scrollbar-thumb { background-color: var(--primary-light); border-radius: 10px; } .alert-item { padding: 12px; border-radius: 8px; margin-bottom: 10px; position: relative; background-color: rgba(74, 140, 202, 0.1); border-left: 3px solid var(--accent); transition: transform 0.2s ease; } .alert-item:hover { transform: translateX(5px); } .alert-item.warning { background-color: rgba(255, 193, 7, 0.1); border-left-color: #ffc107; } .alert-item.critical { background-color: rgba(255, 59, 48, 0.1); border-left-color: var(--danger); } .alert-title { font-weight: 500; font-size: 0.9rem; margin-bottom: 5px; } .alert-message { font-size: 0.8rem; color: var(--text-secondary); margin-bottom: 8px; } .alert-time { font-size: 0.7rem; color: var(--text-secondary); text-align: right; } .footer { margin-top: 20px; padding-top: 15px; border-top: 1px solid rgba(255, 255, 255, 0.05); display: flex; justify-content: space-between; align-items: center; } .data-status { display: flex; align-items: center; gap: 8px; font-size: 0.75rem; color: var(--text-secondary); } .status-dot { width: 8px; height: 8px; border-radius: 50%; background-color: var(--success); } .data-time { font-size: 0.75rem; color: var(--text-secondary); } @media (max-width: 700px) { .dashboard-grid { grid-template-columns: 1fr 1fr; } .market-overview { grid-column: span 2; } .portfolio-value { grid-column: span 2; } .recent-transactions { grid-column: span 2; } .alerts { grid-column: span 2; } } @media (max-width: 500px) { .dashboard-grid { grid-template-columns: 1fr; } .card { grid-column: span 1; } .chart-legend { flex-direction: column; gap: 5px; align-items: flex-start; margin-left: 20px; } } </style> </head> <body> <canvas id="particle-canvas"></canvas> <div class="container"> <div class="header"> <div class="logo"> <div class="logo-icon"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M3 9L12 5L21 9L12 13L3 9Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M3 9V16L12 20L21 16V9" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> </div> <span class="logo-text">FinFlow</span> </div> <div class="profile"> <div class="notification-bell"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M18 8C18 6.4087 17.3679 4.88258 16.2426 3.75736C15.1174 2.63214 13.5913 2 12 2C10.4087 2 8.88258 2.63214 7.75736 3.75736C6.63214 4.88258 6 6.4087 6 8C6 15 3 17 3 17H21C21 17 18 15 18 8Z" stroke="#B2C6D8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M13.73 21C13.5542 21.3031 13.3019 21.5547 12.9982 21.7295C12.6946 21.9044 12.3504 21.9965 12 21.9965C11.6496 21.9965 11.3054 21.9044 11.0018 21.7295C10.6982 21.5547 10.4458 21.3031 10.27 21" stroke="#B2C6D8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> <span class="notification-dot"></span> </div> <div class="avatar"> <span class="avatar-initial">JS</span> </div> </div> </div> <div class="dashboard-grid"> <div class="card market-overview"> <div class="card-header"> <h3 class="card-title">Market Overview</h3> <div class="card-actions"> <button class="btn-icon"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M12 3C11.175 3 10.5 3.675 10.5 4.5C10.5 5.325 11.175 6 12 6C12.825 6 13.5 5.325 13.5 4.5C13.5 3.675 12.825 3 12 3ZM12 18C11.175 18 10.5 18.675 10.5 19.5C10.5 20.325 11.175 21 12 21C12.825 21 13.5 20.325 13.5 19.5C13.5 18.675 12.825 18 12 18ZM12 10.5C11.175 10.5 10.5 11.175 10.5 12C10.5 12.825 11.175 13.5 12 13.5C12.825 13.5 13.5 12.825 13.5 12C13.5 11.175 12.825 10.5 12 10.5Z" fill="currentColor"/> </svg> </button> <button class="btn-icon"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M21 8.25H19.5M15 8.25H3M21 15.75H3M16.5 15.75H9" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/> <path d="M17.25 8.25C17.25 7.42157 16.5784 6.75 15.75 6.75C14.9216 6.75 14.25 7.42157 14.25 8.25C14.25 9.07843 14.9216 9.75 15.75 9.75C16.5784 9.75 17.25 9.07843 17.25 8.25Z" fill="currentColor"/> <path d="M9.75 15.75C9.75 14.9216 9.07843 14.25 8.25 14.25C7.42157 14.25 6.75 14.9216 6.75 15.75C6.75 16.5784 7.42157 17.25 8.25 17.25C9.07843 17.25 9.75 16.5784 9.75 15.75Z" fill="currentColor"/> </svg> </button> </div> </div> <div class="market-chart" id="market-chart"></div> <div class="chart-legend"> <div class="legend-item"> <span class="legend-color sp500"></span> <span>S&P 500: 4,782.46 <span class="positive">+0.46%</span></span> </div> <div class="legend-item"> <span class="legend-color nasdaq"></span> <span>NASDAQ: 15,203.78 <span class="positive">+0.61%</span></span> </div> <div class="legend-item"> <span class="legend-color dow"></span> <span>DOW: 37,905.45 <span class="positive">+0.34%</span></span> </div> </div> </div> <div class="card portfolio-value"> <div class="card-header"> <h3 class="card-title">Portfolio Value</h3> <div class="card-actions"> <button class="btn-icon"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M12 15V9M9 12H15M12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12C21 16.9706 16.9706 21 12 21Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> </svg> </button> </div> </div> <div class="price-data"> <span class="portfolio-value-amount">$143,567.28</span> <div class="portfolio-change positive"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M18 15L12 9L6 15" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> <span>+$2,847.39 (2.02%)</span> </div> </div> </div> <div class="card recent-transactions"> <div class="card-header"> <h3 class="card-title">Recent Transactions</h3> <div class="card-actions"> <button class="btn-icon"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M19 9L12 16L5 9" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> </button> </div> </div> <ul class="transaction-list"> <li class="transaction-item"> <div class="transaction-icon"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M16 3H8C7.46957 3 6.96086 3.21071 6.58579 3.58579C6.21071 3.96086 6 4.46957 6 5V19C6 19.5304 6.21071 20.0391 6.58579 20.4142C6.96086 20.7893 7.46957 21 8 21H16C16.5304 21 17.0391 20.7893 17.4142 20.4142C17.7893 20.0391 18 19.5304 18 19V5C18 4.46957 17.7893 3.96086 17.4142 3.58579C17.0391 3.21071 16.5304 3 16 3Z" stroke="#E8F0F7" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> <path d="M14 14L12 16L10 14" stroke="#E8F0F7" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> <path d="M12 8V16" stroke="#E8F0F7" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> </svg> </div> <div class="transaction-details"> <div class="transaction-name">AAPL Buy</div> <div class="transaction-date">Today, 11:23 AM</div> </div> <div class="transaction-amount buy">+$4,325.75</div> </li> <li class="transaction-item"> <div class="transaction-icon"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M16 3H8C7.46957 3 6.96086 3.21071 6.58579 3.58579C6.21071 3.96086 6 4.46957 6 5V19C6 19.5304 6.21071 20.0391 6.58579 20.4142C6.96086 20.7893 7.46957 21 8 21H16C16.5304 21 17.0391 20.7893 17.4142 20.4142C17.7893 20.0391 18 19.5304 18 19V5C18 4.46957 17.7893 3.96086 17.4142 3.58579C17.0391 3.21071 16.5304 3 16 3Z" stroke="#E8F0F7" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> <path d="M10 10L12 8L14 10" stroke="#E8F0F7" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> <path d="M12 16V8" stroke="#E8F0F7" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> </svg> </div> <div class="transaction-details"> <div class="transaction-name">MSFT Sell</div> <div class="transaction-date">Today, 09:47 AM</div> </div> <div class="transaction-amount sell">-$2,189.50</div> </li> <li class="transaction-item"> <div class="transaction-icon"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M16 3H8C7.46957 3 6.96086 3.21071 6.58579 3.58579C6.21071 3.96086 6 4.46957 6 5V19C6 19.5304 6.21071 20.0391 6.58579 20.4142C6.96086 20.7893 7.46957 21 8 21H16C16.5304 21 17.0391 20.7893 17.4142 20.4142C17.7893 20.0391 18 19.5304 18 19V5C18 4.46957 17.7893 3.96086 17.4142 3.58579C17.0391 3.21071 16.5304 3 16 3Z" stroke="#E8F0F7" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> <path d="M14 14L12 16L10 14" stroke="#E8F0F7" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> <path d="M12 8V16" stroke="#E8F0F7" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> </svg> </div> <div class="transaction-details"> <div class="transaction-name">TSLA Buy</div> <div class="transaction-date">Yesterday, 02:34 PM</div> </div> <div class="transaction-amount buy">+$3,712.00</div> </li> <li class="transaction-item"> <div class="transaction-icon"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M16 3H8C7.46957 3 6.96086 3.21071 6.58579 3.58579C6.21071 3.96086 6 4.46957 6 5V19C6 19.5304 6.21071 20.0391 6.58579 20.4142C6.96086 20.7893 7.46957 21 8 21H16C16.5304 21 17.0391 20.7893 17.4142 20.4142C17.7893 20.0391 18 19.5304 18 19V5C18 4.46957 17.7893 3.96086 17.4142 3.58579C17.0391 3.21071 16.5304 3 16 3Z" stroke="#E8F0F7" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> <path d="M14 14L12 16L10 14" stroke="#E8F0F7" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> <path d="M12 8V16" stroke="#E8F0F7" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> </svg> </div> <div class="transaction-details"> <div class="transaction-name">AMZN Buy</div> <div class="transaction-date">Yesterday, 10:19 AM</div> </div> <div class="transaction-amount buy">+$2,947.25</div> </li> <li class="transaction-item"> <div class="transaction-icon"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M16 3H8C7.46957 3 6.96086 3.21071 6.58579 3.58579C6.21071 3.96086 6 4.46957 6 5V19C6 19.5304 6.21071 20.0391 6.58579 20.4142C6.96086 20.7893 7.46957 21 8 21H16C16.5304 21 17.0391 20.7893 17.4142 20.4142C17.7893 20.0391 18 19.5304 18 19V5C18 4.46957 17.7893 3.96086 17.4142 3.58579C17.0391 3.21071 16.5304 3 16 3Z" stroke="#E8F0F7" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> <path d="M10 10L12 8L14 10" stroke="#E8F0F7" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> <path d="M12 16V8" stroke="#E8F0F7" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> </svg> </div> <div class="transaction-details"> <div class="transaction-name">NVDA Sell</div> <div class="transaction-date">Jan 24, 03:51 PM</div> </div> <div class="transaction-amount sell">-$1,875.20</div> </li> </ul> </div> <div class="card alerts"> <div class="card-header"> <h3 class="card-title">Market Alerts</h3> <div class="card-actions"> <button class="btn-icon"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M12 15V9M9 12H15M12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12C21 16.9706 16.9706 21 12 21Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> </svg> </button> </div> </div> <ul class="alert-list"> <li class="alert-item"> <div class="alert-title">TSLA Approaching Resistance</div> <div class="alert-message">Tesla stock approaching key resistance level at $220.50. Consider setting limit orders.</div> <div class="alert-time">15 min ago</div> </li> <li class="alert-item warning"> <div class="alert-title">Volatility Warning</div> <div class="alert-message">Market volatility increased 22% in the tech sector. Review stop losses on tech holdings.</div> <div class="alert-time">1 hour ago</div> </li> <li class="alert-item critical"> <div class="alert-title">Stop Loss Triggered</div> <div class="alert-message">Stop loss triggered for META at $475.25. Review position in portfolio.</div> <div class="alert-time">2 hours ago</div> </li> <li class="alert-item"> <div class="alert-title">Price Target Reached</div> <div class="alert-message">AAPL reached your price target of $195.75. Consider rebalancing position.</div> <div class="alert-time">Yesterday</div> </li> </ul> </div> </div> <div class="footer"> <div class="data-status"> <span class="status-dot"></span> <span>Live Data</span> </div> <div class="data-time">Last updated: Jan 25, 2023 1:45 PM EST</div> </div> </div> <script> // Particle Background class Particle { constructor(canvas, context, options = {}) { this.canvas = canvas; this.context = context; this.x = options.x || Math.random() * canvas.width; this.y = options.y || Math.random() * canvas.height; this.size = options.size || Math.random() * 1.5 + 0.5; this.speedX = options.speedX || (Math.random() - 0.5) * 0.5; this.speedY = options.speedY || (Math.random() - 0.5) * 0.2; this.opacity = options.opacity || Math.random() * 0.5 + 0.2; this.color = options.color || '#4a8cca'; } update() { this.x += this.speedX; this.y += this.speedY; if (this.x > this.canvas.width) { this.x = 0; } else if (this.x < 0) { this.x = this.canvas.width; } if (this.y > this.canvas.height) { this.y = 0; } else if (this.y < 0) { this.y = this.canvas.height; } } draw() { this.context.fillStyle = this.color; this.context.globalAlpha = this.opacity; this.context.beginPath(); this.context.arc(this.x, this.y, this.size, 0, Math.PI * 2); this.context.fill(); this.context.globalAlpha = 1; } connect(particles) { for (let i = 0; i < particles.length; i++) { const p = particles[i]; const distance = Math.sqrt((this.x - p.x) ** 2 + (this.y - p.y) ** 2); if (distance < 100) { this.context.beginPath(); this.context.strokeStyle = this.color; this.context.globalAlpha = 0.1 * (1 - distance / 100); this.context.lineWidth = 0.6; this.context.moveTo(this.x, this.y); this.context.lineTo(p.x, p.y); this.context.stroke(); this.context.globalAlpha = 1; } } } } class ParticleBackground { constructor(canvasId) { this.canvas = document.getElementById(canvasId); this.context = this.canvas.getContext('2d'); this.particles = []; this.particleCount = 80; this.colors = ['#4a8cca', '#2a5684', '#34c759', '#173857']; this.mouse = { x: undefined, y: undefined, radius: 150 }; this.init(); this.animate(); this.setEventListeners(); } init() {
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Particle Resonance - Interactive Art Installation</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { overflow: hidden; font-family: 'Helvetica Neue', Arial, sans-serif; color: #ffffff; background-color: #000000; height: 100vh; width: 100%; } canvas { position: absolute; top: 0; left: 0; z-index: 1; } #content { position: relative; z-index: 2; height: 100%; width: 100%; display: flex; flex-direction: column; justify-content: center; align-items: center; padding: 2rem; text-align: center; } h1 { font-size: 2.2rem; margin-bottom: 1rem; font-weight: 300; letter-spacing: 3px; text-transform: uppercase; opacity: 0; transform: translateY(20px); animation: fadeIn 1s ease-out forwards 0.5s; } p { max-width: 600px; line-height: 1.6; margin-bottom: 2rem; font-weight: 300; opacity: 0; transform: translateY(20px); animation: fadeIn 1s ease-out forwards 0.8s; } .interaction-modes { display: flex; justify-content: center; gap: 1rem; margin-bottom: 1rem; opacity: 0; transform: translateY(20px); animation: fadeIn 1s ease-out forwards 1.1s; } .mode-btn { background: rgba(255, 255, 255, 0.1); border: 1px solid rgba(255, 255, 255, 0.3); color: white; padding: 0.6rem 1.2rem; border-radius: 30px; cursor: pointer; transition: all 0.3s ease; font-size: 0.9rem; backdrop-filter: blur(5px); } .mode-btn:hover, .mode-btn.active { background: rgba(255, 255, 255, 0.25); transform: translateY(-2px); } .mode-btn.active { border-color: rgba(255, 255, 255, 0.8); box-shadow: 0 0 15px rgba(255, 255, 255, 0.5); } .controls { display: flex; gap: 1.5rem; margin-top: 1rem; opacity: 0; transform: translateY(20px); animation: fadeIn 1s ease-out forwards 1.4s; } .control-group { display: flex; flex-direction: column; align-items: center; } label { margin-bottom: 0.5rem; font-size: 0.85rem; opacity: 0.8; } input[type="range"] { width: 120px; -webkit-appearance: none; height: 6px; background: rgba(255, 255, 255, 0.2); border-radius: 3px; outline: none; } input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; width: 16px; height: 16px; border-radius: 50%; background: white; cursor: pointer; transition: all 0.2s ease; } input[type="range"]::-webkit-slider-thumb:hover { transform: scale(1.2); box-shadow: 0 0 10px rgba(255, 255, 255, 0.8); } .credits { position: absolute; bottom: 20px; font-size: 0.8rem; opacity: 0.6; transition: opacity 0.3s ease; } .credits:hover { opacity: 1; } @keyframes fadeIn { to { opacity: 1; transform: translateY(0); } } @media (max-width: 600px) { h1 { font-size: 1.5rem; } .interaction-modes { flex-wrap: wrap; } .controls { flex-direction: column; gap: 1rem; } .control-group { flex-direction: row; justify-content: space-between; width: 100%; max-width: 280px; } } </style> </head> <body> <canvas id="particleCanvas"></canvas> <div id="content"> <h1>Particle Resonance</h1> <p>Move your cursor or touch the screen to create ripples in the particle field. Explore different interaction patterns and color palettes to compose your own digital symphony.</p> <div class="interaction-modes"> <button class="mode-btn active" data-mode="attract">Attract</button> <button class="mode-btn" data-mode="repel">Repel</button> <button class="mode-btn" data-mode="vortex">Vortex</button> <button class="mode-btn" data-mode="wave">Wave</button> </div> <div class="controls"> <div class="control-group"> <label for="density">Particle Density</label> <input type="range" id="density" min="500" max="3000" value="1500"> </div> <div class="control-group"> <label for="sensitivity">Sensitivity</label> <input type="range" id="sensitivity" min="50" max="400" value="200"> </div> <div class="control-group"> <label for="colorMix">Color Harmony</label> <input type="range" id="colorMix" min="0" max="100" value="50"> </div> </div> <div class="credits">Move, explore, create.</div> </div> <script> document.addEventListener('DOMContentLoaded', function() { const canvas = document.getElementById('particleCanvas'); const ctx = canvas.getContext('2d'); const modeButtons = document.querySelectorAll('.mode-btn'); const densityControl = document.getElementById('density'); const sensitivityControl = document.getElementById('sensitivity'); const colorMixControl = document.getElementById('colorMix'); let width, height; let particles = []; let mouseX = 0, mouseY = 0; let interactionMode = 'attract'; let animationFrame; let touchDevice = false; let lastInteraction = 0; let colorSchemes = [ { // Neon Nights primary: [255, 0, 128], secondary: [0, 255, 255], accent: [255, 255, 0] }, { // Ocean Depths primary: [0, 100, 255], secondary: [0, 200, 180], accent: [140, 0, 255] }, { // Sunset Glow primary: [255, 100, 0], secondary: [255, 0, 100], accent: [100, 0, 255] }, { // Forest Magic primary: [0, 200, 100], secondary: [150, 255, 0], accent: [0, 100, 255] } ]; let currentColorScheme = 0; let colorTransitionProgress = 0; let targetColorScheme = 0; function resizeCanvas() { width = window.innerWidth; height = window.innerHeight; canvas.width = width; canvas.height = height; // Recreate particles on resize initParticles(); } function initParticles() { particles = []; const particleCount = parseInt(densityControl.value); for (let i = 0; i < particleCount; i++) { particles.push({ x: Math.random() * width, y: Math.random() * height, radius: Math.random() * 2 + 1, originalX: Math.random() * width, originalY: Math.random() * height, vx: 0, vy: 0, color: [0, 0, 0], // Will be calculated in render speed: Math.random() * 0.2 + 0.1 }); } } function render() { ctx.clearRect(0, 0, width, height); // Update color transition colorTransitionProgress += 0.005; if (colorTransitionProgress >= 1) { currentColorScheme = targetColorScheme; colorTransitionProgress = 0; // Randomly select next target color scheme let nextScheme; do { nextScheme = Math.floor(Math.random() * colorSchemes.length); } while (nextScheme === currentColorScheme); targetColorScheme = nextScheme; } // Calculate time-based dimming for interactions const currentTime = Date.now(); const timeSinceInteraction = currentTime - lastInteraction; const interactionStrength = Math.max(0, 1 - timeSinceInteraction / 2000); // Get sensitivity value const sensitivity = parseInt(sensitivityControl.value); const colorMix = parseInt(colorMixControl.value) / 100; particles.forEach(p => { // Reset acceleration let ax = 0, ay = 0; // Calculate distance to mouse const dx = mouseX - p.x; const dy = mouseY - p.y; const distSq = dx * dx + dy * dy; const dist = Math.sqrt(distSq); if (dist < sensitivity) { // Create forces based on interaction mode const force = (1 - dist / sensitivity) * interactionStrength; switch(interactionMode) { case 'attract': ax = dx * force * 0.02; ay = dy * force * 0.02; break; case 'repel': ax = -dx * force * 0.05; ay = -dy * force * 0.05; break; case 'vortex': ax = dy * force * 0.03; ay = -dx * force * 0.03; break; case 'wave': const angle = Math.atan2(dy, dx); const waveFactor = Math.sin(dist / 10) * force * 0.1; ax = Math.cos(angle) * waveFactor; ay = Math.sin(angle) * waveFactor; break; } } // Spring force toward original position const hx = p.originalX - p.x; const hy = p.originalY - p.y; ax += hx * 0.01; ay += hy * 0.01; // Update velocity with damping p.vx = p.vx * 0.95 + ax; p.vy = p.vy * 0.95 + ay; // Update position p.x += p.vx; p.y += p.vy; // Calculate color based on position and velocity const speed = Math.sqrt(p.vx * p.vx + p.vy * p.vy) * 2; const normalizedX = p.x / width; const normalizedY = p.y / height; const distToMouse = Math.sqrt(distSq) / sensitivity; // Interpolate between current and target color scheme const cs1 = colorSchemes[currentColorScheme]; const cs2 = colorSchemes[targetColorScheme]; const primaryColor = [ cs1.primary[0] * (1 - colorTransitionProgress) + cs2.primary[0] * colorTransitionProgress, cs1.primary[1] * (1 - colorTransitionProgress) + cs2.primary[1] * colorTransitionProgress, cs1.primary[2] * (1 - colorTransitionProgress) + cs2.primary[2] * colorTransitionProgress ]; const secondaryColor = [ cs1.secondary[0] * (1 - colorTransitionProgress) + cs2.secondary[0] * colorTransitionProgress, cs1.secondary[1] * (1 - colorTransitionProgress) + cs2.secondary[1] * colorTransitionProgress, cs1.secondary[2] * (1 - colorTransitionProgress) + cs2.secondary[2] * colorTransitionProgress ]; const accentColor = [ cs1.accent[0] * (1 - colorTransitionProgress) + cs2.accent[0] * colorTransitionProgress, cs1.accent[1] * (1 - colorTransitionProgress) + cs2.accent[1] * colorTransitionProgress, cs1.accent[2] * (1 - colorTransitionProgress) + cs2.accent[2] * colorTransitionProgress ]; // Mix colors based on position, speed, and user settings const mixFactorX = normalizedX * colorMix + (1 - colorMix) * 0.5; const mixFactorY = normalizedY; const speedFactor = Math.min(1, speed * 3); p.color[0] = Math.round( primaryColor[0] * (1 - mixFactorX) + secondaryColor[0] * mixFactorX * (1 - speedFactor) + accentColor[0] * speedFactor ); p.color[1] = Math.round( primaryColor[1] * (1 - mixFactorY) + secondaryColor[1] * mixFactorY * (1 - speedFactor) + accentColor[1] * speedFactor ); p.color[2] = Math.round( primaryColor[2] * mixFactorX + secondaryColor[2] * (1 - mixFactorX) * (1 - speedFactor) + accentColor[2] * speedFactor ); // Draw particle const alpha = 0.7 + speedFactor * 0.3; const radius = p.radius * (1 + speedFactor); ctx.beginPath(); ctx.arc(p.x, p.y, radius, 0, Math.PI * 2); ctx.fillStyle = `rgba(${p.color[0]}, ${p.color[1]}, ${p.color[2]}, ${alpha})`; ctx.fill(); // Draw connecting lines to nearby particles if (speed > 0.5) { particles.forEach(p2 => { const dx2 = p.x - p2.x; const dy2 = p.y - p2.y; const dist2 = Math.sqrt(dx2 * dx2 + dy2 * dy2); if (dist2 < 50 && dist2 > 0) { ctx.beginPath(); ctx.moveTo(p.x, p.y); ctx.lineTo(p2.x, p2.y); ctx.strokeStyle = `rgba(${p.color[0]}, ${p.color[1]}, ${p.color[2]}, ${(1 - dist2 / 50) * 0.3})`; ctx.stroke(); } }); } }); animationFrame = requestAnimationFrame(render); } function initEventListeners() { window.addEventListener('resize', resizeCanvas); // Mouse events canvas.addEventListener('mousemove', e => { mouseX = e.clientX; mouseY = e.clientY; lastInteraction = Date.now(); if (!touchDevice) { updateBackground(); } }); // Touch events canvas.addEventListener('touchstart', e => { touchDevice = true; if (e.touches.length > 0) { mouseX = e.touches[0].clientX; mouseY = e.touches[0].clientY; lastInteraction = Date.now(); updateBackground(); } e.preventDefault(); }); canvas.addEventListener('touchmove', e => { if (e.touches.length > 0) { mouseX = e.touches[0].clientX; mouseY = e.touches[0].clientY; lastInteraction = Date.now(); updateBackground(); } e.preventDefault(); }); // Mode buttons modeButtons.forEach(btn => { btn.addEventListener('click', () => { modeButtons.forEach(b => b.classList.remove('active')); btn.classList.add('active'); interactionMode = btn.getAttribute('data-mode'); lastInteraction = Date.now(); }); }); // Controls densityControl.addEventListener('input', initParticles); sensitivityControl.addEventListener('input', () => lastInteraction = Date.now()); colorMixControl.addEventListener('input', () => { colorTransitionProgress = 0; targetColorScheme = (currentColorScheme + 1) % colorSchemes.length; }); } function updateBackground() { // Subtle background color update based on interaction document.body.style.background = `radial-gradient(circle at ${mouseX}px ${mouseY}px, rgba(40, 40, 60, 1) 0%, rgba(10, 10, 20, 1) 70%, rgba(0, 0, 0, 1) 100%)`; } function init() { resizeCanvas(); initEventListeners(); initParticles(); render(); } init(); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Minim // Portfolio</title> <style> /* Base Styles & Typography */ @import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&display=swap'); :root { --bg-color: #0f0f0f; --text-primary: #f2f2f2; --text-secondary: #a0a0a0; --accent: #e6e6e6; --accent-hover: #ffffff; --particle-color: rgba(255, 255, 255, 0.6); --spacing-sm: 1rem; --spacing-md: 2rem; --spacing-lg: 3rem; --transition: 0.3s cubic-bezier(0.25, 0.1, 0.25, 1); } * { margin: 0; padding: 0; box-sizing: border-box; } body, html { font-family: 'Space Grotesk', sans-serif; background-color: var(--bg-color); color: var(--text-primary); overflow-x: hidden; height: 100%; width: 100%; font-size: 16px; line-height: 1.5; -webkit-font-smoothing: antialiased; scroll-behavior: smooth; } h1, h2, h3, h4 { font-weight: 500; line-height: 1.2; } h1 { font-size: 2.5rem; letter-spacing: -0.03em; } h2 { font-size: 1.75rem; letter-spacing: -0.02em; margin-bottom: var(--spacing-sm); } p { margin-bottom: var(--spacing-sm); color: var(--text-secondary); } a { color: var(--text-primary); text-decoration: none; transition: var(--transition); } /* Particle Canvas */ #particles-canvas { position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: -1; pointer-events: none; } /* Layout Components */ .container { max-width: 100%; width: 100%; padding: 0 var(--spacing-md); margin: 0 auto; height: 100%; display: flex; flex-direction: column; } header { padding: var(--spacing-md) 0; display: flex; justify-content: space-between; align-items: center; } .logo { font-weight: 600; font-size: 1.4rem; display: flex; align-items: center; } .logo-dot { width: 6px; height: 6px; background-color: var(--accent); border-radius: 50%; margin-left: 4px; display: inline-block; } nav ul { display: flex; list-style: none; gap: var(--spacing-sm); } nav a { position: relative; padding: 0.25rem 0; overflow: hidden; } nav a::after { content: ''; position: absolute; bottom: 0; left: 0; width: 100%; height: 1px; background-color: var(--accent); transform: translateX(-101%); transition: transform 0.3s ease; } nav a:hover::after { transform: translateX(0); } .main-content { flex: 1; display: flex; flex-direction: column; justify-content: center; padding: var(--spacing-md) 0; } .intro { max-width: 600px; margin-bottom: var(--spacing-lg); } .title-highlight { position: relative; display: inline-block; } .title-highlight::after { content: ''; position: absolute; width: 100%; height: 8px; bottom: 4px; left: 0; background-color: rgba(255, 255, 255, 0.1); z-index: -1; } .projects { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: var(--spacing-md); width: 100%; } .project-card { background-color: rgba(255, 255, 255, 0.03); border-radius: 4px; padding: var(--spacing-md); position: relative; transition: var(--transition); cursor: pointer; overflow: hidden; min-height: 250px; display: flex; flex-direction: column; } .project-card::before { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: linear-gradient(to bottom right, rgba(255, 255, 255, 0.03) 0%, rgba(255, 255, 255, 0) 100%); z-index: -1; } .project-card:hover { transform: translateY(-5px); background-color: rgba(255, 255, 255, 0.05); box-shadow: 0 15px 30px rgba(0, 0, 0, 0.1); } .project-number { font-size: 0.875rem; color: var(--text-secondary); margin-bottom: var(--spacing-sm); } .project-tags { margin-top: auto; display: flex; flex-wrap: wrap; gap: 0.5rem; } .tag { font-size: 0.75rem; padding: 0.25rem 0.75rem; background-color: rgba(255, 255, 255, 0.05); border-radius: 20px; } .view-project { margin-top: var(--spacing-sm); display: inline-flex; align-items: center; font-size: 0.875rem; } .view-project svg { margin-left: 4px; transition: var(--transition); } .view-project:hover svg { transform: translateX(3px); } .contact-btn { display: inline-block; margin-top: var(--spacing-md); padding: 0.75rem 1.5rem; background-color: rgba(255, 255, 255, 0.07); border-radius: 4px; border: 1px solid rgba(255, 255, 255, 0.1); cursor: pointer; transition: var(--transition); position: relative; overflow: hidden; } .contact-btn:hover { background-color: rgba(255, 255, 255, 0.1); border-color: rgba(255, 255, 255, 0.2); } .contact-btn::after { content: ''; position: absolute; height: 50px; width: 100px; background: rgba(255, 255, 255, 0.1); filter: blur(30px); top: -20px; left: -100px; transform: rotate(45deg); transition: all 0.7s ease; } .contact-btn:hover::after { left: 120%; } footer { padding: var(--spacing-md) 0; text-align: center; font-size: 0.875rem; color: var(--text-secondary); } /* Modal */ .modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.9); display: flex; align-items: center; justify-content: center; z-index: 1000; opacity: 0; pointer-events: none; transition: opacity 0.3s ease; } .modal.active { opacity: 1; pointer-events: auto; } .modal-content { background-color: var(--bg-color); border-radius: 4px; max-width: 500px; width: 90%; padding: var(--spacing-md); position: relative; transform: translateY(20px); transition: transform 0.3s ease; } .modal.active .modal-content { transform: translateY(0); } .close-modal { position: absolute; top: var(--spacing-sm); right: var(--spacing-sm); background: none; border: none; color: var(--text-primary); font-size: 1.5rem; cursor: pointer; } .modal h2 { margin-bottom: var(--spacing-sm); } .form-group { margin-bottom: var(--spacing-sm); } label { display: block; margin-bottom: 0.5rem; font-size: 0.875rem; } input, textarea { width: 100%; padding: 0.75rem; background-color: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 4px; color: var(--text-primary); font-family: inherit; resize: vertical; transition: var(--transition); } input:focus, textarea:focus { outline: none; border-color: rgba(255, 255, 255, 0.3); background-color: rgba(255, 255, 255, 0.07); } .submit-btn { background-color: rgba(255, 255, 255, 0.1); color: var(--text-primary); border: none; padding: 0.75rem 1.5rem; border-radius: 4px; cursor: pointer; transition: var(--transition); font-family: inherit; margin-top: 0.5rem; } .submit-btn:hover { background-color: rgba(255, 255, 255, 0.2); } /* Loading indicator */ .loading-indicator { width: 24px; height: 24px; border: 2px solid rgba(255, 255, 255, 0.3); border-radius: 50%; border-top-color: var(--text-primary); animation: spin 1s linear infinite; margin: 0 auto; display: none; } @keyframes spin { to { transform: rotate(360deg); } } /* Media Queries */ @media (max-width: 768px) { h1 { font-size: 2rem; } .projects { grid-template-columns: 1fr; } nav ul { gap: var(--spacing-sm); } } @media (max-width: 480px) { h1 { font-size: 1.75rem; } .container { padding: 0 var(--spacing-sm); } header { flex-direction: column; align-items: flex-start; gap: var(--spacing-sm); } nav ul { flex-wrap: wrap; } } </style> </head> <body> <canvas id="particles-canvas"></canvas> <div class="container"> <header> <a href="#" class="logo">minim<span class="logo-dot"></span></a> <nav> <ul> <li><a href="#work">Work</a></li> <li><a href="#about">About</a></li> <li><a href="#" class="contact-link">Contact</a></li> </ul> </nav> </header> <main class="main-content"> <section class="intro"> <h1>Creating <span class="title-highlight">minimal</span> digital experiences with maximal impact</h1> <p>Interactive designer specializing in clean, functional interfaces and thoughtful user experiences. I craft digital solutions that balance artistic expression with purposeful functionality.</p> <button class="contact-btn">Let's collaborate</button> </section> <section id="work" class="projects"> <div class="project-card"> <div class="project-number">01</div> <h2>Aether</h2> <p>An e-commerce platform reimagined with minimalist principles, focusing on white space and typography.</p> <div class="project-tags"> <span class="tag">UX/UI Design</span> <span class="tag">E-commerce</span> </div> <a href="#" class="view-project"> View case study <svg width="14" height="14" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M5 12H19M19 12L12 5M19 12L12 19" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> </a> </div> <div class="project-card"> <div class="project-number">02</div> <h2>Monochrome</h2> <p>A photography portfolio showcasing visual narratives through considered design and subtle interactions.</p> <div class="project-tags"> <span class="tag">Web Design</span> <span class="tag">Photography</span> </div> <a href="#" class="view-project"> View case study <svg width="14" height="14" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M5 12H19M19 12L12 5M19 12L12 19" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> </a> </div> <div class="project-card"> <div class="project-number">03</div> <h2>Lumin</h2> <p>A dashboard redesign focusing on data hierarchy, visualization, and intuitive interaction patterns.</p> <div class="project-tags"> <span class="tag">Product Design</span> <span class="tag">SaaS</span> </div> <a href="#" class="view-project"> View case study <svg width="14" height="14" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M5 12H19M19 12L12 5M19 12L12 19" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> </a> </div> <div class="project-card"> <div class="project-number">04</div> <h2>Nomad</h2> <p>A travel companion app with a distraction-free interface highlighting essential information for travelers.</p> <div class="project-tags"> <span class="tag">Mobile App</span> <span class="tag">Travel</span> </div> <a href="#" class="view-project"> View case study <svg width="14" height="14" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M5 12H19M19 12L12 5M19 12L12 19" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> </a> </div> </section> </main> <footer> <p>© 2023 · Designed & developed with purpose</p> </footer> </div> <div class="modal" id="contact-modal"> <div class="modal-content"> <button class="close-modal">×</button> <h2>Get in touch</h2> <p>Interested in working together? Let me know what you're looking for.</p> <form id="contact-form"> <div class="form-group"> <label for="name">Name</label> <input type="text" id="name" required> </div> <div class="form-group"> <label for="email">Email</label> <input type="email" id="email" required> </div> <div class="form-group"> <label for="message">Message</label> <textarea id="message" rows="4" required></textarea> </div> <button type="submit" class="submit-btn">Send message</button> <div class="loading-indicator"></div> </form> </div> </div> <script> // Particle background class Particle { constructor(canvas, options = {}) { this.canvas = canvas; this.ctx = canvas.getContext('2d'); this.x = options.x || Math.random() * canvas.width; this.y = options.y || Math.random() * canvas.height; this.size = options.size || Math.random() * 1 + 0.5; this.speedX = options.speedX || (Math.random() - 0.5) * 0.3; this.speedY = options.speedY || (Math.random() - 0.5) * 0.3; this.color = options.color || 'rgba(255, 255, 255, 0.6)'; this.alpha = options.alpha || Math.random() * 0.3 + 0.2; this.trail = []; this.trailLength = options.trailLength || Math.floor(Math.random() * 5) + 2; } update() { // Update position this.x += this.speedX; this.y += this.speedY; // Add current position to trail this.trail.unshift({ x: this.x, y: this.y }); if (this.trail.length > this.trailLength) { this.trail.pop(); } // Wrap around edges if (this.x < 0) this.x = this.canvas.width; if (this.x > this.canvas.width) this.x = 0; if (this.y < 0) this.y = this.canvas.height; if (this.y > this.canvas.height) this.y = 0; // Random movement variation if (Math.random() < 0.01) { this.speedX = (Math.random() - 0.5) * 0.3; this.speedY = (Math.random() - 0.5) * 0.3; } } draw() { // Draw trail for (let i = 0; i < this.trail.length; i++) { const t = this.trail[i]; const alpha = (1 - i / this.trail.length) * this.alpha; const size = this.size * (1 - i / this.trail.length); this.ctx.beginPath(); this.ctx.arc(t.x, t.y, size, 0, Math.PI * 2); this.ctx.fillStyle = this.color.replace('0.6', alpha); this.ctx.fill(); } } } class ParticleSystem { constructor(canvasId, options = {}) { this.canvas = document.getElementById(canvasId); this.ctx = this.canvas.getContext('2d'); this.particles = []; this.particleCount = options.particleCount || 50; this.mouseX = 0; this.mouseY = 0; this.isMouseMoving = false; this.mouseTimeout = null; this.setup(); this.createParticles(); this.animate(); this.addEventListeners(); } setup() { this.resizeCanvas(); window.addEventListener('resize', () => this.resizeCanvas()); } resizeCanvas() { this.canvas.width = window.innerWidth; this.canvas.height = window.innerHeight; } createParticles() { for (let i = 0; i < this.particleCount; i++) { this.particles.push(new Particle(this.canvas)); } } addEventListeners() { document.addEventListener('mousemove', (e) => { this.mouseX = e.clientX; this.mouseY = e.clientY; this.isMouseMoving = true; // Reset timeout clearTimeout(this.mouseTimeout); this.mouseTimeout = setTimeout(() => { this.isMouseMoving = false; }, 100); // Create a new particle at mouse position occasionally if (Math.random() < 0.1) { this.particles.push(new Particle(this.canvas, { x: this.mouseX, y: this.mouseY, size: Math.random() * 2 + 1, alpha: Math.random() * 0.4 + 0.3, trailLength: Math.floor(Math.random() * 8) + 3 })); // Keep particle count in check if (this.particles.length > this.particleCount + 10) { this.particles.splice(0, this.particles.length - this.particleCount); } } }); } animate() { this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); this.particles.forEach(particle => { particle.update(); particle.draw(); // Influence particles when mouse is moving if (this.isMouseMoving) { const dx = particle.x - this.mouseX; const dy = particle.y - this.mouseY; const distance = Math.sqrt(dx * dx + dy * dy); const maxDistance = 100; if (distance < maxDistance) { const force = (maxDistance - distance) / maxDistance; const angle = Math.atan2(dy, dx); particle.speedX += Math.cos(angle) * force * 0.02; particle.speedY += Math.sin(angle) * force * 0.02; } } }); // Draw connections between nearby particles for (let i = 0; i < this.particles.length; i++) { for (let j = i + 1; j < this.particles.length; j++) { const p1 = this.particles[i]; const p2 = this.particles[j]; const dx = p1.x - p2.x; const dy = p1.y - p2.y; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < 70) { this.ctx.beginPath(); this.ctx.strokeStyle = `rgba(255, 255, 255, ${0.1 * (1 - distance/70)})`; this.ctx.lineWidth = 0.5; this.ctx.moveTo(p1.x, p1.y); this.ctx.lineTo(p2.x, p2.y); this.ctx.stroke(); } } } requestAnimationFrame(() => this.animate()); } } // Initialize particles when window is loaded window.addEventListener('load', () => { new ParticleSystem('particles-canvas', { particleCount: window.innerWidth < 768 ? 30 : 50 }); // Modal functionality const modal = document.getElementById('contact-modal'); const contactButtons = document.querySelectorAll('.contact-btn, .contact-link'); const closeModal = document.querySelector('.close-modal'); contactButtons.forEach(btn => { btn.addEventListener('click', (e) => { e.preventDefault(); modal.classList.add('active'); document.body.style.overflow = 'hidden'; }); }); closeModal.addEventListener('click', () => { modal.classList.remove('active'); document.body.style.overflow = ''; }); // Close modal when clicking outside modal.addEventListener('click', (e) => { if (e.target === modal) { modal.classList.remove('active'); document.body.style.overflow = ''; } }); // Form submission handling const contactForm = document.getElementById('contact-form'); const loadingIndicator = document.querySelector('.loading-indicator'); contactForm.addEventListener('submit', (e) => { e.preventDefault(); const nameInput = document.getElementById('name'); const emailInput = document.getElementById('email'); const messageInput = document.getElementById('message'); // Show loading indicator loadingIndicator.style.display = 'block'; // Simulate form submission setTimeout(() => { loadingIndicator.style.display = 'none'; // Reset form nameInput.value = ''; emailInput.value = ''; messageInput.value = ''; // Show success message const successMessage = document.createElement('div'); successMessage.textContent = 'Message sent successfully!'; successMessage.style.color = '#a0a0a0'; successMessage.style.marginTop = '1rem'; contactForm.appendChild(successMessage); // Remove success message after a delay setTimeout(() => { successMessage.remove(); modal.classList.remove('active'); document.body.style.overflow = ''; }, 2000); }, 1500); }); // Project card hover effect enhancement const projectCards = document.querySelectorAll('.project-card'); projectCards.forEach(card => { card.addEventListener('mouseenter', function() { const others = [...projectCards].filter(c => c !== this); others.forEach(other => { other.style.opacity = '0.5'; other.style.transform = 'scale(0.98)'; }); }); card.addEventListener('mouseleave', function() { projectCards.forEach(card => { card.style.opacity = '1'; card.style.transform = ''; }); }); }); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Soundwave - Visualize Your Music</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; } :root { --neon-pink: #ff2a6d; --neon-blue: #05d9e8; --neon-purple: #a239ea; --neon-green: #39ff14; --dark-bg: #0f0e17; --darker-bg: #070610; } body { background: var(--darker-bg); color: white; height: 700px; width: 700px; overflow: hidden; position: relative; } #canvas { position: absolute; top: 0; left: 0; z-index: 1; } .content { position: relative; z-index: 2; height: 100%; display: flex; flex-direction: column; padding: 2rem; } header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 2rem; } .logo { font-size: 1.8rem; font-weight: 800; background: linear-gradient(to right, var(--neon-blue), var(--neon-purple)); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; position: relative; } .logo::after { content: ""; position: absolute; height: 6px; width: 6px; background-color: var(--neon-pink); border-radius: 50%; bottom: 5px; right: -8px; } .controls { display: flex; gap: 1rem; } .btn { background: rgba(255, 255, 255, 0.1); border: none; color: white; padding: 0.5rem 1rem; border-radius: 50px; cursor: pointer; transition: all 0.3s ease; backdrop-filter: blur(5px); } .btn:hover { background: rgba(255, 255, 255, 0.2); transform: translateY(-2px); } .btn-primary { background: linear-gradient(45deg, var(--neon-pink), var(--neon-purple)); box-shadow: 0 0 15px rgba(162, 57, 234, 0.5); } .btn-primary:hover { background: linear-gradient(45deg, var(--neon-purple), var(--neon-pink)); box-shadow: 0 0 20px rgba(162, 57, 234, 0.7); } .playback-container { flex: 1; display: flex; flex-direction: column; justify-content: center; align-items: center; text-align: center; gap: 2rem; } .now-playing { text-align: center; } .track-info { margin-bottom: 1rem; } .track-title { font-size: 2.5rem; font-weight: 700; margin-bottom: 0.5rem; background: linear-gradient(to right, var(--neon-blue), var(--neon-pink)); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; } .track-artist { font-size: 1.2rem; color: rgba(255, 255, 255, 0.7); margin-bottom: 1rem; } .playback-controls { display: flex; align-items: center; gap: 1.5rem; margin-bottom: 2rem; } .playback-btn { background: none; border: none; color: white; cursor: pointer; transition: all 0.3s ease; display: flex; align-items: center; justify-content: center; } .play-btn { height: 60px; width: 60px; border-radius: 50%; background: linear-gradient(45deg, var(--neon-blue), var(--neon-purple)); box-shadow: 0 0 20px rgba(5, 217, 232, 0.5); } .play-btn:hover { transform: scale(1.05); box-shadow: 0 0 30px rgba(5, 217, 232, 0.7); } .playback-btn svg { fill: white; width: 24px; height: 24px; } .play-btn svg { width: 30px; height: 30px; } .progress-container { width: 80%; margin: 0 auto; } .progress-bar { width: 100%; height: 4px; background: rgba(255, 255, 255, 0.1); border-radius: 50px; overflow: hidden; position: relative; margin-bottom: 0.5rem; } .progress { position: absolute; height: 100%; background: linear-gradient(to right, var(--neon-blue), var(--neon-pink)); border-radius: 50px; width: 35%; transition: width 0.2s ease; } .progress-time { display: flex; justify-content: space-between; font-size: 0.75rem; color: rgba(255, 255, 255, 0.6); } .eq-visualizer { display: flex; gap: 3px; height: 40px; align-items: flex-end; margin-bottom: 1rem; } .eq-bar { width: 4px; background: var(--neon-purple); border-radius: 2px; transition: height 0.2s ease; } .playlist { background: rgba(15, 14, 23, 0.7); backdrop-filter: blur(10px); border-radius: 16px; padding: 1.5rem; margin-top: 2rem; } .playlist-title { font-size: 1.2rem; margin-bottom: 1rem; color: rgba(255, 255, 255, 0.9); display: flex; align-items: center; gap: 0.5rem; } .playlist-title svg { fill: var(--neon-blue); width: 18px; height: 18px; } .playlist-tracks { display: flex; gap: 1rem; overflow-x: auto; padding-bottom: 0.5rem; scrollbar-width: thin; scrollbar-color: var(--neon-purple) rgba(255, 255, 255, 0.1); } .playlist-tracks::-webkit-scrollbar { height: 5px; } .playlist-tracks::-webkit-scrollbar-track { background: rgba(255, 255, 255, 0.1); border-radius: 10px; } .playlist-tracks::-webkit-scrollbar-thumb { background: var(--neon-purple); border-radius: 10px; } .track-card { min-width: 150px; border-radius: 8px; overflow: hidden; transition: all 0.3s ease; position: relative; cursor: pointer; } .track-card:hover { transform: translateY(-5px); } .track-card:hover .track-img { transform: scale(1.05); } .track-img-container { height: 150px; overflow: hidden; border-radius: 8px; position: relative; } .track-img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.5s ease; } .track-overlay { position: absolute; bottom: 0; left: 0; right: 0; padding: 1rem 0.5rem 0.5rem; background: linear-gradient(to top, rgba(0, 0, 0, 0.8), transparent); transform: translateY(100%); transition: transform 0.3s ease; } .track-card:hover .track-overlay { transform: translateY(0); } .track-card-title { font-size: 0.9rem; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-top: 0.5rem; } .track-card-artist { font-size: 0.7rem; color: rgba(255, 255, 255, 0.7); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .info-card { position: absolute; bottom: 1.5rem; right: 1.5rem; background: rgba(15, 14, 23, 0.8); backdrop-filter: blur(10px); border-radius: 8px; padding: 0.75rem; display: flex; align-items: center; gap: 0.5rem; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); transition: all 0.3s ease; z-index: 5; transform: translateY(100px); opacity: 0; animation: slideUp 0.5s ease 1s forwards; } @keyframes slideUp { to { transform: translateY(0); opacity: 1; } } .info-icon { display: flex; align-items: center; justify-content: center; height: 30px; width: 30px; border-radius: 50%; background: linear-gradient(45deg, var(--neon-blue), var(--neon-purple)); } .info-icon svg { width: 15px; height: 15px; fill: white; } .info-text { font-size: 0.75rem; color: rgba(255, 255, 255, 0.8); } @media (max-width: 600px) { .content { padding: 1.5rem; } .track-title { font-size: 2rem; } .progress-container { width: 90%; } .playlist { padding: 1rem; } } @media (max-width: 400px) { .content { padding: 1rem; } .track-title { font-size: 1.5rem; } .playback-controls { gap: 1rem; } .play-btn { height: 50px; width: 50px; } .track-card { min-width: 120px; } .track-img-container { height: 120px; } } #volume-control { position: absolute; bottom: 1.5rem; left: 1.5rem; background: rgba(15, 14, 23, 0.8); backdrop-filter: blur(10px); border-radius: 50px; padding: 0.5rem 1rem; display: flex; align-items: center; gap: 0.75rem; z-index: 5; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); } #volume-icon { width: 20px; height: 20px; fill: white; } #volume-slider { -webkit-appearance: none; appearance: none; width: 80px; height: 4px; border-radius: 2px; background: rgba(255, 255, 255, 0.2); outline: none; } #volume-slider::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 12px; height: 12px; border-radius: 50%; background: white; cursor: pointer; transition: all 0.3s ease; } #volume-slider::-webkit-slider-thumb:hover { transform: scale(1.2); background: var(--neon-pink); } #volume-slider::-moz-range-thumb { width: 12px; height: 12px; border-radius: 50%; background: white; cursor: pointer; border: none; transition: all 0.3s ease; } #volume-slider::-moz-range-thumb:hover { transform: scale(1.2); background: var(--neon-pink); } #audio-source { display: none; } </style> </head> <body> <canvas id="canvas"></canvas> <audio id="audio-source" loop> <source src="https://soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3" type="audio/mpeg"> </audio> <div class="content"> <header> <div class="logo">Soundwave</div> <div class="controls"> <button class="btn">Explore</button> <button class="btn btn-primary">Upgrade</button> </div> </header> <div class="playback-container"> <div class="now-playing"> <div class="track-info"> <h1 class="track-title">Midnight Pulse</h1> <p class="track-artist">ELECTRA feat. Neon Lights</p> </div> <div class="eq-visualizer" id="eq-visualizer"> <!-- EQ bars will be added via JS --> </div> <div class="playback-controls"> <button class="playback-btn"> <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <path d="M7 6C5.9 6 5 6.9 5 8V16C5 17.1 5.9 18 7 18H9C10.1 18 11 17.1 11 16V8C11 6.9 10.1 6 9 6H7ZM15 6C13.9 6 13 6.9 13 8V16C13 17.1 13.9 18 15 18H17C18.1 18 19 17.1 19 16V8C19 6.9 18.1 6 17 6H15Z"/> </svg> </button> <button class="playback-btn play-btn" id="play-btn"> <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <path d="M8 5V19L19 12L8 5Z"/> </svg> </button> <button class="playback-btn"> <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <path d="M6 18L14.5 12L6 6V18ZM16 6V18H18V6H16Z"/> </svg> </button> </div> </div> <div class="progress-container"> <div class="progress-bar"> <div class="progress" id="progress"></div> </div> <div class="progress-time"> <span>1:45</span> <span>4:30</span> </div> </div> </div> <div class="playlist"> <div class="playlist-title"> <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <path d="M12 3V13.55C11.41 13.21 10.73 13 10 13C7.79 13 6 14.79 6 17C6 19.21 7.79 21 10 21C12.21 21 14 19.21 14 17V7H18V3H12ZM10 19C8.9 19 8 18.1 8 17C8 15.9 8.9 15 10 15C11.1 15 12 15.9 12 17C12 18.1 11.1 19 10 19Z"/> </svg> <span>Ambient Electronics</span> </div> <div class="playlist-tracks"> <div class="track-card"> <div class="track-img-container"> <img src="https://source.unsplash.com/random/300x300/?cyberpunk,neon" alt="Track" class="track-img"> <div class="track-overlay"> <svg viewBox="0 0 24 24" width="24" height="24" fill="white"> <path d="M8 5V19L19 12L8 5Z"/> </svg> </div> </div> <h3 class="track-card-title">Night Drive</h3> <p class="track-card-artist">Synth Collective</p> </div> <div class="track-card"> <div class="track-img-container"> <img src="https://source.unsplash.com/random/300x300/?concert,music" alt="Track" class="track-img"> <div class="track-overlay"> <svg viewBox="0 0 24 24" width="24" height="24" fill="white"> <path d="M8 5V19L19 12L8 5Z"/> </svg> </div> </div> <h3 class="track-card-title">Electric Dreams</h3> <p class="track-card-artist">Aurora Waves</p> </div> <div class="track-card"> <div class="track-img-container"> <img src="https://source.unsplash.com/random/300x300/?dj,electronic" alt="Track" class="track-img"> <div class="track-overlay"> <svg viewBox="0 0 24 24" width="24" height="24" fill="white"> <path d="M8 5V19L19 12L8 5Z"/> </svg> </div> </div> <h3 class="track-card-title">Midnight Pulse</h3> <p class="track-card-artist">ELECTRA</p> </div> <div class="track-card"> <div class="track-img-container"> <img src="https://source.unsplash.com/random/300x300/?techno,club" alt="Track" class="track-img"> <div class="track-overlay"> <svg viewBox="0 0 24 24" width="24" height="24" fill="white"> <path d="M8 5V19L19 12L8 5Z"/> </svg> </div> </div> <h3 class="track-card-title">Neon Horizons</h3> <p class="track-card-artist">Digital Dreams</p> </div> </div> </div> </div> <div id="volume-control"> <svg id="volume-icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <path d="M3 9V15H7L12 20V4L7 9H3ZM16.5 12C16.5 10.23 15.48 8.71 14 7.97V16.02C15.48 15.29 16.5 13.77 16.5 12ZM14 3.23V5.29C16.89 6.15 19 8.83 19 12C19 15.17 16.89 17.85 14 18.71V20.77C18.01 19.86 21 16.28 21 12C21 7.72 18.01 4.14 14 3.23Z"/> </svg> <input type="range" id="volume-slider" min="0" max="100" value="70"> </div> <div class="info-card"> <div class="info-icon"> <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <path d="M13 7h-2v2h2V7zm0 4h-2v6h2v-6zm-1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/> </svg> </div> <div class="info-text">Tap the play button to see the particles react to the beat!</div> </div> <script> // Canvas setup const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); let animationId; let particles = []; let isPlaying = false; // Audio elements const audio = document.getElementById('audio-source'); const playBtn = document.getElementById('play-btn'); const volumeSlider = document.getElementById('volume-slider'); const progress = document.getElementById('progress'); // Audio context for analysis let audioContext; let analyser; let dataArray; let source; // EQ Visualizer const eqContainer = document.getElementById('eq-visualizer'); const numBars = 20; const eqBars = []; // Create EQ bars for (let i = 0; i < numBars; i++) { const bar = document.createElement('div'); bar.className = 'eq-bar'; bar.style.height = '5px'; eqContainer.appendChild(bar); eqBars.push(bar); } // Initialize canvas dimensions function resizeCanvas() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; } resizeCanvas(); window.addEventListener('resize', resizeCanvas); // Particle class class Particle { constructor() { this.reset(); } reset() { this.x = Math.random() * canvas.width; this.y = Math.random() * canvas.height; this.size = Math.random() * 5 + 1; this.baseSize = this.size; this.color = this.getRandomColor(); this.speedX = Math.random() * 3 - 1.5; this.speedY = Math.random() * 3 - 1.5; this.intensity = 0; } getRandomColor() { const colors = [ '#ff2a6d', // Pink '#05d9e8', // Blue '#a239ea', // Purple '#39ff14' // Green ]; return colors[Math.floor(Math.random() * colors.length)]; } update(beat) { // Adjust particle size based on beat intensity const targetSize = this.baseSize + beat * 3; this.size = this.size * 0.95 + targetSize * 0.05; // Update position this.x += this.speedX; this.y += this.speedY; // Bounce off edges if (this.x < 0 || this.x > canvas.width) { this.speedX *= -1; } if (this.y < 0 || this.y > canvas.height) { this.speedY *= -1; } // Reset particles that go off-screen if (this.x < -50 || this.x > canvas.width + 50 || this.y < -50 || this.y > canvas.height + 50) { this.reset(); } } draw() { ctx.beginPath(); ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); ctx.fillStyle = this.color; ctx.fill(); // Add glow effect ctx.shadowBlur = 15; ctx.shadowColor = this.color; } } // Create particles function initParticles() { particles = []; for (let i = 0; i < 100; i++) { particles.push(new Particle()); } } // Initialize audio analyzer function initAudio() { audioContext = new (window.AudioContext || window.webkitAudioContext)(); analyser = audioContext.createAnalyser(); analyser.fftSize = 256; source = audioContext.createMediaElementSource(audio); source.connect(analyser); analyser.connect(audioContext.destination); dataArray = new Uint8Array(analyser.frequencyBinCount); } // Animate function function animate() { ctx.clearRect(0, 0, canvas.width, canvas.height); // Create gradient background let gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height); gradient.addColorStop(0, '#070610'); gradient.addColorStop(1, '#0f0e17'); ctx.fillStyle = gradient; ctx.fillRect(0, 0, canvas.width, canvas.height); let beatIntensity = 0; if (isPlaying) { // Get frequency data analyser.getByteFrequencyData(dataArray); // Calculate beat intensity from bass frequencies let bassSum = 0; for (let i = 0; i < 10; i++) { bassSum += dataArray[i]; } beatIntensity = bassSum / 2550; // Normalize 0-1 // Update EQ visualizer for (let i = 0; i < numBars; i++) { const index = Math.floor(i * analyser.frequencyBinCount / numBars); const value = dataArray[index] / 255; const height = value * 40; eqBars[i].style.height = `${height}px`; // Assign different colors based on frequency if (i < numBars * 0.3) { eqBars[i].style.background = '#ff2a6d'; // Low frequencies (bass) } else if (i < numBars * 0.7) { eqBars[i].style.background = '#05d9e8'; // Mid frequencies } else { eqBars[i].style.background = '#a239ea'; // High frequencies } } } else { // When paused, make bars pulse gently const time = Date.now() / 1000; for (let i = 0; i < numBars; i++) { const height = 5 + Math.sin(time + i * 0.2) * 2; eqBars[i].style.height = `${height}px`; eqBars[i].style.background = '#a239ea'; } } // Update particles for (let i = 0; i < particles.length; i++) { particles[i].update(beatIntensity); particles[i].draw(); } animationId = requestAnimationFrame(animate); } // Handle play/pause playBtn.addEventListener('click', () => { if (audioContext === undefined) { initAudio(); } if (isPlaying) { audio.pause(); playBtn.innerHTML = '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8 5V19L19 12L8 5Z"/></svg>'; } else { audio.play(); playBtn.innerHTML = '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>'; } isPlaying = !isPlaying; }); // Handle volume change volumeSlider.addEventListener('input', () => { audio.volume = volumeSlider.value / 100; }); // Update progress bar audio.addEventListener('timeupdate', () => { const progressPercent = (audio.currentTime / audio.duration) * 100; progress.style.width = `${progressPercent}%`; }); // Initialize initParticles(); animate(); // Fake progress animation when not playing let fakeProgress = 0; const fakeProgressAnimation = () => { if (!isPlaying) { fakeProgress = (fakeProgress + 0.2) % 100; progress.style.width = `${35 + Math.sin(fakeProgress * 0.1) * 5}%`; } setTimeout(fakeProgressAnimation, 100); }; fakeProgressAnimation(); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Molecular Discovery Hub</title> <style> :root { --primary-blue: #0a3d62; --secondary-blue: #3498db; --highlight-blue: #00a8ff; --primary-green: #006266; --secondary-green: #1abc9c; --highlight-green: #00d2d3; --background: #f0f7fa; --text-dark: #2c3e50; --text-light: #ecf0f1; --grid-color: rgba(10, 61, 98, 0.1); } * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Roboto', 'Segoe UI', sans-serif; } body { background-color: var(--background); overflow: hidden; width: 100%; height: 100vh; display: flex; flex-direction: column; color: var(--text-dark); } .container { width: 100%; height: 100%; max-width: 700px; max-height: 700px; margin: 0 auto; position: relative; display: flex; flex-direction: column; } /* Grid Background */ .grid-background { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-image: linear-gradient(var(--grid-color) 1px, transparent 1px), linear-gradient(90deg, var(--grid-color) 1px, transparent 1px); background-size: 20px 20px; z-index: -1; opacity: 0.6; } /* Header */ header { padding: 20px; text-align: center; position: relative; z-index: 2; } h1 { color: var(--primary-blue); font-size: 28px; margin-bottom: 8px; font-weight: 600; letter-spacing: -0.5px; } .subtitle { color: var(--primary-green); font-size: 14px; margin-bottom: 15px; font-weight: 400; } /* Canvas container */ .simulation-container { flex: 1; position: relative; overflow: hidden; border-radius: 8px; box-shadow: 0 8px 20px rgba(10, 61, 98, 0.1); background: rgba(255, 255, 255, 0.8); margin: 0 20px; } #particle-canvas { width: 100%; height: 100%; display: block; } /* Simulation Controls */ .simulation-controls { position: absolute; bottom: 20px; left: 0; right: 0; display: flex; justify-content: center; gap: 12px; z-index: 3; } .control-btn { background: var(--primary-blue); color: var(--text-light); border: none; border-radius: 20px; padding: 8px 16px; font-size: 13px; cursor: pointer; transition: all 0.3s ease; display: flex; align-items: center; gap: 6px; } .control-btn:hover { background: var(--highlight-blue); transform: translateY(-2px); box-shadow: 0 4px 8px rgba(10, 61, 98, 0.2); } .control-btn:active { transform: translateY(0); } .control-btn svg { width: 14px; height: 14px; fill: currentColor; } /* Research Insights Panel */ .insights-panel { position: absolute; top: 60px; right: 30px; width: 180px; background: rgba(255, 255, 255, 0.95); border-radius: 8px; padding: 15px; box-shadow: 0 4px 15px rgba(10, 61, 98, 0.15); z-index: 4; transition: all 0.3s ease; transform: translateX(200px); opacity: 0; } .insights-panel.active { transform: translateX(0); opacity: 1; } .insights-panel h3 { color: var(--primary-blue); font-size: 14px; margin-bottom: 10px; border-bottom: 1px solid var(--grid-color); padding-bottom: 5px; } .insight-item { margin-bottom: 10px; font-size: 12px; } .insight-item .value { font-weight: 600; color: var(--secondary-blue); } /* Focus Area */ .focus-area { position: absolute; border: 2px dashed var(--highlight-green); border-radius: 50%; pointer-events: none; opacity: 0; transition: opacity 0.3s ease; z-index: 2; } /* Legend */ .legend { position: absolute; bottom: 80px; left: 20px; background: rgba(255, 255, 255, 0.9); border-radius: 6px; padding: 10px; font-size: 12px; box-shadow: 0 2px 10px rgba(10, 61, 98, 0.1); z-index: 3; } .legend-item { display: flex; align-items: center; margin-bottom: 5px; } .legend-color { width: 12px; height: 12px; border-radius: 50%; margin-right: 8px; } /* Tooltip */ .tooltip { position: absolute; background: rgba(44, 62, 80, 0.9); color: var(--text-light); padding: 8px 12px; border-radius: 4px; font-size: 12px; pointer-events: none; z-index: 10; opacity: 0; transition: opacity 0.2s ease; max-width: 180px; } /* Feature Tags */ .feature-tags { display: flex; justify-content: center; gap: 8px; margin-bottom: 15px; } .tag { background: var(--primary-green); color: white; font-size: 11px; padding: 3px 10px; border-radius: 12px; opacity: 0.8; } /* Responsive */ @media (max-width: 500px) { h1 { font-size: 22px; } .subtitle { font-size: 12px; } .legend { bottom: 70px; font-size: 10px; } .control-btn { padding: 6px 12px; font-size: 11px; } .insights-panel { width: 150px; right: 15px; padding: 10px; } } </style> </head> <body> <div class="container"> <div class="grid-background"></div> <header> <h1>Molecular Dynamics Explorer</h1> <p class="subtitle">Interactive visualization of atomic behaviors and quantum-scale interactions</p> <div class="feature-tags"> <div class="tag">Quantum Mechanics</div> <div class="tag">Brownian Motion</div> <div class="tag">Thermodynamics</div> </div> </header> <div class="simulation-container"> <canvas id="particle-canvas"></canvas> <div class="focus-area"></div> <div class="legend"> <div class="legend-item"> <div class="legend-color" style="background: var(--secondary-blue);"></div> <span>Hydrogen</span> </div> <div class="legend-item"> <div class="legend-color" style="background: var(--secondary-green);"></div> <span>Oxygen</span> </div> <div class="legend-item"> <div class="legend-color" style="background: var(--highlight-blue);"></div> <span>Nitrogen</span> </div> </div> <div class="simulation-controls"> <button class="control-btn" id="focus-btn"> <svg viewBox="0 0 24 24"> <path d="M12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm-7 7H3v4c0 1.1.9 2 2 2h4v-2H5v-4zM5 5h4V3H5c-1.1 0-2 .9-2 2v4h2V5zm14-2h-4v2h4v4h2V5c0-1.1-.9-2-2-2zm0 16h-4v2h4c1.1 0 2-.9 2-2v-4h-2v4z"></path> </svg> Focus Mode </button> <button class="control-btn" id="data-btn"> <svg viewBox="0 0 24 24"> <path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 14H7v-2h5v2zm0-4H7v-2h5v2zm0-4H7V7h5v2zm4 8h-2v-2h2v2zm0-4h-2v-2h2v2zm0-4h-2V7h2v2z"></path> </svg> Research Data </button> <button class="control-btn" id="restart-btn"> <svg viewBox="0 0 24 24"> <path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"></path> </svg> Reset </button> </div> </div> <div class="insights-panel"> <h3>Research Insights</h3> <div class="insight-item"> Avg. Velocity: <span class="value">3.42 nm/s</span> </div> <div class="insight-item"> Temperature: <span class="value">298.15 K</span> </div> <div class="insight-item"> Particle Count: <span class="value" id="particle-count">124</span> </div> <div class="insight-item"> Kinetic Energy: <span class="value">852.7 kJ/mol</span> </div> <div class="insight-item"> Entropy: <span class="value" id="entropy-value">184.3 J/K</span> </div> </div> <div class="tooltip"></div> </div> <script> document.addEventListener('DOMContentLoaded', function() { // Canvas setup const canvas = document.getElementById('particle-canvas'); const ctx = canvas.getContext('2d'); const container = document.querySelector('.simulation-container'); // Set canvas dimensions function resizeCanvas() { canvas.width = container.clientWidth; canvas.height = container.clientHeight; } window.addEventListener('resize', resizeCanvas); resizeCanvas(); // Particle system configuration const config = { particleCount: 120, colors: { hydrogen: 'rgba(52, 152, 219, 0.8)', oxygen: 'rgba(26, 188, 156, 0.8)', nitrogen: 'rgba(0, 168, 255, 0.8)' }, sizes: { hydrogen: 4, oxygen: 6, nitrogen: 5 }, focusMode: false, focusArea: { x: 0, y: 0, radius: 100 }, connectionThreshold: 60, particleSpeed: 1 }; // Particle class class Particle { constructor(type) { this.type = type; this.x = Math.random() * canvas.width; this.y = Math.random() * canvas.height; this.size = config.sizes[type]; this.color = config.colors[type]; this.velocityX = (Math.random() - 0.5) * config.particleSpeed; this.velocityY = (Math.random() - 0.5) * config.particleSpeed; this.originalVelocityX = this.velocityX; this.originalVelocityY = this.velocityY; // For animation when clicking this.targetX = this.x; this.targetY = this.y; this.isAnimating = false; // Charge property for particle interactions this.charge = Math.random() > 0.5 ? 1 : -1; } update() { // Focus mode velocity changes if (config.focusMode) { const dx = this.x - config.focusArea.x; const dy = this.y - config.focusArea.y; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < config.focusArea.radius) { // Particles in focus area move slower and more deliberately this.velocityX = this.originalVelocityX * 0.5; this.velocityY = this.originalVelocityY * 0.5; } else { // Particles outside focus area move faster and more chaotically this.velocityX = this.originalVelocityX * 1.2; this.velocityY = this.originalVelocityY * 1.2; // Add slight attraction to focus area this.velocityX += (dx > 0 ? -0.01 : 0.01); this.velocityY += (dy > 0 ? -0.01 : 0.01); } } else { // Reset to original velocity in normal mode this.velocityX = this.originalVelocityX; this.velocityY = this.originalVelocityY; } // Animation when clicking if (this.isAnimating) { const dx = this.targetX - this.x; const dy = this.targetY - this.y; this.x += dx * 0.08; this.y += dy * 0.08; if (Math.abs(dx) < 0.1 && Math.abs(dy) < 0.1) { this.isAnimating = false; } } else { // Normal movement this.x += this.velocityX; this.y += this.velocityY; // Bounce off walls with slight randomization if (this.x <= this.size || this.x >= canvas.width - this.size) { this.velocityX = -this.velocityX * (0.9 + Math.random() * 0.2); this.originalVelocityX = this.velocityX; } if (this.y <= this.size || this.y >= canvas.height - this.size) { this.velocityY = -this.velocityY * (0.9 + Math.random() * 0.2); this.originalVelocityY = this.velocityY; } } } draw() { ctx.beginPath(); ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); ctx.fillStyle = this.color; ctx.fill(); // Inner glow effect const gradient = ctx.createRadialGradient( this.x, this.y, this.size * 0.3, this.x, this.y, this.size ); gradient.addColorStop(0, 'rgba(255, 255, 255, 0.8)'); gradient.addColorStop(1, this.color); ctx.beginPath(); ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); ctx.fillStyle = gradient; ctx.fill(); // Show valence indicator for particles in focus area if (config.focusMode) { const dx = this.x - config.focusArea.x; const dy = this.y - config.focusArea.y; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < config.focusArea.radius) { ctx.beginPath(); ctx.arc(this.x, this.y, this.size + 3, 0, Math.PI * 2); ctx.strokeStyle = this.charge > 0 ? 'rgba(192, 57, 43, 0.4)' : 'rgba(41, 128, 185, 0.4)'; ctx.lineWidth = 1; ctx.stroke(); } } } } // Initialize particles let particles = []; function createParticles() { particles = []; for (let i = 0; i < config.particleCount; i++) { let type; // Distribute particle types based on natural abundance const rand = Math.random(); if (rand < 0.6) { type = 'hydrogen'; } else if (rand < 0.85) { type = 'oxygen'; } else { type = 'nitrogen'; } particles.push(new Particle(type)); } // Update the particle count in the insights panel document.getElementById('particle-count').textContent = particles.length; } // Draw connections between close particles function drawConnections() { 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 distance = Math.sqrt(dx * dx + dy * dy); if (distance < config.connectionThreshold) { // Calculate opacity based on distance const opacity = 1 - (distance / config.connectionThreshold); // Different connection colors based on particle types let connectionColor; if (particles[i].type === 'hydrogen' && particles[j].type === 'oxygen' || particles[i].type === 'oxygen' && particles[j].type === 'hydrogen') { // H-O bond connectionColor = `rgba(41, 128, 185, ${opacity * 0.5})`; } else if (particles[i].type === 'hydrogen' && particles[j].type === 'hydrogen') { // H-H bond connectionColor = `rgba(52, 152, 219, ${opacity * 0.3})`; } else if (particles[i].type === 'nitrogen' && particles[j].type === 'hydrogen' || particles[i].type === 'hydrogen' && particles[j].type === 'nitrogen') { // N-H bond connectionColor = `rgba(0, 168, 255, ${opacity * 0.5})`; } else { // Other bonds connectionColor = `rgba(127, 140, 141, ${opacity * 0.2})`; } ctx.beginPath(); ctx.moveTo(particles[i].x, particles[i].y); ctx.lineTo(particles[j].x, particles[j].y); ctx.strokeStyle = connectionColor; ctx.lineWidth = opacity * 1.5; ctx.stroke(); } } } } // Animation loop function animate() { ctx.clearRect(0, 0, canvas.width, canvas.height); // Draw grid pattern drawGrid(); // Draw connections drawConnections(); // Update and draw particles particles.forEach(particle => { particle.update(); particle.draw(); }); // Draw focus area if (config.focusMode) { ctx.beginPath(); ctx.arc(config.focusArea.x, config.focusArea.y, config.focusArea.radius, 0, Math.PI * 2); ctx.strokeStyle = 'rgba(26, 188, 156, 0.4)'; ctx.lineWidth = 2; ctx.stroke(); // Draw measurement lines ctx.setLineDash([4, 4]); ctx.beginPath(); ctx.moveTo(config.focusArea.x - config.focusArea.radius, config.focusArea.y); ctx.lineTo(config.focusArea.x + config.focusArea.radius, config.focusArea.y); ctx.moveTo(config.focusArea.x, config.focusArea.y - config.focusArea.radius); ctx.lineTo(config.focusArea.x, config.focusArea.y + config.focusArea.radius); ctx.strokeStyle = 'rgba(26, 188, 156, 0.3)'; ctx.lineWidth = 1; ctx.stroke(); ctx.setLineDash([]); } // Update entropy value for scientific effect if (Math.random() < 0.05) { const entropyBase = 184.3; const entropyVariation = (Math.random() - 0.5) * 2; document.getElementById('entropy-value').textContent = (entropyBase + entropyVariation).toFixed(1) + ' J/K'; } requestAnimationFrame(animate); } function drawGrid() { const gridSize = 20; const width = canvas.width; const height = canvas.height; ctx.strokeStyle = 'rgba(10, 61, 98, 0.07)'; ctx.lineWidth = 0.5; // Vertical lines for (let x = 0; x <= width; x += gridSize) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, height); ctx.stroke(); } // Horizontal lines for (let y = 0; y <= height; y += gridSize) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(width, y); ctx.stroke(); } // Add subtle wave effect to grid ctx.strokeStyle = 'rgba(26, 188, 156, 0.05)'; const time = Date.now() * 0.001; for (let x = 0; x <= width; x += gridSize * 4) { ctx.beginPath(); for (let y = 0; y <= height; y += 2) { const offset = Math.sin(y * 0.02 + time) * 5; if (y === 0) { ctx.moveTo(x + offset, y); } else { ctx.lineTo(x + offset, y); } } ctx.stroke(); } } // Event listeners const focusBtn = document.getElementById('focus-btn'); const dataBtn = document.getElementById('data-btn'); const restartBtn = document.getElementById('restart-btn'); const insightsPanel = document.querySelector('.insights-panel'); const focusArea = document.querySelector('.focus-area'); const tooltip = document.querySelector('.tooltip'); focusBtn.addEventListener('click', function() { config.focusMode = !config.focusMode; this.style.background = config.focusMode ? 'var(--highlight-green)' : 'var(--primary-blue)'; if (config.focusMode) { config.focusArea.x = canvas.width / 2; config.focusArea.y = canvas.height / 2; focusArea.style.width = `${config.focusArea.radius * 2}px`; focusArea.style.height = `${config.focusArea.radius * 2}px`; focusArea.style.left = `${config.focusArea.x - config.focusArea.radius}px`; focusArea.style.top = `${config.focusArea.y - config.focusArea.radius}px`; focusArea.style.opacity = '1'; } else { focusArea.style.opacity = '0'; } }); dataBtn.addEventListener('click', function() { insightsPanel.classList.toggle('active'); }); restartBtn.addEventListener('click', function() { createParticles(); }); // Canvas interactions canvas.addEventListener('click', function(e) { const rect = canvas.getBoundingClientRect(); const clickX = e.clientX - rect.left; const clickY = e.clientY - rect.top; if (config.focusMode) { // Move focus area config.focusArea.x = clickX; config.focusArea.y = clickY; focusArea.style.left = `${clickX - config.focusArea.radius}px`; focusArea.style.top = `${clickY - config.focusArea.radius}px`; } else { // Create a ripple effect particles.forEach(particle => { const dx = clickX - particle.x; const dy = clickY - particle.y; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < 100) { // Repel particles const angle = Math.atan2(dy, dx); const force = (100 - distance) / 10; particle.targetX = particle.x - Math.cos(angle) * force * 2; particle.targetY = particle.y - Math.sin(angle) * force * 2; particle.isAnimating = true; } }); } }); // Mouse move for showing particle info canvas.addEventListener('mousemove', function(e) { const rect = canvas.getBoundingClientRect(); const mouseX = e.clientX - rect.left; const mouseY = e.clientY - rect.top; // Find closest particle let closestParticle = null; let closestDistance = Infinity; particles.forEach(particle => { const dx = mouseX - particle.x; const dy = mouseY - particle.y; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < 20 && distance < closestDistance) { closestParticle = particle; closestDistance = distance; } }); if (closestParticle) { // Show tooltip with particle info tooltip.style.opacity = '1'; tooltip.style.left = `${mouseX + 15}px`; tooltip.style.top = `${mouseY - 15}px`; const particleData = { hydrogen: { name: 'Hydrogen (H)', atomicNumber: 1, mass: '1.008 u', properties: 'Most abundant element in universe' }, oxygen: { name: 'Oxygen (O)', atomicNumber: 8, mass: '15.999 u', properties: 'Essential for cellular respiration' }, nitrogen: { name: 'Nitrogen (N)', atomicNumber: 7, mass: '14.007 u', properties: 'Forms 78% of Earth\'s atmosphere' } }; const data = particleData[closestParticle.type]; tooltip.innerHTML = ` <strong>${data.name}</strong><br> Atomic #: ${data.atomicNumber}<br> Mass: ${data.mass}<br> Charge: ${closestParticle.charge > 0 ? 'Positive' : 'Negative'} `; } else { tooltip.style.opacity = '0'; } }); canvas.addEventListener('mouseleave', function() { tooltip.style.opacity = '0'; }); // Initialize createParticles(); animate(); }); </script> </body> </html>