Fire animations can add a dynamic and captivating element to any digital project. Whether you're designing a game, a website, or an app, these animations can bring your visuals to life.
In this article, we'll explore 10 stunning fire animation examples that showcase the power of creative design and technical skill. Get ready to be inspired by these fiery masterpieces.
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 fire animations a breeze. Loved by designers and developers alike, it ensures pixel-perfect UI every time.
Ready to ignite your creativity? 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 and design stunning UIs, including fire animations, with unmatched efficiency. Our drag-and-drop editor and beautifully crafted components ensure pixel-perfect results every time.
Ready to bring your creative visions to life? 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>Fire Safety Dashboard</title> <style> :root { --primary-dark: #212529; --primary-red: #e63946; --secondary-red: #ff6b6b; --primary-orange: #ff9f1c; --secondary-orange: #ffbe0b; --text-light: #f8f9fa; --text-dark: #343a40; --accent-blue: #4cc9f0; --shadow: rgba(0, 0, 0, 0.2); } * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } body { background-color: var(--primary-dark); color: var(--text-light); height: 700px; width: 700px; overflow: hidden; display: flex; flex-direction: column; align-items: center; justify-content: flex-start; padding: 20px; } header { width: 100%; text-align: center; margin-bottom: 20px; position: relative; z-index: 10; } h1 { font-size: 2.2rem; margin-bottom: 5px; color: var(--text-light); text-shadow: 0 0 10px rgba(230, 57, 70, 0.7); } .dashboard-container { width: 100%; height: calc(100% - 80px); display: grid; grid-template-columns: 3fr 2fr; grid-template-rows: 1fr 1fr; gap: 20px; position: relative; z-index: 10; } .card { background: rgba(33, 37, 41, 0.8); border-radius: 10px; padding: 15px; box-shadow: 0 4px 15px var(--shadow); border: 1px solid rgba(255, 255, 255, 0.1); transition: transform 0.3s ease, box-shadow 0.3s ease; position: relative; overflow: hidden; } .card:hover { transform: translateY(-5px); box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3); } .card-title { font-size: 1.2rem; margin-bottom: 15px; font-weight: 600; color: var(--secondary-orange); display: flex; align-items: center; } .card-title i { margin-right: 8px; } .risk-level { grid-column: 1; grid-row: 1; display: flex; flex-direction: column; } .safety-tips { grid-column: 2; grid-row: 1 / span 2; display: flex; flex-direction: column; } .emergency-contacts { grid-column: 1; grid-row: 2; } .risk-meter { width: 100%; height: 30px; background: linear-gradient(to right, #4cc9f0, #4895ef, #4361ee, #3f37c9, #3a0ca3); border-radius: 15px; margin-top: 10px; position: relative; overflow: hidden; } .risk-indicator { position: absolute; width: 15px; height: 35px; background-color: white; top: -2px; border-radius: 5px; transform: translateX(-50%); transition: left 0.5s ease; box-shadow: 0 0 10px rgba(255, 255, 255, 0.7); z-index: 2; } .risk-labels { display: flex; justify-content: space-between; margin-top: 8px; font-size: 0.8rem; color: var(--text-light); } .tip-list { list-style-type: none; flex-grow: 1; overflow-y: auto; padding-right: 5px; } .tip-list::-webkit-scrollbar { width: 6px; } .tip-list::-webkit-scrollbar-track { background: rgba(255, 255, 255, 0.1); border-radius: 3px; } .tip-list::-webkit-scrollbar-thumb { background: var(--secondary-orange); border-radius: 3px; } .tip-item { margin-bottom: 15px; padding-bottom: 15px; border-bottom: 1px solid rgba(255, 255, 255, 0.1); display: flex; align-items: flex-start; } .tip-item:last-child { border-bottom: none; } .tip-number { background-color: var(--primary-red); color: white; width: 25px; height: 25px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; margin-right: 12px; flex-shrink: 0; } .contact-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin-top: 10px; } .contact-item { background: rgba(255, 255, 255, 0.05); padding: 10px; border-radius: 8px; transition: all 0.3s ease; } .contact-item:hover { background: rgba(255, 255, 255, 0.1); transform: scale(1.05); } .contact-title { font-weight: 600; color: var(--secondary-red); margin-bottom: 5px; font-size: 0.9rem; } .contact-value { font-size: 1rem; color: var(--text-light); } /* Fire Animation */ .fire-container { position: fixed; bottom: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 1; opacity: 0.2; transition: opacity 0.8s ease; } .fire-container:hover { opacity: 0.35; cursor: pointer; } .fire { position: absolute; bottom: -50px; left: 0; width: 100%; height: 200px; background-image: radial-gradient(ellipse at center, rgba(255, 159, 28, 0) 0%, rgba(255, 159, 28, 0) 35%, rgba(255, 159, 28, 0.2) 80%, rgba(255, 159, 28, 0.3) 100%); } .flame { position: absolute; bottom: 0; width: 15px; height: 15px; background-color: var(--primary-orange); border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%; transform-origin: center bottom; opacity: 0; filter: blur(5px); animation: flicker 3s infinite; } @keyframes flicker { 0%, 100% { opacity: 0.5; transform: scale(1) rotate(-2deg); } 25% { opacity: 0.7; transform: scale(1.1) rotate(2deg); } 50% { opacity: 0.8; transform: scale(1.05) rotate(-1deg); } 75% { opacity: 0.6; transform: scale(1.2) rotate(3deg); } } .data-stat { display: flex; align-items: center; margin-top: 15px; padding: 8px 12px; background: rgba(255, 255, 255, 0.05); border-radius: 8px; transition: all 0.3s ease; } .data-stat:hover { background: rgba(255, 255, 255, 0.1); } .stat-icon { width: 30px; height: 30px; background: var(--primary-red); display: flex; align-items: center; justify-content: center; border-radius: 50%; margin-right: 12px; } .stat-text { flex-grow: 1; } .stat-label { font-size: 0.85rem; color: var(--text-light); opacity: 0.8; } .stat-value { font-size: 1.2rem; font-weight: 600; color: var(--text-light); } .check-button { display: inline-block; margin-top: 15px; padding: 10px 15px; background-color: var(--primary-red); color: white; border: none; border-radius: 5px; font-weight: 600; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); } .check-button:hover { background-color: #d13039; transform: translateY(-2px); box-shadow: 0 6px 8px rgba(0, 0, 0, 0.15); } @media (max-width: 700px) { body { padding: 15px; } h1 { font-size: 1.8rem; } .dashboard-container { grid-template-columns: 1fr; grid-template-rows: auto auto auto; } .risk-level, .emergency-contacts, .safety-tips { grid-column: 1; } .risk-level { grid-row: 1; } .emergency-contacts { grid-row: 3; } .safety-tips { grid-row: 2; max-height: 250px; } .contact-grid { grid-template-columns: 1fr; } } @media (max-width: 500px) { h1 { font-size: 1.5rem; } .card-title { font-size: 1rem; } .dashboard-container { gap: 15px; } .card { padding: 10px; } } </style> </head> <body> <div class="fire-container" id="fireContainer"> <div class="fire" id="fire"></div> </div> <header> <h1>FIRE SAFETY DASHBOARD</h1> </header> <div class="dashboard-container"> <div class="card risk-level"> <div class="card-title"> <i>🔥</i> Current Hazard Level </div> <div class="risk-meter"> <div class="risk-indicator" id="riskIndicator"></div> </div> <div class="risk-labels"> <span>Low</span> <span>Moderate</span> <span>High</span> <span>Severe</span> <span>Critical</span> </div> <div class="data-stat"> <div class="stat-icon">📈</div> <div class="stat-text"> <div class="stat-label">Last inspection</div> <div class="stat-value">42 days ago</div> </div> </div> <div class="data-stat"> <div class="stat-icon">🚨</div> <div class="stat-text"> <div class="stat-label">Alarm status</div> <div class="stat-value">Operational</div> </div> </div> <button class="check-button" id="checkButton">Run Safety Check</button> </div> <div class="card safety-tips"> <div class="card-title"> <i>📋</i> Critical Safety Measures </div> <ul class="tip-list"> <li class="tip-item"> <div class="tip-number">1</div> <div>Install smoke detectors on every level of your building and test them monthly. Replace batteries at least once per year or when chirping occurs.</div> </li> <li class="tip-item"> <div class="tip-number">2</div> <div>Keep fire extinguishers readily accessible in high-risk areas like kitchens, utility rooms, and near exit routes. Ensure all staff know how to use them.</div> </li> <li class="tip-item"> <div class="tip-number">3</div> <div>Maintain clear evacuation routes and emergency exits at all times. Conduct quarterly drills and ensure exit signs remain illuminated.</div> </li> <li class="tip-item"> <div class="tip-number">4</div> <div>Inspect electrical systems annually and address frayed wires, overloaded circuits, or damaged outlets immediately to prevent electrical fires.</div> </li> <li class="tip-item"> <div class="tip-number">5</div> <div>Store flammable materials properly in designated fire-resistant cabinets, away from heat sources and electrical equipment.</div> </li> <li class="tip-item"> <div class="tip-number">6</div> <div>Establish a designated smoking area at least 25 feet from all entrances and ensure proper disposal containers for cigarette butts.</div> </li> </ul> </div> <div class="card emergency-contacts"> <div class="card-title"> <i>☎️</i> Emergency Contacts </div> <div class="contact-grid"> <div class="contact-item"> <div class="contact-title">Fire Department</div> <div class="contact-value">911</div> </div> <div class="contact-item"> <div class="contact-title">Building Safety Officer</div> <div class="contact-value">555-7890</div> </div> <div class="contact-item"> <div class="contact-title">Fire Marshal Office</div> <div class="contact-value">555-4321</div> </div> <div class="contact-item"> <div class="contact-title">Maintenance</div> <div class="contact-value">555-9876</div> </div> </div> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { // Fire animation const fireContainer = document.getElementById('fireContainer'); const fire = document.getElementById('fire'); const riskIndicator = document.getElementById('riskIndicator'); const checkButton = document.getElementById('checkButton'); // Set initial risk level (between 0 and 100) let riskLevel = 65; updateRiskIndicator(); // Generate flames for (let i = 0; i < 50; i++) { createFlame(); } // Check button functionality checkButton.addEventListener('click', function() { // Simulate safety check with randomized outcome const newRisk = Math.floor(Math.random() * 100); animateRiskChange(newRisk); // Change button text temporarily checkButton.textContent = "Running Check..."; checkButton.disabled = true; setTimeout(() => { checkButton.textContent = "Run Safety Check"; checkButton.disabled = false; }, 2000); }); // Intensify fire when hovering over fire container fireContainer.addEventListener('mouseenter', function() { document.querySelectorAll('.flame').forEach(flame => { flame.style.animationDuration = '1.5s'; flame.style.height = parseInt(flame.style.height) * 1.5 + 'px'; }); }); fireContainer.addEventListener('mouseleave', function() { document.querySelectorAll('.flame').forEach(flame => { flame.style.animationDuration = '3s'; flame.style.height = parseInt(flame.style.height) / 1.5 + 'px'; }); }); // Functions function createFlame() { const flame = document.createElement('div'); flame.className = 'flame'; const size = Math.random() * 70 + 30; const position = Math.random() * 100; const delay = Math.random() * 3; const duration = Math.random() * 2 + 2; flame.style.left = position + '%'; flame.style.width = size / 3 + 'px'; flame.style.height = size + 'px'; flame.style.animationDelay = delay + 's'; flame.style.animationDuration = duration + 's'; if (Math.random() > 0.5) { flame.style.backgroundColor = 'var(--primary-red)'; } else { flame.style.backgroundColor = 'var(--primary-orange)'; } fire.appendChild(flame); } function updateRiskIndicator() { const position = riskLevel + '%'; riskIndicator.style.left = position; // Change color based on risk level if (riskLevel < 20) { riskIndicator.style.backgroundColor = '#4cc9f0'; // Blue - Low } else if (riskLevel < 40) { riskIndicator.style.backgroundColor = '#4895ef'; // Light blue - Moderate } else if (riskLevel < 60) { riskIndicator.style.backgroundColor = '#ffbe0b'; // Yellow - High } else if (riskLevel < 80) { riskIndicator.style.backgroundColor = '#ff9f1c'; // Orange - Severe } else { riskIndicator.style.backgroundColor = '#e63946'; // Red - Critical } // Also update fire intensity updateFireIntensity(); } function updateFireIntensity() { const opacity = 0.2 + (riskLevel / 200); // Base opacity 0.2, max 0.7 fireContainer.style.opacity = opacity; // Scale flame height based on risk level document.querySelectorAll('.flame').forEach(flame => { const baseHeight = parseInt(flame.style.height.replace('px', '')); const scaleFactor = 1 + (riskLevel / 100); flame.style.height = baseHeight * scaleFactor + 'px'; }); } function animateRiskChange(newRisk) { const startRisk = riskLevel; const change = newRisk - startRisk; const duration = 1500; // Animation duration in ms const startTime = performance.now(); function updateAnimation(currentTime) { const elapsedTime = currentTime - startTime; const progress = Math.min(elapsedTime / duration, 1); // Easing function for smoother animation const easedProgress = 1 - Math.pow(1 - progress, 3); riskLevel = startRisk + change * easedProgress; updateRiskIndicator(); if (progress < 1) { requestAnimationFrame(updateAnimation); } } requestAnimationFrame(updateAnimation); } }); </script> </body> </html>
<html> <head> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Cinzel', serif; } body { width: 100%; height: 100%; overflow: hidden; display: flex; justify-content: center; align-items: center; background-color: #0a0a0a; } @import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@400;600;800&display=swap'); .container { position: relative; width: 100%; height: 100vh; max-width: 700px; max-height: 700px; display: flex; flex-direction: column; justify-content: center; align-items: center; background: radial-gradient(circle at center, #200505 0%, #0a0a0a 70%); overflow: hidden; } .fire-container { position: absolute; bottom: 0; height: 300px; width: 100%; display: flex; justify-content: center; z-index: 1; transition: all 0.6s ease-in-out; } .flame { position: absolute; bottom: -20px; width: 180px; height: 180px; background: linear-gradient(to top, #ff4500, #ff7800); border-radius: 45% 45% 40% 40% / 40% 40% 60% 60%; transform-origin: center bottom; box-shadow: 0 0 80px 15px rgba(255, 69, 0, 0.5); animation: flicker 3s ease-in-out infinite; filter: blur(1px); opacity: 0.9; } .flame::before, .flame::after { content: ''; position: absolute; width: 100%; height: 100%; border-radius: inherit; background: inherit; } .flame::before { transform: scale(0.9) translate(10%, 10%); filter: blur(12px); animation: pulse 4s infinite alternate; } .flame::after { transform: scale(0.8) translate(-10%, 10%); filter: blur(15px); animation: pulse 6s infinite alternate-reverse; } .flame-inner { position: absolute; bottom: 10%; left: 25%; width: 50%; height: 60%; background: #ffdd1f; border-radius: 50% 50% 35% 35% / 50% 50% 65% 65%; animation: pulse 2s ease-in-out infinite alternate; filter: blur(2px); opacity: 0.8; } .embers { position: absolute; bottom: 0; width: 100%; height: 100px; pointer-events: none; } .ember { position: absolute; bottom: 0; width: 6px; height: 6px; background: #ff7800; border-radius: 50%; animation: ember-rise 3s linear infinite; opacity: 0; } .game-title { position: relative; z-index: 3; text-align: center; color: #fff; text-shadow: 0 0 15px #ff4500, 0 0 20px #ff4500; margin-bottom: 30px; transform: translateY(-80px); transition: all 0.3s ease; } .game-title h1 { font-size: 3.5rem; font-weight: 800; letter-spacing: 3px; margin-bottom: 10px; } .game-title p { font-size: 1.1rem; letter-spacing: 2px; opacity: 0.8; } .menu { position: relative; z-index: 3; display: flex; flex-direction: column; gap: 18px; transform: translateY(-30px); } .menu-item { color: #fff; font-size: 1.3rem; letter-spacing: 2px; text-transform: uppercase; padding: 12px 40px; cursor: pointer; text-align: center; background-color: rgba(10, 10, 10, 0.6); border: 1px solid rgba(255, 70, 0, 0.2); border-radius: 5px; box-shadow: 0 0 10px rgba(255, 70, 0, 0.3); transition: all 0.3s ease; position: relative; overflow: hidden; } .menu-item:hover { background-color: rgba(20, 20, 20, 0.8); border-color: rgba(255, 70, 0, 0.6); box-shadow: 0 0 20px rgba(255, 70, 0, 0.6); transform: scale(1.05); } .menu-item.active { background-color: rgba(30, 30, 30, 0.9); border-color: rgba(255, 70, 0, 0.8); box-shadow: 0 0 30px rgba(255, 70, 0, 0.8); } .menu-item::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%; background: linear-gradient(90deg, transparent, rgba(255, 70, 0, 0.2), transparent); transition: 0.5s; } .menu-item:hover::before { left: 100%; } .rune-circle { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 500px; height: 500px; border-radius: 50%; border: 2px solid rgba(255, 70, 0, 0.2); z-index: 0; animation: rotate 60s linear infinite; pointer-events: none; } .rune { position: absolute; width: 40px; height: 40px; opacity: 0.4; filter: brightness(1.5) contrast(1.2); } @keyframes flicker { 0%, 100% { transform: scale(1) translateY(0); opacity: 0.9; } 25% { transform: scale(1.02) translateY(-2px); opacity: 1; } 50% { transform: scale(0.98) translateY(1px); opacity: 0.85; } 75% { transform: scale(1.01) translateY(-1px); opacity: 0.95; } } @keyframes pulse { 0% { transform: scale(0.8) translate(0, 0); } 100% { transform: scale(1.1) translate(0, -5px); } } @keyframes ember-rise { 0% { bottom: 0; opacity: 0; transform: translateX(0); } 10% { opacity: 1; } 60% { opacity: 0.6; } 100% { bottom: 100%; opacity: 0; transform: translateX(calc(var(--rand-x) * 30px)); } } @keyframes rotate { 0% { transform: translate(-50%, -50%) rotate(0deg); } 100% { transform: translate(-50%, -50%) rotate(360deg); } } .loading-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: #0a0a0a; z-index: 10; display: flex; justify-content: center; align-items: center; animation: fadeOut 2s forwards 1s; } .loading-text { color: #ff4500; font-size: 1.6rem; letter-spacing: 3px; position: relative; } .loading-text::after { content: ''; position: absolute; bottom: -10px; left: 0; width: 0%; height: 2px; background: #ff4500; animation: loadBar 1s forwards; } @keyframes loadBar { 0% { width: 0%; } 100% { width: 100%; } } @keyframes fadeOut { 0% { opacity: 1; } 100% { opacity: 0; visibility: hidden; } } @media (max-width: 700px) { .game-title h1 { font-size: 2.5rem; } .game-title p { font-size: 0.9rem; } .menu-item { font-size: 1.1rem; padding: 10px 30px; } .flame { width: 160px; height: 160px; } .rune-circle { width: 380px; height: 380px; } } </style> </head> <body> <div class="container"> <div class="loading-overlay"> <div class="loading-text">KINDLING FLAMES...</div> </div> <div class="rune-circle" id="runeCircle"></div> <div class="game-title"> <h1>EMBERBLIGHT</h1> <p>Chronicles of the Eternal Flame</p> </div> <div class="menu"> <div class="menu-item" data-intensity="1.2">BEGIN JOURNEY</div> <div class="menu-item" data-intensity="1.5">FORGE OF LEGENDS</div> <div class="menu-item" data-intensity="1.8">ARCANE CODEX</div> <div class="menu-item" data-intensity="2.0">QUIT TO ASHES</div> </div> <div class="fire-container"> <div class="flame"> <div class="flame-inner"></div> </div> <div class="embers" id="embers"></div> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { // Create runes const runeCircle = document.getElementById('runeCircle'); const runeSymbols = ['⚶', '⚸', '⚹', '⚺', '⚻', '⚼', '⚿', '⛥', '⛦', '⛧']; const runeCount = 8; for (let i = 0; i < runeCount; i++) { const angle = (i / runeCount) * 2 * Math.PI; const x = 230 * Math.cos(angle); const y = 230 * Math.sin(angle); const rune = document.createElement('div'); rune.classList.add('rune'); rune.style.left = `calc(50% + ${x}px)`; rune.style.top = `calc(50% + ${y}px)`; rune.textContent = runeSymbols[Math.floor(Math.random() * runeSymbols.length)]; runeCircle.appendChild(rune); } // Create embers const embersContainer = document.getElementById('embers'); const emberCount = 20; for (let i = 0; i < emberCount; i++) { createEmber(); } function createEmber() { const ember = document.createElement('div'); ember.classList.add('ember'); // Random properties const randX = Math.random() * 2 - 1; // -1 to 1 const size = 3 + Math.random() * 4; const duration = 2 + Math.random() * 3; const delay = Math.random() * 3; ember.style.left = `${20 + Math.random() * 60}%`; ember.style.width = `${size}px`; ember.style.height = `${size}px`; ember.style.animationDuration = `${duration}s`; ember.style.animationDelay = `${delay}s`; ember.style.setProperty('--rand-x', randX); embersContainer.appendChild(ember); // Remove and recreate ember after animation completes setTimeout(() => { ember.remove(); createEmber(); }, (duration + delay) * 1000); } // Interactive flame control const fireContainer = document.querySelector('.fire-container'); const flame = document.querySelector('.flame'); const menuItems = document.querySelectorAll('.menu-item'); let currentIntensity = 1; let lastSelectedItem = null; menuItems.forEach(item => { item.addEventListener('mouseover', function() { const intensity = parseFloat(this.getAttribute('data-intensity')); animateFireIntensity(intensity); }); item.addEventListener('mouseout', function() { if (lastSelectedItem) { const intensity = parseFloat(lastSelectedItem.getAttribute('data-intensity')); animateFireIntensity(intensity); } else { animateFireIntensity(1); } }); item.addEventListener('click', function() { // Remove active class from all items menuItems.forEach(mi => mi.classList.remove('active')); // Add active class to clicked item this.classList.add('active'); lastSelectedItem = this; // Set intensity based on selected item const intensity = parseFloat(this.getAttribute('data-intensity')); animateFireIntensity(intensity); // Apply pulsating beat to match the fire applyRhythmicBeat(); }); }); function animateFireIntensity(intensity) { const scale = 0.8 + (intensity * 0.2); currentIntensity = intensity; fireContainer.style.transition = 'all 0.6s cubic-bezier(0.215, 0.610, 0.355, 1.000)'; flame.style.transition = 'all 0.6s cubic-bezier(0.215, 0.610, 0.355, 1.000)'; flame.style.transform = `scale(${scale})`; flame.style.height = `${180 * scale}px`; flame.style.width = `${180 * scale}px`; // Adjust colors based on intensity const hue1 = 15 - (intensity * 5); // More intense = deeper red const hue2 = 30 - (intensity * 5); flame.style.background = `linear-gradient(to top, hsl(${hue1}, 100%, 50%), hsl(${hue2}, 100%, 55%))`; // Adjust shadow glow flame.style.boxShadow = `0 0 ${80 * intensity}px ${15 * intensity}px rgba(255, ${69 - intensity * 20}, 0, ${0.3 + intensity * 0.2})`; } function applyRhythmicBeat() { // Clear any existing animation flame.style.animation = 'none'; // Reset to apply new animation setTimeout(() => { flame.style.animation = `flicker ${4 - currentIntensity}s ease-in-out infinite`; }, 10); // Apply pulse effect to title const gameTitle = document.querySelector('.game-title'); gameTitle.style.animation = 'none'; setTimeout(() => { gameTitle.style.animation = `pulse ${5 - currentIntensity}s ease-in-out infinite alternate`; gameTitle.style.textShadow = `0 0 ${10 + currentIntensity * 5}px #ff4500, 0 0 ${15 + currentIntensity * 5}px #ff4500`; }, 10); } // Initialize with default animation setTimeout(() => { applyRhythmicBeat(); }, 2000); }); </script> </body> </html>
<html> <head> <title>Digital Art Portfolio - Artistic Fire Animation</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { background-color: #0a0a0a; font-family: 'Montserrat', sans-serif; width: 100%; height: 100vh; overflow: hidden; display: flex; justify-content: center; align-items: center; position: relative; } .portfolio-container { width: 700px; height: 700px; position: relative; display: flex; flex-direction: column; justify-content: center; align-items: center; color: white; text-align: center; z-index: 2; } .headline { font-size: 3.5rem; font-weight: 900; margin-bottom: 1rem; background: linear-gradient(to right, #f5a742, #e85a19, #ff3e00); -webkit-background-clip: text; -webkit-text-fill-color: transparent; opacity: 0; transform: translateY(20px); animation: fadeUp 1.5s ease forwards 0.5s; } .subheadline { font-size: 1.2rem; font-weight: 300; letter-spacing: 1px; max-width: 80%; margin-bottom: 2rem; opacity: 0; transform: translateY(20px); animation: fadeUp 1.5s ease forwards 0.8s; } .cta-button { padding: 12px 24px; background: rgba(255, 255, 255, 0.1); border: 1px solid rgba(255, 255, 255, 0.3); border-radius: 30px; color: white; font-size: 1rem; font-weight: 600; text-decoration: none; cursor: pointer; transition: all 0.3s ease; position: relative; overflow: hidden; opacity: 0; transform: translateY(20px); animation: fadeUp 1.5s ease forwards 1.1s; } .cta-button:hover { background: rgba(255, 255, 255, 0.2); transform: translateY(-3px); box-shadow: 0 7px 15px rgba(0, 0, 0, 0.3); } .cta-button::after { content: ''; position: absolute; top: 50%; left: 50%; width: 150%; height: 150%; background: radial-gradient(circle, rgba(255, 255, 255, 0.3) 0%, rgba(255, 255, 255, 0) 70%); transform: translate(-50%, -50%) scale(0); transition: transform 0.5s ease; } .cta-button:hover::after { transform: translate(-50%, -50%) scale(1); } @keyframes fadeUp { to { opacity: 1; transform: translateY(0); } } .canvas-container { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 1; } canvas { width: 100%; height: 100%; } .portfolio-nav { position: absolute; bottom: 30px; width: 60%; display: flex; justify-content: space-between; opacity: 0; animation: fadeUp 1.5s ease forwards 1.4s; } .nav-item { font-size: 0.9rem; font-weight: 500; text-transform: uppercase; letter-spacing: 1px; color: rgba(255, 255, 255, 0.7); cursor: pointer; transition: all 0.3s ease; position: relative; } .nav-item:hover { color: #f5a742; } .nav-item::after { content: ''; position: absolute; bottom: -5px; left: 0; width: 0; height: 2px; background: linear-gradient(to right, #f5a742, #e85a19); transition: width 0.3s ease; } .nav-item:hover::after { width: 100%; } /* Responsive adjustments */ @media (max-width: 700px) { .headline { font-size: 2.5rem; } .subheadline { font-size: 1rem; max-width: 90%; } .portfolio-nav { width: 80%; } .nav-item { font-size: 0.8rem; } } @media (max-width: 500px) { .headline { font-size: 2rem; } .subheadline { font-size: 0.9rem; } .portfolio-nav { width: 90%; flex-wrap: wrap; justify-content: center; gap: 15px; } } </style> </head> <body> <div class="portfolio-container"> <h1 class="headline">Brushstroke Flames</h1> <p class="subheadline">Where digital artistry meets the primal beauty of fire. Each flicker is a stroke of inspiration; each flame a story waiting to be told.</p> <a class="cta-button" id="explore-button">Explore My Canvas</a> <div class="portfolio-nav"> <div class="nav-item">Digital Paintings</div> <div class="nav-item">Installations</div> <div class="nav-item">Commissions</div> <div class="nav-item">About</div> </div> </div> <div class="canvas-container"> <canvas id="fire-canvas"></canvas> </div> <script> document.addEventListener('DOMContentLoaded', () => { const canvas = document.getElementById('fire-canvas'); const ctx = canvas.getContext('2d'); // Set canvas dimensions to match container function resizeCanvas() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; } resizeCanvas(); window.addEventListener('resize', resizeCanvas); // Fire animation class class FireAnimation { constructor() { this.particles = []; this.paletteBase = [ { r: 245, g: 167, b: 66 }, // Gold { r: 232, g: 90, b: 25 }, // Orange { r: 255, g: 62, b: 0 }, // Bright red-orange { r: 191, g: 34, b: 34 }, // Deep red { r: 80, g: 20, b: 70 } // Purple shadow ]; this.palette = [...this.paletteBase]; this.time = 0; this.lastUpdateTime = 0; // Initialize particles this.createParticles(); // Start the animation this.animate(); // Add interactivity canvas.addEventListener('mousemove', this.handleMouseMove.bind(this)); } createParticles() { const particleCount = Math.floor(canvas.width * canvas.height / 3000); for (let i = 0; i < particleCount; i++) { this.particles.push({ x: Math.random() * canvas.width, y: canvas.height + Math.random() * 100, size: 5 + Math.random() * 25, opacity: 0.1 + Math.random() * 0.5, speedX: (Math.random() - 0.5) * 1.5, speedY: -1.5 - Math.random() * 3, colorIndex: Math.floor(Math.random() * this.palette.length), rotation: Math.random() * Math.PI * 2, rotationSpeed: (Math.random() - 0.5) * 0.02, sway: 0.3 + Math.random() * 0.5, swaySpeed: 0.005 + Math.random() * 0.01, swayOffset: Math.random() * Math.PI * 2, lifespan: 100 + Math.random() * 200 }); } } animate(currentTime = 0) { const deltaTime = currentTime - this.lastUpdateTime; this.lastUpdateTime = currentTime; // Clear canvas ctx.clearRect(0, 0, canvas.width, canvas.height); // Update time this.time += 0.01; // Shift color palette slightly over time this.updatePalette(); // Draw and update particles this.updateParticles(deltaTime); // Add new particles if needed if (this.particles.length < 100) { this.createParticles(); } // Continue animation requestAnimationFrame(this.animate.bind(this)); } updatePalette() { // Subtly shift colors over time for a dynamic effect this.palette = this.paletteBase.map((color, index) => { const t = this.time + index * 0.5; const variation = 20; return { r: Math.min(255, Math.max(0, color.r + Math.sin(t) * variation)), g: Math.min(255, Math.max(0, color.g + Math.sin(t + 1) * variation)), b: Math.min(255, Math.max(0, color.b + Math.sin(t + 2) * variation)) }; }); } updateParticles(deltaTime) { for (let i = 0; i < this.particles.length; i++) { const p = this.particles[i]; // Apply movement p.x += p.speedX + Math.sin(this.time * p.swaySpeed + p.swayOffset) * p.sway; p.y += p.speedY; p.rotation += p.rotationSpeed; p.lifespan -= 1; // Gradually reduce size and opacity as particle rises const lifeFactor = p.lifespan / 300; const currentSize = p.size * lifeFactor; const currentOpacity = p.opacity * lifeFactor; // Draw particle as a brushstroke-like shape if (p.lifespan > 0) { this.drawBrushstroke( p.x, p.y, currentSize, p.rotation, this.palette[p.colorIndex], currentOpacity ); } // Reset particles that have faded out if (p.lifespan <= 0 || p.y < -100) { this.particles[i] = { x: Math.random() * canvas.width, y: canvas.height + Math.random() * 50, size: 5 + Math.random() * 25, opacity: 0.1 + Math.random() * 0.5, speedX: (Math.random() - 0.5) * 1.5, speedY: -1.5 - Math.random() * 3, colorIndex: Math.floor(Math.random() * this.palette.length), rotation: Math.random() * Math.PI * 2, rotationSpeed: (Math.random() - 0.5) * 0.02, sway: 0.3 + Math.random() * 0.5, swaySpeed: 0.005 + Math.random() * 0.01, swayOffset: Math.random() * Math.PI * 2, lifespan: 100 + Math.random() * 200 }; } } } drawBrushstroke(x, y, size, rotation, color, opacity) { ctx.save(); ctx.translate(x, y); ctx.rotate(rotation); // Create a gradient for more realistic looking flame const gradient = ctx.createLinearGradient(0, -size, 0, size); gradient.addColorStop(0, `rgba(${color.r}, ${color.g}, ${color.b}, 0)`); gradient.addColorStop(0.5, `rgba(${color.r}, ${color.g}, ${color.b}, ${opacity})`); gradient.addColorStop(1, `rgba(${color.r}, ${color.g}, ${color.b}, 0)`); ctx.fillStyle = gradient; // Draw a curved brushstroke shape ctx.beginPath(); ctx.moveTo(-size/3, -size); ctx.quadraticCurveTo(size/2, 0, -size/3, size); ctx.quadraticCurveTo(size/2, 0, size/3, -size/2); ctx.closePath(); ctx.fill(); // Add some smaller accent strokes for texture ctx.fillStyle = `rgba(${color.r}, ${color.g}, ${color.b}, ${opacity * 0.7})`; ctx.beginPath(); ctx.ellipse(size/6, 0, size/4, size/2, 0, 0, Math.PI * 2); ctx.fill(); ctx.restore(); } handleMouseMove(e) { const rect = canvas.getBoundingClientRect(); const mouseX = e.clientX - rect.left; const mouseY = e.clientY - rect.top; // Add some particles near the mouse position for (let i = 0; i < 3; i++) { const offsetX = (Math.random() - 0.5) * 50; const offsetY = (Math.random() - 0.5) * 50; this.particles.push({ x: mouseX + offsetX, y: mouseY + offsetY, size: 10 + Math.random() * 20, opacity: 0.2 + Math.random() * 0.4, speedX: (Math.random() - 0.5) * 2, speedY: -2 - Math.random() * 2, colorIndex: Math.floor(Math.random() * this.palette.length), rotation: Math.random() * Math.PI * 2, rotationSpeed: (Math.random() - 0.5) * 0.03, sway: 0.3 + Math.random() * 0.5, swaySpeed: 0.005 + Math.random() * 0.01, swayOffset: Math.random() * Math.PI * 2, lifespan: 50 + Math.random() * 100 }); } } } // Initialize the fire animation const fireAnimation = new FireAnimation(); // Add interaction for the button const exploreButton = document.getElementById('explore-button'); exploreButton.addEventListener('mouseenter', () => { // Add more particles when hovering over the button for (let i = 0; i < 20; i++) { const rect = exploreButton.getBoundingClientRect(); const centerX = rect.left + rect.width / 2; const centerY = rect.top + rect.height / 2; const offsetX = (Math.random() - 0.5) * rect.width * 1.5; const offsetY = (Math.random() - 0.5) * rect.height * 1.5; fireAnimation.particles.push({ x: centerX + offsetX, y: centerY + offsetY, size: 10 + Math.random() * 15, opacity: 0.2 + Math.random() * 0.4, speedX: (Math.random() - 0.5) * 2, speedY: -2 - Math.random() * 2, colorIndex: Math.floor(Math.random() * fireAnimation.palette.length), rotation: Math.random() * Math.PI * 2, rotationSpeed: (Math.random() - 0.5) * 0.03, sway: 0.3 + Math.random() * 0.5, swaySpeed: 0.005 + Math.random() * 0.01, swayOffset: Math.random() * Math.PI * 2, lifespan: 50 + Math.random() * 100 }); } }); // Toggle portfolio view on button click exploreButton.addEventListener('click', () => { // This would typically navigate to the portfolio section // For the embed case, we'll add an animation effect instead document.querySelectorAll('.nav-item').forEach((item, index) => { item.style.animation = `fadeUp 0.5s ease forwards ${index * 0.1}s`; item.style.opacity = '0'; setTimeout(() => { item.style.opacity = '1'; }, 100); }); // Create a burst of particles for (let i = 0; i < 50; i++) { const rect = exploreButton.getBoundingClientRect(); const centerX = rect.left + rect.width / 2; const centerY = rect.top + rect.height / 2; const angle = Math.random() * Math.PI * 2; const distance = 10 + Math.random() * 50; fireAnimation.particles.push({ x: centerX + Math.cos(angle) * distance, y: centerY + Math.sin(angle) * distance, size: 5 + Math.random() * 25, opacity: 0.2 + Math.random() * 0.6, speedX: Math.cos(angle) * (1 + Math.random() * 3), speedY: Math.sin(angle) * (1 + Math.random() * 3), colorIndex: Math.floor(Math.random() * fireAnimation.palette.length), rotation: Math.random() * Math.PI * 2, rotationSpeed: (Math.random() - 0.5) * 0.05, sway: 0.3 + Math.random() * 0.5, swaySpeed: 0.005 + Math.random() * 0.01, swayOffset: Math.random() * Math.PI * 2, lifespan: 50 + Math.random() * 100 }); } }); // Add interaction for navigation items document.querySelectorAll('.nav-item').forEach(item => { item.addEventListener('mouseenter', () => { const rect = item.getBoundingClientRect(); const centerX = rect.left + rect.width / 2; const centerY = rect.top + rect.height / 2; // Add a few particles near the nav item for (let i = 0; i < 10; i++) { const offsetX = (Math.random() - 0.5) * rect.width; const offsetY = (Math.random() - 0.5) * rect.height; fireAnimation.particles.push({ x: centerX + offsetX, y: centerY + offsetY, size: 5 + Math.random() * 10, opacity: 0.1 + Math.random() * 0.3, speedX: (Math.random() - 0.5) * 1, speedY: -1 - Math.random() * 1, colorIndex: Math.floor(Math.random() * fireAnimation.palette.length), rotation: Math.random() * Math.PI * 2, rotationSpeed: (Math.random() - 0.5) * 0.02, sway: 0.2 + Math.random() * 0.3, swaySpeed: 0.005 + Math.random() * 0.01, swayOffset: Math.random() * Math.PI * 2, lifespan: 30 + Math.random() * 50 }); } }); }); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>TechSpark 2024 - Registration</title> <style> @import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600;700;800&family=Poppins:wght@300;400;500;700&display=swap'); :root { --primary: #ff6b35; --secondary: #ff9a5a; --accent: #ffd166; --dark: #2a2a2a; --light: #ffffff; --success: #06d6a0; } * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Poppins', sans-serif; background-color: #f8f8f8; color: var(--dark); overflow-x: hidden; display: flex; justify-content: center; align-items: center; min-height: 100vh; padding: 20px; } .container { width: 100%; max-width: 660px; position: relative; border-radius: 16px; overflow: hidden; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); background-color: white; } .header { background: linear-gradient(135deg, #ff9a5a 0%, #ff6b35 100%); padding: 30px 40px; position: relative; overflow: hidden; } .event-name { font-family: 'Montserrat', sans-serif; font-weight: 800; font-size: 2.2rem; color: white; margin-bottom: 5px; position: relative; z-index: 2; } .event-tagline { font-size: 1.1rem; color: rgba(255, 255, 255, 0.9); font-weight: 300; margin-bottom: 15px; position: relative; z-index: 2; } .event-details { display: flex; gap: 20px; margin-top: 15px; position: relative; z-index: 2; } .event-date, .event-location { display: flex; align-items: center; color: white; font-size: 0.9rem; } .event-date svg, .event-location svg { margin-right: 8px; width: 18px; height: 18px; } .form-container { padding: 35px 40px; position: relative; } .form-title { font-family: 'Montserrat', sans-serif; font-weight: 700; font-size: 1.5rem; margin-bottom: 25px; color: var(--dark); } .input-group { margin-bottom: 20px; position: relative; } .input-group label { display: block; margin-bottom: 8px; font-weight: 500; font-size: 0.95rem; color: var(--dark); } .input-group input, .input-group select { width: 100%; padding: 12px 15px; border: 2px solid #e8e8e8; border-radius: 8px; font-size: 0.95rem; transition: all 0.3s ease; font-family: 'Poppins', sans-serif; } .input-group input:focus, .input-group select:focus { border-color: var(--primary); outline: none; box-shadow: 0 0 0 3px rgba(255, 107, 53, 0.2); } .input-row { display: flex; gap: 15px; } .input-row .input-group { flex: 1; } .register-btn { background: linear-gradient(135deg, #ff6b35 0%, #ff9a5a 100%); color: white; border: none; padding: 14px 25px; border-radius: 8px; font-weight: 600; font-size: 1rem; cursor: pointer; transition: all 0.3s ease; display: flex; align-items: center; justify-content: center; width: 100%; position: relative; overflow: hidden; z-index: 1; } .register-btn:hover { transform: translateY(-2px); box-shadow: 0 7px 15px rgba(255, 107, 53, 0.3); } .register-btn:active { transform: translateY(0); } /* Fire animation */ .fire-container { position: absolute; bottom: -30px; left: 0; width: 100%; height: 100px; display: flex; justify-content: space-around; pointer-events: none; z-index: 1; } .flame { width: 15px; height: 15px; background: var(--accent); border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%; position: absolute; bottom: -10px; filter: blur(3px); opacity: 0; transform: translateY(0) scale(0.5); animation: flameAnimation 2s infinite ease-out; } @keyframes flameAnimation { 0% { transform: translateY(0) scale(0.5); opacity: 0; } 30% { opacity: 0.8; } 60% { opacity: 0.4; } 100% { transform: translateY(-100px) scale(0); opacity: 0; } } .success-message { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(255, 255, 255, 0.95); display: flex; flex-direction: column; align-items: center; justify-content: center; z-index: 10; opacity: 0; visibility: hidden; transition: all 0.4s ease; transform: translateY(20px); } .success-message.show { opacity: 1; visibility: visible; transform: translateY(0); } .success-icon { width: 80px; height: 80px; background-color: var(--success); border-radius: 50%; display: flex; align-items: center; justify-content: center; margin-bottom: 20px; animation: successPulse 2s infinite; } @keyframes successPulse { 0% { box-shadow: 0 0 0 0 rgba(6, 214, 160, 0.7); } 70% { box-shadow: 0 0 0 20px rgba(6, 214, 160, 0); } 100% { box-shadow: 0 0 0 0 rgba(6, 214, 160, 0); } } .success-title { font-family: 'Montserrat', sans-serif; font-weight: 700; font-size: 1.8rem; margin-bottom: 15px; color: var(--dark); } .success-subtitle { font-size: 1.1rem; text-align: center; color: #666; margin-bottom: 25px; max-width: 80%; } .close-success { background: linear-gradient(135deg, #ff6b35 0%, #ff9a5a 100%); color: white; border: none; padding: 12px 25px; border-radius: 8px; font-weight: 600; cursor: pointer; transition: all 0.3s ease; } .close-success:hover { transform: translateY(-2px); box-shadow: 0 7px 15px rgba(255, 107, 53, 0.3); } .confetti { position: absolute; width: 10px; height: 10px; background-color: #ffd166; opacity: 0; } @media (max-width: 700px) { .container { border-radius: 12px; } .header { padding: 25px 30px; } .event-name { font-size: 1.8rem; } .event-tagline { font-size: 1rem; } .form-container { padding: 25px 30px; } .input-row { flex-direction: column; gap: 0; } .form-title { font-size: 1.3rem; } } </style> </head> <body> <div class="container"> <div class="header"> <h1 class="event-name">TechSpark 2024</h1> <p class="event-tagline">The premier gathering for tech innovators and visionaries</p> <div class="event-details"> <div class="event-date"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" /> </svg> <span>October 15-18, 2024</span> </div> <div class="event-location"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" /> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" /> </svg> <span>San Francisco, CA</span> </div> </div> <div class="fire-container" id="headerFire"></div> </div> <div class="form-container"> <h2 class="form-title">Reserve Your Spot</h2> <form id="registrationForm"> <div class="input-row"> <div class="input-group"> <label for="firstName">First Name</label> <input type="text" id="firstName" required> </div> <div class="input-group"> <label for="lastName">Last Name</label> <input type="text" id="lastName" required> </div> </div> <div class="input-group"> <label for="email">Email Address</label> <input type="email" id="email" required> </div> <div class="input-group"> <label for="company">Company/Organization</label> <input type="text" id="company"> </div> <div class="input-group"> <label for="ticketType">Ticket Type</label> <select id="ticketType" required> <option value="">Select a ticket type</option> <option value="standard">Standard Access ($499)</option> <option value="pro">Pro Access + Workshops ($799)</option> <option value="vip">VIP All-Inclusive ($1,299)</option> </select> </div> <button type="submit" class="register-btn" id="registerBtn"> Secure Your Ticket </button> </form> <div class="success-message" id="successMessage"> <div class="success-icon"> <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" fill="white" viewBox="0 0 16 16"> <path d="M13.485 1.431a1.473 1.473 0 0 1 2.104 2.062l-7.84 9.801a1.473 1.473 0 0 1-2.12.04L.431 8.138a1.473 1.473 0 0 1 2.084-2.083l4.111 4.112 6.82-8.69a.486.486 0 0 1 .04-.045z"/> </svg> </div> <h2 class="success-title">You're Registered!</h2> <p class="success-subtitle">Check your email for confirmation and important event details.</p> <button class="close-success" id="closeSuccess">Got It!</button> </div> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { // Create flames in header const headerFire = document.getElementById('headerFire'); const flamesToCreate = 20; for (let i = 0; i < flamesToCreate; i++) { createFlame(headerFire); } function createFlame(container) { const flame = document.createElement('div'); flame.classList.add('flame'); // Randomize flame properties const size = 10 + Math.random() * 20; const leftPos = Math.random() * 100; const delay = Math.random() * 3; const duration = 1.5 + Math.random() * 2; // Random colors between orange, yellow and red const colors = ['#ff6b35', '#ff9a5a', '#ffd166', '#ffbc42']; const randomColor = colors[Math.floor(Math.random() * colors.length)]; flame.style.width = `${size}px`; flame.style.height = `${size * 1.2}px`; flame.style.left = `${leftPos}%`; flame.style.backgroundColor = randomColor; flame.style.animationDelay = `${delay}s`; flame.style.animationDuration = `${duration}s`; container.appendChild(flame); } // Add interaction flames on input fields const inputs = document.querySelectorAll('input, select'); inputs.forEach(input => { input.addEventListener('focus', function() { createInteractionFlames(this); }); }); function createInteractionFlames(element) { const rect = element.getBoundingClientRect(); const formContainer = document.querySelector('.form-container'); const formRect = formContainer.getBoundingClientRect(); // Create 5 celebratory flames for (let i = 0; i < 5; i++) { const flame = document.createElement('div'); flame.classList.add('flame'); const size = 8 + Math.random() * 12; const leftOffset = Math.random() * rect.width; flame.style.width = `${size}px`; flame.style.height = `${size * 1.2}px`; flame.style.left = `${rect.left - formRect.left + leftOffset}px`; flame.style.bottom = `${formRect.height - (rect.top - formRect.top)}px`; flame.style.animationDuration = '1s'; // Bright colors for interaction flames const colors = ['#ffd166', '#ffbc42', '#ff9a5a', '#ff6b35']; const randomColor = colors[Math.floor(Math.random() * colors.length)]; flame.style.backgroundColor = randomColor; formContainer.appendChild(flame); // Remove flame after animation completes setTimeout(() => { flame.remove(); }, 1000); } } // Form submission handling const registrationForm = document.getElementById('registrationForm'); const successMessage = document.getElementById('successMessage'); const closeSuccess = document.getElementById('closeSuccess'); registrationForm.addEventListener('submit', function(e) { e.preventDefault(); // Create celebratory flames and confetti createCelebration(); // Show success message setTimeout(() => { successMessage.classList.add('show'); }, 800); }); closeSuccess.addEventListener('click', function() { successMessage.classList.remove('show'); // Reset form registrationForm.reset(); }); function createCelebration() { const formContainer = document.querySelector('.form-container'); const containerRect = formContainer.getBoundingClientRect(); // Create flames for (let i = 0; i < 30; i++) { const flame = document.createElement('div'); flame.classList.add('flame'); const size = 10 + Math.random() * 25; const leftPos = Math.random() * containerRect.width; const bottomPos = Math.random() * 100; const delay = Math.random() * 0.5; flame.style.width = `${size}px`; flame.style.height = `${size * 1.2}px`; flame.style.left = `${leftPos}px`; flame.style.bottom = `${bottomPos}px`; flame.style.animationDelay = `${delay}s`; flame.style.animationDuration = '1.5s'; // Bright celebration colors const colors = ['#ffd166', '#ffbc42', '#ff9a5a', '#ff6b35', '#ffa07a']; const randomColor = colors[Math.floor(Math.random() * colors.length)]; flame.style.backgroundColor = randomColor; formContainer.appendChild(flame); // Remove flame after animation completes setTimeout(() => { flame.remove(); }, 1500 + delay * 1000); } // Create confetti for (let i = 0; i < 80; i++) { createConfetti(formContainer, containerRect); } // Trigger button pulse effect const registerBtn = document.getElementById('registerBtn'); registerBtn.classList.add('pulse'); setTimeout(() => { registerBtn.classList.remove('pulse'); }, 1000); } function createConfetti(container, containerRect) { const confetti = document.createElement('div'); confetti.classList.add('confetti'); const size = 5 + Math.random() * 10; const startLeft = Math.random() * containerRect.width; const startTop = Math.random() * (containerRect.height / 2); // Random confetti shapes const shapes = ['circle', 'square', 'triangle']; const shape = shapes[Math.floor(Math.random() * shapes.length)]; // Random confetti colors const colors = ['#ffd166', '#ffbc42', '#ff9a5a', '#ff6b35', '#06d6a0', '#1b9aaa']; const color = colors[Math.floor(Math.random() * colors.length)]; confetti.style.width = `${size}px`; confetti.style.height = `${size}px`; confetti.style.left = `${startLeft}px`; confetti.style.top = `${startTop}px`; confetti.style.backgroundColor = color; if (shape === 'circle') { confetti.style.borderRadius = '50%'; } else if (shape === 'triangle') { confetti.style.width = '0'; confetti.style.height = '0'; confetti.style.backgroundColor = 'transparent'; confetti.style.borderLeft = `${size/2}px solid transparent`; confetti.style.borderRight = `${size/2}px solid transparent`; confetti.style.borderBottom = `${size}px solid ${color}`; } container.appendChild(confetti); // Animate confetti const duration = 1 + Math.random() * 2; const rotation = Math.random() * 360; const xMovement = -50 + Math.random() * 100; confetti.animate([ { transform: `translate(0, 0) rotate(0deg)`, opacity: 0 }, { transform: `translate(${xMovement}px, ${containerRect.height/3}px) rotate(${rotation}deg)`, opacity: 0.8, offset: 0.4 }, { transform: `translate(${xMovement*1.5}px, ${containerRect.height}px) rotate(${rotation*2}deg)`, opacity: 0 } ], { duration: duration * 1000, easing: 'cubic-bezier(0.215, 0.610, 0.355, 1.000)', fill: 'forwards' }); // Remove confetti after animation setTimeout(() => { confetti.remove(); }, duration * 1000); } }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>IGNITE Energy Drink</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } body { background-color: #000; overflow: hidden; width: 100%; height: 100vh; display: flex; justify-content: center; align-items: center; perspective: 1000px; } .container { position: relative; width: 700px; height: 700px; display: flex; justify-content: center; align-items: center; flex-direction: column; } .fire-container { position: absolute; width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; z-index: 1; } .fire-base { position: absolute; bottom: 10%; width: 300px; height: 300px; background: radial-gradient(circle at center, #ff4500, transparent 70%); border-radius: 50%; animation: pulse 2s infinite alternate; filter: blur(5px); z-index: 2; } @keyframes pulse { 0% { transform: scale(0.95); opacity: 0.7; } 100% { transform: scale(1.05); opacity: 1; } } .fire-particle { position: absolute; bottom: 20%; width: 20px; height: 60px; background: linear-gradient(to top, #ff4500, #ffcc00); filter: blur(3px); border-radius: 50% 50% 20% 20%; transform-origin: bottom center; z-index: 3; } .product { position: relative; width: 100px; height: 300px; background: linear-gradient(135deg, #333 0%, #111 100%); border-radius: 10px 10px 30px 30px; box-shadow: 0 0 20px rgba(255, 69, 0, 0.5); transform-style: preserve-3d; z-index: 5; transition: transform 0.5s ease; cursor: pointer; } .product:hover { transform: rotateY(10deg) translateZ(20px); } .product::before { content: ''; position: absolute; top: -10px; left: 25px; width: 50px; height: 15px; background: #222; border-radius: 5px; } .product::after { content: ''; position: absolute; top: 50px; left: 5px; width: 90px; height: 200px; background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="90" height="200" viewBox="0 0 90 200"><path d="M45,10 L70,50 L70,150 L20,150 L20,50 Z" fill="none" stroke="%23ff4500" stroke-width="2" /></svg>'); opacity: 0.8; } .logo { position: absolute; top: 100px; left: 50%; transform: translateX(-50%); color: #ff4500; font-size: 24px; font-weight: 900; text-shadow: 0 0 5px rgba(255, 69, 0, 0.7); letter-spacing: 2px; z-index: 10; text-transform: uppercase; } .metallic-accent { position: absolute; width: 100%; height: 20px; background: linear-gradient(90deg, #333, #ccc, #333); top: 80px; animation: shine 3s infinite; } @keyframes shine { 0% { background-position: -200px; } 100% { background-position: 200px; } } .slogan { position: absolute; bottom: 150px; color: #fff; font-size: 32px; font-weight: 800; text-shadow: 0 0 10px rgba(255, 69, 0, 0.7); text-transform: uppercase; letter-spacing: 3px; text-align: center; z-index: 10; width: 100%; transform: translateY(0); animation: float 4s ease-in-out infinite; } @keyframes float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-10px); } } .tagline { position: absolute; bottom: 120px; color: #ccc; font-size: 16px; font-weight: 400; text-align: center; width: 80%; z-index: 10; } .cta-button { position: absolute; bottom: 60px; padding: 15px 40px; background: linear-gradient(45deg, #ff4500, #ff7e00); border: none; border-radius: 30px; color: white; font-size: 18px; font-weight: 700; text-transform: uppercase; cursor: pointer; z-index: 10; letter-spacing: 1px; box-shadow: 0 5px 15px rgba(255, 69, 0, 0.4); transition: all 0.3s ease; overflow: hidden; } .cta-button:hover { transform: translateY(-3px); box-shadow: 0 8px 25px rgba(255, 69, 0, 0.6); } .cta-button::after { content: ""; position: absolute; top: 0; left: -100%; width: 100%; height: 100%; background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent); transition: 0.5s; } .cta-button:hover::after { left: 100%; } .energy-pulse { position: absolute; width: 300px; height: 300px; background: radial-gradient(circle, rgba(255,69,0,0.5) 0%, transparent 70%); border-radius: 50%; z-index: 1; opacity: 0; animation: pulse-ring 3s infinite; } @keyframes pulse-ring { 0% { transform: scale(0.5); opacity: 0.3; } 80%, 100% { transform: scale(2); opacity: 0; } } .stats { position: absolute; display: flex; justify-content: space-around; bottom: 20px; width: 80%; z-index: 10; } .stat { display: flex; flex-direction: column; align-items: center; color: #fff; } .stat-value { font-size: 20px; font-weight: 700; color: #ff4500; } .stat-label { font-size: 12px; text-transform: uppercase; } .energy-bar { position: absolute; top: 0; width: 100%; height: 30px; background: linear-gradient(to right, #ff4500, #ff7e00); z-index: 10; transform-origin: left; animation: energize 3s infinite alternate ease-in-out; } @keyframes energize { 0% { transform: scaleX(0.3); } 100% { transform: scaleX(1); } } .particles { position: absolute; width: 100%; height: 100%; z-index: 0; } .particle { position: absolute; background-color: #ff4500; width: 3px; height: 3px; border-radius: 50%; animation: moveUp linear infinite; } @keyframes moveUp { 0% { transform: translateY(0) rotate(0deg); opacity: 0; } 50% { opacity: 1; } 100% { transform: translateY(-700px) rotate(360deg); opacity: 0; } } /* Mobile Responsive */ @media (max-width: 700px) { .product { width: 80px; height: 240px; } .logo { font-size: 20px; top: 80px; } .slogan { font-size: 24px; bottom: 140px; } .tagline { font-size: 14px; bottom: 110px; } .cta-button { padding: 12px 30px; font-size: 16px; bottom: 50px; } .stats { flex-direction: column; align-items: center; bottom: 15px; } .stat { margin-bottom: 5px; } } </style> </head> <body> <div class="container"> <div class="energy-bar"></div> <div class="fire-container"> <div class="fire-base"></div> <div class="energy-pulse"></div> </div> <div class="product"> <div class="metallic-accent"></div> <div class="logo">IGNITE</div> </div> <div class="slogan">UNLEASH YOUR INFERNO</div> <div class="tagline">The only energy drink crafted with thermogenic compounds for sustained intensity and zero crash</div> <button class="cta-button">FEEL THE BURN</button> <div class="stats"> <div class="stat"> <div class="stat-value">200</div> <div class="stat-label">mg caffeine</div> </div> <div class="stat"> <div class="stat-value">0</div> <div class="stat-label">sugar</div> </div> <div class="stat"> <div class="stat-value">5</div> <div class="stat-label">b vitamins</div> </div> </div> <div class="particles"></div> </div> <script> document.addEventListener('DOMContentLoaded', function() { // Create fire particles const fireContainer = document.querySelector('.fire-container'); for (let i = 0; i < 20; i++) { const particle = document.createElement('div'); particle.classList.add('fire-particle'); // Randomize position, size and animation const size = 5 + Math.random() * 15; const posX = 150 - 75 + Math.random() * 150; const delay = Math.random() * 2; const duration = 1 + Math.random() * 3; particle.style.width = `${size}px`; particle.style.height = `${size * 3}px`; particle.style.left = `${posX}px`; particle.style.animationDelay = `${delay}s`; // Add custom animation particle.style.animation = ` moveUp ${duration}s infinite ease-out ${delay}s, fadeOut ${duration}s infinite ease-out ${delay}s `; fireContainer.appendChild(particle); } // Create background particles const particlesContainer = document.querySelector('.particles'); for (let i = 0; i < 100; i++) { const particle = document.createElement('div'); particle.classList.add('particle'); // Random position const posX = Math.random() * 700; const posY = Math.random() * 700; // Random size const size = Math.random() * 4; // Random animation duration const duration = 3 + Math.random() * 10; particle.style.left = `${posX}px`; particle.style.bottom = `${posY}px`; particle.style.width = `${size}px`; particle.style.height = `${size}px`; particle.style.animationDuration = `${duration}s`; particle.style.animationDelay = `${Math.random() * 5}s`; // Add particles particlesContainer.appendChild(particle); } // Add energy pulse effects const pulseContainer = document.querySelector('.fire-container'); setInterval(() => { const pulse = document.createElement('div'); pulse.classList.add('energy-pulse'); pulseContainer.appendChild(pulse); // Remove after animation completes setTimeout(() => { pulse.remove(); }, 3000); }, 1500); // Product can rotation on mouse move const product = document.querySelector('.product'); document.addEventListener('mousemove', (e) => { const xAxis = (window.innerWidth / 2 - e.pageX) / 15; const yAxis = (window.innerHeight / 2 - e.pageY) / 15; product.style.transform = `rotateY(${xAxis}deg) rotateX(${yAxis}deg)`; }); // CTA button effects const button = document.querySelector('.cta-button'); button.addEventListener('click', function() { // Create a burst of energy for (let i = 0; i < 10; i++) { const pulse = document.createElement('div'); pulse.classList.add('energy-pulse'); pulse.style.animationDuration = `${1 + Math.random()}s`; pulseContainer.appendChild(pulse); setTimeout(() => { pulse.remove(); }, 3000); } // Button animation this.style.transform = 'scale(0.95)'; setTimeout(() => { this.style.transform = ''; }, 200); }); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Ember - Meditative Flame</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Montserrat', sans-serif; } body { display: flex; justify-content: center; align-items: center; min-height: 100vh; background-color: #111; overflow: hidden; } .app-container { position: relative; width: 100%; max-width: 700px; height: 700px; background: linear-gradient(to top, #0c0c0c, #181818); border-radius: 24px; overflow: hidden; box-shadow: 0 20px 50px rgba(0, 0, 0, 0.5); } .flame-container { position: absolute; bottom: 120px; left: 50%; transform: translateX(-50%); width: 200px; height: 240px; display: flex; justify-content: center; filter: blur(5px); opacity: 0.85; transition: opacity 0.5s; } .flame-base { position: absolute; bottom: -40px; width: 100px; height: 40px; background: #662a00; border-radius: 50%; filter: blur(8px); transform: scale(1.5, 0.5); box-shadow: 0 0 40px 20px rgba(255, 108, 28, 0.2); opacity: 0.5; } .flame { position: absolute; bottom: 0; width: 80px; height: 80px; background: radial-gradient(circle at 60% 60%, #ff9d68, #ff7425); border-radius: 50% 50% 35% 35% / 50% 50% 35% 35%; transform-origin: center bottom; animation: flame-dance 4s infinite alternate ease-in-out; opacity: 0.8; box-shadow: 0 0 80px 30px rgba(255, 137, 28, 0.25); } .inner-flame { position: absolute; bottom: 20px; left: 25%; width: 40px; height: 40px; background: radial-gradient(circle at 50% 40%, #ffd1aa, #ff9955); border-radius: 50% 50% 35% 35% / 50% 50% 35% 35%; transform-origin: center bottom; animation: inner-flame-dance 3s infinite alternate ease-in-out; opacity: 0.6; } .center-flame { position: absolute; bottom: 40px; left: 32%; width: 28px; height: 28px; background: radial-gradient(circle at 50% 40%, #fff5e6, #ffb380); border-radius: 50%; transform-origin: center bottom; animation: center-flame-dance 2.5s infinite alternate ease-in-out; opacity: 0.4; } @keyframes flame-dance { 0% { transform: scaleY(0.9) scaleX(1.05) rotate(-1deg); height: 80px; } 100% { transform: scaleY(1) scaleX(0.95) rotate(1deg); height: 85px; } } @keyframes inner-flame-dance { 0% { transform: scaleY(0.95) scaleX(1.05) rotate(2deg); height: 40px; } 100% { transform: scaleY(1.05) scaleX(0.95) rotate(-2deg); height: 42px; } } @keyframes center-flame-dance { 0% { transform: scaleY(0.9) scaleX(1.1); opacity: 0.3; } 100% { transform: scaleY(1.1) scaleX(0.9); opacity: 0.5; } } .breathing-circle { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 300px; height: 300px; border-radius: 50%; box-shadow: 0 0 60px 10px rgba(255, 147, 66, 0.1); background: radial-gradient(circle at center, rgba(255, 147, 66, 0.05) 0%, transparent 70%); animation: breathing-pulse 6s infinite ease-in-out; z-index: -1; } @keyframes breathing-pulse { 0%, 100% { transform: translate(-50%, -50%) scale(0.8); opacity: 0.3; } 50% { transform: translate(-50%, -50%) scale(1.2); opacity: 0.5; } } .app-header { position: absolute; top: 40px; left: 0; width: 100%; padding: 0 40px; color: #fff; opacity: 0.8; } .app-title { font-size: 28px; font-weight: 300; margin-bottom: 8px; letter-spacing: 1px; color: #ffb380; } .app-subtitle { font-size: 14px; font-weight: 300; opacity: 0.7; max-width: 80%; line-height: 1.5; } .controls { position: absolute; bottom: 40px; left: 0; width: 100%; padding: 0 40px; display: flex; flex-direction: column; align-items: center; } .timer-display { color: #ffb380; font-size: 36px; font-weight: 200; margin-bottom: 20px; letter-spacing: 2px; opacity: 0.8; } .breathing-text { color: #fff; font-size: 18px; margin-bottom: 25px; opacity: 0; transition: opacity 1s ease; font-weight: 300; letter-spacing: 0.5px; } .buttons-container { display: flex; gap: 20px; } .control-btn { background: transparent; border: 1px solid rgba(255, 179, 128, 0.3); border-radius: 30px; color: #ffb380; padding: 10px 25px; font-size: 15px; cursor: pointer; transition: all 0.3s ease; backdrop-filter: blur(5px); } .control-btn:hover { background-color: rgba(255, 179, 128, 0.1); border-color: rgba(255, 179, 128, 0.5); } .control-btn.active { background-color: rgba(255, 179, 128, 0.2); border-color: rgba(255, 179, 128, 0.6); } .settings { position: absolute; top: 40px; right: 40px; display: flex; gap: 15px; } .settings-btn { background: transparent; border: none; color: rgba(255, 255, 255, 0.5); cursor: pointer; font-size: 20px; transition: color 0.3s ease; width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; border-radius: 50%; } .settings-btn:hover { color: rgba(255, 255, 255, 0.8); } .settings-btn i { font-size: 18px; } .settings-panel { position: absolute; top: 90px; right: 40px; background: rgba(18, 18, 18, 0.8); backdrop-filter: blur(10px); border-radius: 15px; padding: 20px; width: 280px; transform: translateY(-20px); opacity: 0; visibility: hidden; transition: all 0.3s ease; z-index: 10; } .settings-panel.visible { transform: translateY(0); opacity: 1; visibility: visible; } .settings-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; padding-bottom: 10px; border-bottom: 1px solid rgba(255, 255, 255, 0.1); } .settings-title { color: #ffb380; font-size: 16px; font-weight: 500; } .close-settings { background: transparent; border: none; color: rgba(255, 255, 255, 0.5); cursor: pointer; font-size: 16px; } .settings-section { margin-bottom: 15px; } .settings-label { color: rgba(255, 255, 255, 0.7); font-size: 14px; margin-bottom: 8px; display: block; } .slider-container { display: flex; align-items: center; gap: 10px; } .slider { -webkit-appearance: none; width: 100%; height: 4px; border-radius: 2px; background: rgba(255, 255, 255, 0.1); outline: none; } .slider::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 16px; height: 16px; border-radius: 50%; background: #ffb380; cursor: pointer; } .slider-value { color: rgba(255, 255, 255, 0.5); font-size: 12px; min-width: 30px; text-align: right; } .preset-buttons { display: flex; gap: 10px; margin-top: 5px; } .preset-btn { background: transparent; border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 30px; color: rgba(255, 255, 255, 0.7); padding: 6px 12px; font-size: 12px; cursor: pointer; transition: all 0.3s ease; } .preset-btn:hover, .preset-btn.active { background-color: rgba(255, 179, 128, 0.2); border-color: rgba(255, 179, 128, 0.5); color: #ffb380; } .sound-options { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 5px; } .sound-option { background: transparent; border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 30px; color: rgba(255, 255, 255, 0.7); padding: 6px 12px; font-size: 12px; cursor: pointer; transition: all 0.3s ease; } .sound-option:hover, .sound-option.active { background-color: rgba(255, 179, 128, 0.2); border-color: rgba(255, 179, 128, 0.5); color: #ffb380; } @media (max-width: 600px) { .app-container { border-radius: 0; } .app-header { padding: 0 20px; } .app-title { font-size: 24px; } .controls { padding: 0 20px; } .timer-display { font-size: 32px; } .settings { right: 20px; } .settings-panel { right: 20px; width: 250px; } .breathing-circle { width: 260px; height: 260px; } .flame-container { bottom: 140px; } } /* Link to Montserrat font */ @import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@200;300;400;500&display=swap'); /* Link to Font Awesome */ @import url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css'); </style> </head> <body> <div class="app-container"> <div class="app-header"> <h1 class="app-title">Ember</h1> <p class="app-subtitle">Let the gentle flame guide your breath into a state of deep relaxation and mindfulness</p> </div> <div class="breathing-circle"></div> <div class="flame-container"> <div class="flame-base"></div> <div class="flame"></div> <div class="inner-flame"></div> <div class="center-flame"></div> </div> <div class="settings"> <button class="settings-btn" id="settingsBtn"> <i class="fas fa-sliders"></i> </button> <button class="settings-btn" id="soundBtn"> <i class="fas fa-volume-high"></i> </button> </div> <div class="settings-panel" id="settingsPanel"> <div class="settings-header"> <span class="settings-title">Customize Your Practice</span> <button class="close-settings" id="closeSettings">×</button> </div> <div class="settings-section"> <label class="settings-label">Breathing Rhythm</label> <div class="preset-buttons"> <button class="preset-btn active" data-in="4" data-hold="4" data-out="4">4-4-4</button> <button class="preset-btn" data-in="4" data-hold="7" data-out="8">4-7-8</button> <button class="preset-btn" data-in="5" data-hold="0" data-out="5">5-5</button> <button class="preset-btn" data-in="6" data-hold="2" data-out="7">6-2-7</button> </div> </div> <div class="settings-section"> <label class="settings-label">Flame Intensity</label> <div class="slider-container"> <input type="range" min="1" max="10" value="5" class="slider" id="flameIntensity"> <span class="slider-value" id="flameValue">5</span> </div> </div> <div class="settings-section"> <label class="settings-label">Ambient Sound</label> <div class="sound-options"> <button class="sound-option active" data-sound="none">None</button> <button class="sound-option" data-sound="fire">Crackling Fire</button> <button class="sound-option" data-sound="rain">Gentle Rain</button> <button class="sound-option" data-sound="forest">Forest Night</button> </div> </div> <div class="settings-section"> <label class="settings-label">Session Duration</label> <div class="slider-container"> <input type="range" min="1" max="60" value="10" class="slider" id="sessionDuration"> <span class="slider-value" id="durationValue">10m</span> </div> </div> </div> <div class="controls"> <div class="timer-display" id="timer">10:00</div> <div class="breathing-text" id="breathingText">Breathe in...</div> <div class="buttons-container"> <button class="control-btn" id="startBtn">Begin Practice</button> <button class="control-btn" id="resetBtn">Reset</button> </div> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { // Elements const startBtn = document.getElementById('startBtn'); const resetBtn = document.getElementById('resetBtn'); const timer = document.getElementById('timer'); const breathingText = document.getElementById('breathingText'); const settingsBtn = document.getElementById('settingsBtn'); const soundBtn = document.getElementById('soundBtn'); const settingsPanel = document.getElementById('settingsPanel'); const closeSettings = document.getElementById('closeSettings'); const flameIntensity = document.getElementById('flameIntensity'); const flameValue = document.getElementById('flameValue'); const sessionDuration = document.getElementById('sessionDuration'); const durationValue = document.getElementById('durationValue'); const presetButtons = document.querySelectorAll('.preset-btn'); const soundOptions = document.querySelectorAll('.sound-option'); const flameContainer = document.querySelector('.flame-container'); const flame = document.querySelector('.flame'); const innerFlame = document.querySelector('.inner-flame'); const centerFlame = document.querySelector('.center-flame'); const breathingCircle = document.querySelector('.breathing-circle'); // Session state let isRunning = false; let isPaused = false; let timerInterval; let breathingInterval; let timeRemaining = 10 * 60; // 10 minutes in seconds by default let breathingPhase = 'in'; let breatheInDuration = 4; // seconds let breatheHoldDuration = 4; // seconds let breatheOutDuration = 4; // seconds let isMuted = false; // Update timer display function updateTimerDisplay() { const minutes = Math.floor(timeRemaining / 60); const seconds = timeRemaining % 60; timer.textContent = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; } // Start session function startSession() { if (!isRunning) { isRunning = true; startBtn.textContent = 'Pause'; startBtn.classList.add('active'); timerInterval = setInterval(() => { timeRemaining--; updateTimerDisplay(); if (timeRemaining <= 0) { endSession(); } }, 1000); // Start breathing cycle startBreathingCycle(); } else if (!isPaused) { // Pause session isPaused = true; startBtn.textContent = 'Resume'; clearInterval(timerInterval); clearInterval(breathingInterval); breathingText.style.opacity = 0; } else { // Resume session isPaused = false; startBtn.textContent = 'Pause'; timerInterval = setInterval(() => { timeRemaining--; updateTimerDisplay(); if (timeRemaining <= 0) { endSession(); } }, 1000); // Resume breathing cycle startBreathingCycle(); } } // Reset session function resetSession() { isRunning = false; isPaused = false; clearInterval(timerInterval); clearInterval(breathingInterval); timeRemaining = parseInt(sessionDuration.value) * 60; updateTimerDisplay(); startBtn.textContent = 'Begin Practice'; startBtn.classList.remove('active'); breathingText.style.opacity = 0; // Reset flame animation updateFlameIntensity(flameIntensity.value); } // End session function endSession() { isRunning = false; isPaused = false; clearInterval(timerInterval); clearInterval(breathingInterval); startBtn.textContent = 'Begin Practice'; startBtn.classList.remove('active'); breathingText.style.opacity = 0; // Reset flame animation timeRemaining = parseInt(sessionDuration.value) * 60; updateTimerDisplay(); } // Start breathing cycle function startBreathingCycle() { let phase = 'in'; let phaseTime = 0; breathingText.textContent = 'Breathe in...'; breathingText.style.opacity = 1; animateBreathIn(); breathingInterval = setInterval(() => { phaseTime++; if (phase === 'in' && phaseTime >= breatheInDuration) { phase = 'hold'; phaseTime = 0; if (breatheHoldDuration > 0) { breathingText.textContent = 'Hold...'; animateBreathHold(); } else { phase = 'out'; breathingText.textContent = 'Breathe out...'; animateBreathOut(); } } else if (phase === 'hold' && phaseTime >= breatheHoldDuration) { phase = 'out'; phaseTime = 0; breathingText.textContent = 'Breathe out...'; animateBreathOut(); } else if (phase === 'out' && phaseTime >= breatheOutDuration) { phase = 'in'; phaseTime = 0; breathingText.textContent = 'Breathe in...'; animateBreathIn(); } }, 1000); } // Animate breath in function animateBreathIn() { breathingCircle.style.animation = `breathing-pulse ${breatheInDuration}s ease-in forwards`; const flameScale = 1 + (parseInt(flameIntensity.value) / 20); flame.style.transform = `scaleY(${flameScale}) scaleX(${flameScale * 0.95})`; flame.style.opacity = 0.8 * (parseInt(flameIntensity.value) / 10); } // Animate breath hold function animateBreathHold() { breathingCircle.style.animation = 'none'; breathingCircle.style.transform = 'translate(-50%, -50%) scale(1.2)'; breathingCircle.style.opacity = '0.5'; } // Animate breath out function animateBreathOut() { breathingCircle.style.animation = `breathing-pulse ${breatheOutDuration}s ease-out reverse`; flame.style.transform = 'scaleY(0.9) scaleX(1.05)'; flame.style.opacity = 0.8 * (parseInt(flameIntensity.value) / 10 * 0.7); } // Toggle settings panel function toggleSettings() { settingsPanel.classList.toggle('visible'); } // Toggle sound function toggleSound() { isMuted = !isMuted; soundBtn.innerHTML = isMuted ? '<i class="fas fa-volume-mute"></i>' : '<i class="fas fa-volume-high"></i>'; } // Update flame intensity function updateFlameIntensity(value) { flameValue.textContent = value; // Adjust flame size and opacity based on intensity const intensity = parseInt(value) / 10; flame.style.opacity = 0.6 + (intensity * 0.4); innerFlame.style.opacity = 0.4 + (intensity * 0.4); centerFlame.style.opacity = 0.3 + (intensity * 0.3); const scaleBase = 0.8 + (intensity * 0.4); flame.style.transform = `scaleY(${scaleBase}) scaleX(${scaleBase * 0.95}) rotate(-1deg)`; // Adjust animation speed based on intensity const animSpeed = 5 - (intensity * 1.5); document.documentElement.style.setProperty('--flame-anim-duration', `${animSpeed}s`); } // Update session duration function updateSessionDuration(value) { durationValue.textContent = `${value}m`; timeRemaining = parseInt(value) * 60; updateTimerDisplay(); } // Set breathing preset function setBreathingPreset(inDuration, holdDuration, outDuration) { breatheInDuration = inDuration; breatheHoldDuration = holdDuration; breatheOutDuration = outDuration; } // Set ambient sound function setAmbientSound(sound) { // Would actually implement sound here with an Audio API console.log(`Setting ambient sound to: ${sound}`); } // Event listeners startBtn.addEventListener('click', startSession); resetBtn.addEventListener('click', resetSession); settingsBtn.addEventListener('click', toggleSettings); soundBtn.addEventListener('click', toggleSound); closeSettings.addEventListener('click', toggleSettings); flameIntensity.addEventListener('input', function() { updateFlameIntensity(this.value); }); sessionDuration.addEventListener('input', function() { updateSessionDuration(this.value); }); // Preset button listeners presetButtons.forEach(button => { button.addEventListener('click', function() { presetButtons.forEach(btn => btn.classList.remove('active')); this.classList.add('active'); const inDuration = parseInt(this.getAttribute('data-in')); const holdDuration = parseInt(this.getAttribute('data-hold')); const outDuration = parseInt(this.getAttribute('data-out')); setBreathingPreset(inDuration, holdDuration, outDuration); }); }); // Sound option listeners soundOptions.forEach(option => { option.addEventListener('click', function() { soundOptions.forEach(opt => opt.classList.remove('active')); this.classList.add('active'); const sound = this.getAttribute('data-sound'); setAmbientSound(sound); }); }); // Initialize updateTimerDisplay(); updateFlameIntensity(flameIntensity.value); updateSessionDuration(sessionDuration.value); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Virtual Fireplace</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { background-color: #1a1a1a; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; height: 100vh; width: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; position: relative; overflow: hidden; } .fireplace-container { width: 650px; height: 600px; position: relative; border-radius: 5px; overflow: hidden; box-shadow: 0 0 20px 5px rgba(255, 165, 0, 0.2); background-color: #201a17; } .mantle { position: absolute; bottom: 0; width: 100%; height: 180px; background: linear-gradient(to bottom, #432616, #291810); display: flex; flex-direction: column; align-items: center; box-shadow: 0 -5px 10px rgba(0, 0, 0, 0.5) inset; z-index: 10; } .mantle-top { width: 100%; height: 30px; background: linear-gradient(to bottom, #5a3622, #432616); box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3); } .logs { position: absolute; bottom: 20px; width: 300px; height: 50px; display: flex; justify-content: center; z-index: 5; } .log { width: 80px; height: 30px; background: linear-gradient(45deg, #3a2614, #5a3a22); border-radius: 15px; position: absolute; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5); } .log:nth-child(1) { transform: rotate(20deg); left: 30px; bottom: 10px; } .log:nth-child(2) { transform: rotate(-15deg); right: 30px; bottom: 15px; } .log:nth-child(3) { transform: rotate(0deg); bottom: 25px; } .ember { position: absolute; bottom: 60px; width: 280px; height: 30px; background: radial-gradient(ellipse at center, #ff4500, transparent 70%); border-radius: 50%; opacity: 0.7; filter: blur(5px); z-index: 3; } .fire-container { position: absolute; bottom: 70px; left: 50%; transform: translateX(-50%); height: 180px; width: 200px; display: flex; justify-content: center; z-index: 4; } .fire-base { position: absolute; bottom: 0; width: 120px; height: 120px; background: radial-gradient(ellipse at center, #ff8c00, #ff4500); border-radius: 45% 45% 45% 45% / 40% 40% 60% 60%; opacity: 0.9; filter: blur(5px); animation: base-flicker 1s infinite alternate; } .flame { position: absolute; bottom: 0; width: 60px; height: 150px; background: linear-gradient(to top, #ff4500, #ff8c00, #ffd700); border-radius: 50% 50% 30% 30% / 60% 60% 40% 40%; transform-origin: center bottom; opacity: 0.8; filter: blur(5px); animation: flicker 0.5s infinite alternate; } .flame:nth-child(2) { left: 30px; height: 140px; width: 50px; background: linear-gradient(to top, #ff4500, #ff7f00, #ffd700); animation-delay: 0.1s; animation-duration: 0.6s; } .flame:nth-child(3) { left: 90px; height: 130px; width: 45px; background: linear-gradient(to top, #ff4500, #ff7f00, #ffed99); animation-delay: 0.15s; animation-duration: 0.7s; } .flame:nth-child(4) { left: 60px; height: 160px; width: 55px; background: linear-gradient(to top, #ff4500, #ffa500, #ffff00); animation-delay: 0.2s; animation-duration: 0.5s; } .spark { position: absolute; bottom: 90px; width: 4px; height: 4px; background-color: #ffd700; border-radius: 50%; opacity: 0; z-index: 6; } .glow { position: absolute; bottom: 25px; left: 50%; transform: translateX(-50%); width: 400px; height: 100px; background: radial-gradient(ellipse at center, rgba(255, 165, 0, 0.4), transparent 70%); border-radius: 50%; z-index: 2; animation: glow 2s infinite alternate; } .controls { display: flex; justify-content: space-between; width: 650px; margin-top: 20px; background: rgba(30, 30, 30, 0.7); padding: 15px; border-radius: 5px; color: #ffffff; } .control-group { display: flex; align-items: center; } .control-label { margin-right: 10px; font-size: 14px; } .slider { -webkit-appearance: none; width: 100px; height: 4px; background: #4e4e4e; border-radius: 2px; outline: none; } .slider::-webkit-slider-thumb { -webkit-appearance: none; width: 12px; height: 12px; border-radius: 50%; background: #ff7f00; cursor: pointer; } .toggle-sound { background: none; border: 1px solid #ff7f00; color: #ff7f00; padding: 5px 10px; border-radius: 3px; cursor: pointer; transition: all 0.3s ease; } .toggle-sound:hover { background-color: rgba(255, 127, 0, 0.2); } .toggle-sound.active { background-color: #ff7f00; color: #1a1a1a; } .ambiance-info { position: absolute; top: 15px; left: 15px; padding: 10px 15px; background: rgba(0, 0, 0, 0.6); color: #fff; border-radius: 4px; font-size: 14px; max-width: 250px; opacity: 0.8; transition: opacity 0.3s ease; z-index: 20; } .ambiance-info:hover { opacity: 1; } .ambiance-info h3 { margin-bottom: 5px; color: #ff7f00; font-size: 16px; } @keyframes flicker { 0% { height: 130px; opacity: 0.9; } 25% { height: 140px; opacity: 0.8; } 50% { height: 150px; opacity: 0.9; } 75% { height: 145px; opacity: 0.8; } 100% { height: 130px; opacity: 0.9; } } @keyframes base-flicker { 0% { transform: scale(1); opacity: 0.9; } 100% { transform: scale(1.05); opacity: 0.8; } } @keyframes glow { 0% { opacity: 0.5; } 100% { opacity: 0.8; } } @keyframes sparks { 0% { transform: translateY(0) scale(1); opacity: 1; } 100% { transform: translateY(-100px) scale(0); opacity: 0; } } @keyframes float { 0% { transform: translateY(0); } 50% { transform: translateY(-10px); } 100% { transform: translateY(0); } } .fireplace-frame { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><rect x="0" y="0" width="100" height="100" fill="none" stroke="%23472b1a" stroke-width="8"/></svg>'); background-size: 100% 100%; pointer-events: none; z-index: 12; } .smoke-particle { position: absolute; bottom: 140px; background: rgba(200, 200, 200, 0.15); border-radius: 50%; opacity: 0; filter: blur(3px); z-index: 3; } @media (max-width: 700px) { .fireplace-container { width: 95%; height: 500px; } .controls { width: 95%; flex-direction: column; gap: 10px; } .control-group { justify-content: space-between; width: 100%; } .ambiance-info { max-width: 200px; font-size: 12px; } .mantle { height: 150px; } } </style> </head> <body> <div class="fireplace-container"> <div class="ambiance-info"> <h3>Nordic Pine Fireplace</h3> <p>This sustainably sourced pine wood creates a bright flame with a subtle sweet aroma, perfect for quiet winter evenings.</p> </div> <div class="fire-container"> <div class="flame"></div> <div class="flame"></div> <div class="flame"></div> <div class="flame"></div> <div class="fire-base"></div> </div> <div class="ember"></div> <div class="glow"></div> <div class="logs"> <div class="log"></div> <div class="log"></div> <div class="log"></div> </div> <div class="mantle"> <div class="mantle-top"></div> </div> <div class="fireplace-frame"></div> </div> <div class="controls"> <div class="control-group"> <span class="control-label">Flame Intensity</span> <input type="range" min="1" max="10" value="7" class="slider" id="intensitySlider"> </div> <div class="control-group"> <span class="control-label">Flame Color</span> <input type="range" min="1" max="10" value="5" class="slider" id="colorSlider"> </div> <div class="control-group"> <button class="toggle-sound" id="soundToggle">Turn Sound On</button> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { const fireContainer = document.querySelector('.fire-container'); const flames = document.querySelectorAll('.flame'); const fireBase = document.querySelector('.fire-base'); const intensitySlider = document.getElementById('intensitySlider'); const colorSlider = document.getElementById('colorSlider'); const soundToggle = document.getElementById('soundToggle'); const fireplace = document.querySelector('.fireplace-container'); let soundEnabled = false; let crackleTimeout = null; let audioContext = null; // Create and animate sparks function createSpark() { const spark = document.createElement('div'); spark.classList.add('spark'); // Random position within the fire const leftPos = 50 + (Math.random() * 100 - 50); spark.style.left = `${leftPos}px`; fireContainer.appendChild(spark); // Animate the spark const keyframes = [ { transform: `translate(0, 0) scale(1)`, opacity: 1 }, { transform: `translate(${Math.random() * 40 - 20}px, -${50 + Math.random() * 100}px) scale(0)`, opacity: 0 } ]; const animation = spark.animate(keyframes, { duration: 1000 + Math.random() * 1000, easing: 'ease-out' }); animation.onfinish = () => { spark.remove(); }; } // Create smoke particles function createSmoke() { const smoke = document.createElement('div'); smoke.classList.add('smoke-particle'); // Randomize size and position const size = 10 + Math.random() * 30; const leftPos = 60 + (Math.random() * 80 - 40); smoke.style.width = `${size}px`; smoke.style.height = `${size}px`; smoke.style.left = `${leftPos}px`; fireContainer.appendChild(smoke); // Animate the smoke const keyframes = [ { transform: 'translateY(0) scale(1)', opacity: 0 }, { transform: 'translateY(-20px) scale(1.2)', opacity: 0.3 }, { transform: 'translateY(-100px) scale(1.8)', opacity: 0 } ]; const animation = smoke.animate(keyframes, { duration: 3000 + Math.random() * 2000, easing: 'ease-out' }); animation.onfinish = () => { smoke.remove(); }; } // Generate wood crackling sound function createCrackleSound() { if (!audioContext) { audioContext = new (window.AudioContext || window.webkitAudioContext)(); } if (!soundEnabled || !audioContext) return; const oscillator = audioContext.createOscillator(); const gainNode = audioContext.createGain(); // Random frequency for varied crackle sounds oscillator.frequency.value = 100 + Math.random() * 1000; oscillator.type = Math.random() > 0.5 ? 'triangle' : 'square'; // Short duration for crack sound gainNode.gain.value = 0.05 * (intensitySlider.value / 10); gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.1); oscillator.connect(gainNode); gainNode.connect(audioContext.destination); oscillator.start(); oscillator.stop(audioContext.currentTime + 0.1); // Schedule next crackle const nextCrackle = Math.random() * 2000 + 500; // Between 0.5 and 2.5 seconds crackleTimeout = setTimeout(createCrackleSound, nextCrackle); } // Update flame appearance based on sliders function updateFlames() { const intensity = intensitySlider.value; const colorValue = colorSlider.value; // Adjust flame height based on intensity flames.forEach(flame => { const baseHeight = parseInt(flame.style.height || '140'); const newHeight = 100 + (intensity * 10); flame.style.height = `${newHeight}px`; // Adjust animation speed based on intensity const animDuration = 0.8 - (intensity * 0.05); flame.style.animationDuration = `${animDuration}s`; }); // Adjust flame color based on color slider const redValue = 255; const greenValue = 69 + (colorValue * 14); // 69 to 209 (oranges to yellows) const blueValue = colorValue > 7 ? (colorValue - 7) * 30 : 0; // Add blue for hotter flames const baseColor = `rgb(${redValue}, ${greenValue}, ${blueValue})`; const midColor = `rgb(${redValue}, ${Math.min(greenValue + 40, 255)}, ${Math.min(blueValue + 20, 255)})`; const topColor = `rgb(${redValue}, ${Math.min(greenValue + 80, 255)}, ${Math.min(blueValue + 40, 255)})`; flames.forEach(flame => { flame.style.background = `linear-gradient(to top, ${baseColor}, ${midColor}, ${topColor})`; }); // Adjust ember glow const ember = document.querySelector('.ember'); ember.style.opacity = 0.5 + (intensity * 0.05); // Adjust overall glow const glow = document.querySelector('.glow'); glow.style.opacity = 0.4 + (intensity * 0.06); glow.style.background = `radial-gradient(ellipse at center, rgba(${redValue}, ${greenValue}, ${blueValue}, 0.4), transparent 70%)`; } // Update flame appearance based on sliders intensitySlider.addEventListener('input', updateFlames); colorSlider.addEventListener('input', updateFlames); // Toggle sound on/off soundToggle.addEventListener('click', function() { soundEnabled = !soundEnabled; if (soundEnabled) { soundToggle.classList.add('active'); soundToggle.textContent = 'Turn Sound Off'; // Initialize AudioContext on first user interaction if (!audioContext) { audioContext = new (window.AudioContext || window.webkitAudioContext)(); } createCrackleSound(); } else { soundToggle.classList.remove('active'); soundToggle.textContent = 'Turn Sound On'; if (crackleTimeout) { clearTimeout(crackleTimeout); crackleTimeout = null; } } }); // Click/tap on fire to create extra sparks and crackle sounds fireplace.addEventListener('click', function(e) { // Don't trigger if clicking controls if (e.target.closest('.controls') || e.target.closest('.ambiance-info')) return; // Create a burst of sparks for (let i = 0; i < 10; i++) { setTimeout(() => createSpark(), i * 50); } // Create extra crackle sounds if sound is enabled if (soundEnabled && audioContext) { for (let i = 0; i < 5; i++) { setTimeout(() => { const oscillator = audioContext.createOscillator(); const gainNode = audioContext.createGain(); oscillator.frequency.value = 100 + Math.random() * 2000; oscillator.type = ['triangle', 'square', 'sawtooth'][Math.floor(Math.random() * 3)]; gainNode.gain.value = 0.1; gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.2); oscillator.connect(gainNode); gainNode.connect(audioContext.destination); oscillator.start(); oscillator.stop(audioContext.currentTime + 0.2); }, i * 100); } } }); // Set up intervals for continuous effects setInterval(createSpark, 300); // Create sparks regularly setInterval(createSmoke, 500); // Create smoke particles regularly // Initialize flames updateFlames(); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Pyromorphosis | Geometric Fire Installation</title> <style> :root { --dark: #0f0f12; --neon-red: #ff2b4a; --neon-orange: #ff7425; --neon-yellow: #ffde59; --neon-blue: #4bcffa; --neon-purple: #b851ec; } * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Space Mono', monospace; background-color: var(--dark); color: white; width: 100%; height: 100vh; overflow: hidden; display: flex; flex-direction: column; align-items: center; justify-content: center; } .container { width: 700px; height: 700px; position: relative; display: flex; flex-direction: column; padding: 20px; } header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; } h1 { font-size: 28px; font-weight: 700; letter-spacing: -1px; color: white; text-transform: uppercase; } .subtitle { font-size: 14px; opacity: 0.7; margin-top: 5px; font-style: italic; } .fire-container { width: 100%; height: 450px; background-color: rgba(20, 20, 25, 0.7); position: relative; margin: 10px 0 25px; overflow: hidden; border-radius: 4px; display: flex; align-items: center; justify-content: center; } .flame { position: absolute; transition: transform 0.5s ease-out, opacity 0.3s ease-in-out; } .shape { position: absolute; transform-origin: center; transition: all 0.8s cubic-bezier(0.34, 1.56, 0.64, 1); } .controls { display: flex; justify-content: space-between; align-items: center; width: 100%; margin-top: 10px; } .slider-container { width: 47%; } .slider-label { display: flex; justify-content: space-between; margin-bottom: 5px; font-size: 12px; text-transform: uppercase; } input[type="range"] { width: 100%; height: 6px; -webkit-appearance: none; background: rgba(255, 255, 255, 0.1); border-radius: 3px; outline: none; } input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; width: 15px; height: 15px; border-radius: 50%; background: var(--neon-blue); cursor: pointer; transition: all 0.3s ease; } input[type="range"]::-webkit-slider-thumb:hover { background: var(--neon-yellow); transform: scale(1.2); } .button-container { display: flex; justify-content: space-between; margin-top: 20px; gap: 10px; } button { padding: 10px 20px; border: none; border-radius: 3px; font-family: 'Space Mono', monospace; font-weight: bold; cursor: pointer; text-transform: uppercase; font-size: 12px; letter-spacing: 1px; transition: all 0.3s ease; background-color: rgba(255, 255, 255, 0.07); color: white; flex: 1; } button:hover { background-color: var(--neon-blue); color: var(--dark); transform: translateY(-2px); } .disassemble-btn { background-color: rgba(255, 43, 74, 0.2); } .disassemble-btn:hover { background-color: var(--neon-red); } .reassemble-btn { background-color: rgba(255, 116, 37, 0.2); } .reassemble-btn:hover { background-color: var(--neon-orange); } .refresh-btn { background-color: rgba(184, 81, 236, 0.2); } .refresh-btn:hover { background-color: var(--neon-purple); } .instructions { font-size: 14px; opacity: 0.7; text-align: center; margin-top: 15px; line-height: 1.4; } .info-panel { position: absolute; bottom: 20px; left: 20px; font-size: 12px; opacity: 0.5; } @media (max-width: 700px) { .container { width: 100%; height: auto; padding: 15px; } h1 { font-size: 20px; } .fire-container { height: 350px; } .controls { flex-direction: column; gap: 15px; } .slider-container { width: 100%; } .button-container { flex-direction: column; } } .glow { position: absolute; width: 0; height: 0; border-radius: 50%; background: radial-gradient(circle, var(--neon-orange) 0%, rgba(255, 116, 37, 0) 70%); filter: blur(15px); opacity: 0.5; z-index: -1; transition: all 1s ease; } .version-info { position: absolute; top: 20px; right: 20px; font-size: 10px; opacity: 0.3; } </style> </head> <body> <div class="container"> <header> <div> <h1>Pyromorphosis</h1> <div class="subtitle">Deconstruction of elemental forces</div> </div> </header> <div class="fire-container" id="fireCanvas"> <div class="glow" id="glow"></div> </div> <div class="controls"> <div class="slider-container"> <div class="slider-label"> <span>Fragmentation</span> <span id="fragmentationValue">5</span> </div> <input type="range" id="fragmentationSlider" min="3" max="15" value="5"> </div> <div class="slider-container"> <div class="slider-label"> <span>Speed</span> <span id="speedValue">2</span> </div> <input type="range" id="speedSlider" min="1" max="5" value="2"> </div> </div> <div class="button-container"> <button class="disassemble-btn" id="disassembleBtn">Disassemble</button> <button class="reassemble-btn" id="reassembleBtn">Reassemble</button> <button class="refresh-btn" id="refreshBtn">New Form</button> </div> <p class="instructions"> Manipulate the geometric flames using the controls. <br> Watch as the rigid forms of geometry yield to chaos. </p> <div class="version-info">v1.07</div> </div> <script> document.addEventListener('DOMContentLoaded', function() { const fireCanvas = document.getElementById('fireCanvas'); const fragmentationSlider = document.getElementById('fragmentationSlider'); const fragmentationValue = document.getElementById('fragmentationValue'); const speedSlider = document.getElementById('speedSlider'); const speedValue = document.getElementById('speedValue'); const disassembleBtn = document.getElementById('disassembleBtn'); const reassembleBtn = document.getElementById('reassembleBtn'); const refreshBtn = document.getElementById('refreshBtn'); const glow = document.getElementById('glow'); let flames = []; let isDisassembled = false; let canvasWidth = fireCanvas.offsetWidth; let canvasHeight = fireCanvas.offsetHeight; const colors = [ 'var(--neon-red)', 'var(--neon-orange)', 'var(--neon-yellow)', 'var(--neon-blue)', 'var(--neon-purple)' ]; // Initialize the glow effect glow.style.width = '250px'; glow.style.height = '250px'; glow.style.left = (canvasWidth / 2 - 125) + 'px'; glow.style.top = (canvasHeight / 2 - 125) + 'px'; // Create the flame shapes function createFlames() { clearFlames(); const fragmentation = parseInt(fragmentationSlider.value); const centerX = canvasWidth / 2; const centerY = canvasHeight / 2; // Create the flame container const flameElement = document.createElement('div'); flameElement.className = 'flame'; flameElement.style.width = '200px'; flameElement.style.height = '300px'; flameElement.style.left = (centerX - 100) + 'px'; flameElement.style.top = (centerY - 150) + 'px'; fireCanvas.appendChild(flameElement); flames.push(flameElement); // Create geometric shapes for the flame for (let i = 0; i < fragmentation * 3; i++) { const shape = document.createElement('div'); shape.className = 'shape'; // Randomize shape types const shapeType = Math.floor(Math.random() * 3); let width, height, borderRadius, rotation; if (shapeType === 0) { // Triangle width = 20 + Math.random() * 60; height = width; shape.style.width = width + 'px'; shape.style.height = height + 'px'; shape.style.backgroundColor = 'transparent'; shape.style.borderLeft = (width / 2) + 'px solid transparent'; shape.style.borderRight = (width / 2) + 'px solid transparent'; shape.style.borderBottom = height + 'px solid ' + colors[Math.floor(Math.random() * colors.length)]; } else if (shapeType === 1) { // Rectangle width = 10 + Math.random() * 40; height = 20 + Math.random() * 60; shape.style.width = width + 'px'; shape.style.height = height + 'px'; shape.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)]; } else { // Circle/ellipse width = 10 + Math.random() * 30; height = 10 + Math.random() * 30; shape.style.width = width + 'px'; shape.style.height = height + 'px'; shape.style.borderRadius = '50%'; shape.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)]; } // Position within the flame container const posX = -20 + Math.random() * 180; const posY = Math.random() * 280; shape.style.left = posX + 'px'; shape.style.top = posY + 'px'; // Rotation and opacity rotation = Math.random() * 360; shape.style.transform = `rotate(${rotation}deg)`; shape.style.opacity = 0.5 + Math.random() * 0.5; // Add animation shape.style.animation = `float ${2 + Math.random() * 3}s infinite ease-in-out alternate`; // Store original position for disassembly/reassembly shape.dataset.originalX = posX; shape.dataset.originalY = posY; shape.dataset.originalRotation = rotation; flameElement.appendChild(shape); } // Start animation animateFlames(); } // Clear existing flames function clearFlames() { flames.forEach(flame => { fireCanvas.removeChild(flame); }); flames = []; } // Animate flames function animateFlames() { const speed = parseInt(speedSlider.value); flames.forEach(flame => { const shapes = flame.querySelectorAll('.shape'); shapes.forEach(shape => { // Create dynamic animation shape.animate([ { transform: `translate(0, ${2 * speed}px) rotate(${shape.dataset.originalRotation}deg)` }, { transform: `translate(${Math.random() * 10 - 5}px, ${-2 * speed}px) rotate(${parseInt(shape.dataset.originalRotation) + 5}deg)` } ], { duration: 2000 / speed, iterations: Infinity, direction: 'alternate', easing: 'ease-in-out' }); }); }); // Animate the glow animateGlow(); } // Animate the glow effect function animateGlow() { const speed = parseInt(speedSlider.value); setInterval(() => { const size = 200 + Math.random() * 100; glow.style.width = size + 'px'; glow.style.height = size + 'px'; glow.style.left = (canvasWidth / 2 - size / 2) + 'px'; glow.style.top = (canvasHeight / 2 - size / 2) + 'px'; // Switch between colors const colorIndex = Math.floor(Math.random() * colors.length); glow.style.background = `radial-gradient(circle, ${colors[colorIndex]} 0%, rgba(255, 116, 37, 0) 70%)`; glow.style.opacity = 0.3 + Math.random() * 0.3; }, 2000 / speed); } // Disassemble the flame into geometric components function disassembleFlame() { if (isDisassembled) return; flames.forEach(flame => { const shapes = flame.querySelectorAll('.shape'); shapes.forEach(shape => { // Stop existing animations shape.getAnimations().forEach(anim => anim.cancel()); // Explode outward const angle = Math.random() * Math.PI * 2; const distance = 100 + Math.random() * 200; const targetX = Math.cos(angle) * distance; const targetY = Math.sin(angle) * distance; shape.style.transform = `translate(${targetX}px, ${targetY}px) rotate(${Math.random() * 360}deg)`; shape.style.opacity = 0.2 + Math.random() * 0.4; }); }); isDisassembled = true; // Update glow glow.style.width = '400px'; glow.style.height = '400px'; glow.style.left = (canvasWidth / 2 - 200) + 'px'; glow.style.top = (canvasHeight / 2 - 200) + 'px'; glow.style.opacity = '0.2'; } // Reassemble the geometric components back into flame function reassembleFlame() { if (!isDisassembled) return; flames.forEach(flame => { const shapes = flame.querySelectorAll('.shape'); shapes.forEach(shape => { // Return to original position const originalX = parseFloat(shape.dataset.originalX); const originalY = parseFloat(shape.dataset.originalY); const originalRotation = shape.dataset.originalRotation; shape.style.transform = `translate(${originalX}px, ${originalY}px) rotate(${originalRotation}deg)`; shape.style.opacity = 0.5 + Math.random() * 0.5; }); }); isDisassembled = false; // Start animation again setTimeout(animateFlames, 800); // Update glow glow.style.width = '250px'; glow.style.height = '250px'; glow.style.left = (canvasWidth / 2 - 125) + 'px'; glow.style.top = (canvasHeight / 2 - 125) + 'px'; glow.style.opacity = '0.5'; } // Event Listeners fragmentationSlider.addEventListener('input', function() { fragmentationValue.textContent = this.value; createFlames(); }); speedSlider.addEventListener('input', function() { speedValue.textContent = this.value; // Cancel existing animations and restart flames.forEach(flame => { const shapes = flame.querySelectorAll('.shape'); shapes.forEach(shape => { shape.getAnimations().forEach(anim => anim.cancel()); }); }); animateFlames(); }); disassembleBtn.addEventListener('click', disassembleFlame); reassembleBtn.addEventListener('click', reassembleFlame); refreshBtn.addEventListener('click', createFlames); // Handle window resize window.addEventListener('resize', function() { canvasWidth = fireCanvas.offsetWidth; canvasHeight = fireCanvas.offsetHeight; createFlames(); }); // Interactive hover effect on the canvas fireCanvas.addEventListener('mousemove', function(e) { const rect = fireCanvas.getBoundingClientRect(); const mouseX = e.clientX - rect.left; const mouseY = e.clientY - rect.top; if (!isDisassembled) { flames.forEach(flame => { const shapes = flame.querySelectorAll('.shape'); shapes.forEach(shape => { const shapeRect = shape.getBoundingClientRect(); const shapeX = shapeRect.left + shapeRect.width / 2 - rect.left; const shapeY = shapeRect.top + shapeRect.height / 2 - rect.top; const distance = Math.sqrt(Math.pow(mouseX - shapeX, 2) + Math.pow(mouseY - shapeY, 2)); if (distance < 50) { // Repel the shape away from the cursor const angle = Math.atan2(shapeY - mouseY, shapeX - mouseX); const pushX = Math.cos(angle) * (50 - distance) * 0.2; const pushY = Math.sin(angle) * (50 - distance) * 0.2; const originalX = parseFloat(shape.dataset.originalX); const originalY = parseFloat(shape.dataset.originalY); const originalRotation = parseFloat(shape.dataset.originalRotation); shape.style.transform = `translate(${originalX + pushX}px, ${originalY + pushY}px) rotate(${originalRotation}deg)`; } }); }); } }); // Initialize createFlames(); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>BLAZE IT UP 2024 | Extreme Sports Championship</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } body { background-color: #121212; overflow-x: hidden; width: 100%; height: 100vh; color: white; } .container { width: 100%; max-width: 700px; height: 700px; margin: 0 auto; position: relative; overflow: hidden; } .hero { position: relative; height: 100%; display: flex; flex-direction: column; justify-content: flex-start; perspective: 800px; } .header { position: relative; padding: 20px; z-index: 10; } .logo { font-size: 42px; font-weight: 900; letter-spacing: 2px; color: #FF5722; text-shadow: 0 0 15px rgba(255, 87, 34, 0.7); position: relative; transform: skew(-5deg); display: inline-block; } .tagline { font-size: 18px; font-weight: 600; margin-top: 5px; color: #FFC107; text-transform: uppercase; letter-spacing: 3px; } .main-content { position: relative; flex: 1; display: flex; flex-direction: column; justify-content: space-between; padding: 0 20px; z-index: 5; } .event-info { margin-top: 20px; padding: 25px; background: rgba(0, 0, 0, 0.6); backdrop-filter: blur(5px); border-radius: 10px; border-left: 4px solid #FF5722; transform: translateX(-100%); opacity: 0; animation: slideIn 0.8s forwards 0.5s; } .date { font-size: 24px; font-weight: 700; margin-bottom: 10px; color: #FFC107; } .location { font-size: 18px; margin-bottom: 20px; } .event-description { font-size: 16px; line-height: 1.6; margin-bottom: 20px; } .highlights { display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; margin-top: 30px; opacity: 0; transform: translateY(20px); animation: fadeUp 0.8s forwards 1s; } .highlight-card { padding: 15px; background: rgba(0, 0, 0, 0.7); border-radius: 8px; text-align: center; cursor: pointer; transition: all 0.3s ease; position: relative; overflow: hidden; border: 1px solid rgba(255, 87, 34, 0.3); } .highlight-card:hover { transform: translateY(-5px); box-shadow: 0 10px 20px rgba(255, 87, 34, 0.3); border-color: #FF5722; } .highlight-card h3 { font-size: 16px; margin-bottom: 8px; color: #FF5722; position: relative; z-index: 2; } .highlight-card p { font-size: 13px; color: #ccc; position: relative; z-index: 2; } .cta { margin-top: 40px; margin-bottom: 20px; text-align: center; transform: translateY(20px); opacity: 0; animation: fadeUp 0.8s forwards 1.5s; } .cta-button { display: inline-block; padding: 15px 35px; background: linear-gradient(135deg, #FF5722, #FF9800); color: white; font-size: 18px; font-weight: 700; text-transform: uppercase; letter-spacing: 2px; border: none; border-radius: 50px; cursor: pointer; position: relative; overflow: hidden; transition: all 0.3s ease; box-shadow: 0 5px 20px rgba(255, 87, 34, 0.5); outline: none; } .cta-button:hover { transform: translateY(-3px); box-shadow: 0 10px 25px rgba(255, 87, 34, 0.6); } .cta-button:active { transform: translateY(0); } /* Fire and animation effects */ .fire-container { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 1; overflow: hidden; } .fire-particle { position: absolute; bottom: -50px; background: radial-gradient(ellipse at center, rgba(255, 121, 0, 0.8) 0%, rgba(255, 87, 34, 0.5) 40%, rgba(255, 0, 0, 0) 70%); border-radius: 50%; transform-origin: center bottom; opacity: 0; pointer-events: none; } .glow { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: radial-gradient(ellipse at center, rgba(255, 87, 34, 0.1) 0%, rgba(0, 0, 0, 0) 70%); opacity: 0.5; z-index: 2; pointer-events: none; animation: glowPulse 4s infinite alternate; } .bg-texture { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-image: linear-gradient(to bottom right, rgba(50, 50, 50, 0.1) 25%, transparent 25%, transparent 50%, rgba(50, 50, 50, 0.1) 50%, rgba(50, 50, 50, 0.1) 75%, transparent 75%, transparent); background-size: 5px 5px; opacity: 0.2; z-index: 1; pointer-events: none; } .flame-highlight { position: absolute; bottom: 0; left: 50%; transform: translateX(-50%); width: 400px; height: 200px; background: radial-gradient(ellipse at center, rgba(255, 87, 34, 0.3) 0%, rgba(0, 0, 0, 0) 70%); z-index: 1; pointer-events: none; } /* Sparks for highlight cards */ .spark { position: absolute; background-color: #FFC107; width: 3px; height: 3px; border-radius: 50%; filter: blur(1px); opacity: 0; pointer-events: none; z-index: 1; } /* Athletes silhouettes */ .athletes { position: absolute; bottom: 0; right: 0; width: 300px; height: 300px; z-index: 3; opacity: 0.8; } .athlete { position: absolute; background-color: #000; bottom: 0; border-radius: 3px; } .athlete-1 { height: 180px; width: 50px; right: 30px; bottom: 0; transform: skew(-15deg); box-shadow: -10px 0 15px rgba(255, 87, 34, 0.5); } .athlete-2 { height: 200px; width: 45px; right: 100px; bottom: 0; transform: skew(10deg); box-shadow: 10px 0 15px rgba(255, 87, 34, 0.5); } .athlete-3 { height: 160px; width: 55px; right: 160px; bottom: 0; transform: skew(-5deg); box-shadow: -5px 0 15px rgba(255, 87, 34, 0.5); } @keyframes slideIn { to { transform: translateX(0); opacity: 1; } } @keyframes fadeUp { to { transform: translateY(0); opacity: 1; } } @keyframes glowPulse { 0% { opacity: 0.3; } 50% { opacity: 0.6; } 100% { opacity: 0.4; } } @media (max-width: 600px) { .logo { font-size: 32px; } .tagline { font-size: 14px; } .highlights { grid-template-columns: 1fr; } .event-info { padding: 15px; } .date { font-size: 20px; } .location { font-size: 16px; } .cta-button { padding: 12px 25px; font-size: 16px; } .athlete-1, .athlete-2, .athlete-3 { transform: scale(0.8); } } </style> </head> <body> <div class="container"> <div class="hero"> <div class="bg-texture"></div> <div class="glow"></div> <div class="flame-highlight"></div> <div class="header"> <h1 class="logo">BLAZE IT UP</h1> <p class="tagline">2024 Extreme Sports Championship</p> </div> <div class="main-content"> <div class="event-info"> <div class="date">AUGUST 15-18, 2024</div> <div class="location">Phoenix Arena | Arizona, USA</div> <div class="event-description"> The ultimate extreme sports showdown with Olympic medalists and X-Games champions competing in skateboarding, BMX, and motocross events. 72 hours of non-stop adrenaline. </div> </div> <div class="highlights"> <div class="highlight-card" data-type="skateboard"> <h3>Street Skateboarding</h3> <p>World's top 20 skaters including Nyjah Huston & Leticia Bufoni</p> </div> <div class="highlight-card" data-type="bmx"> <h3>BMX Mega Ramp</h3> <p>50ft drops with Daniel Dhers defending his title</p> </div> <div class="highlight-card" data-type="motocross"> <h3>FMX Best Trick</h3> <p>Featuring Jackson Strong's new double backflip variations</p> </div> </div> <div class="cta"> <button class="cta-button">GET TICKETS</button> </div> </div> <div class="fire-container"></div> <div class="athletes"> <div class="athlete athlete-1"></div> <div class="athlete athlete-2"></div> <div class="athlete athlete-3"></div> </div> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { // Fire animation const fireContainer = document.querySelector('.fire-container'); let scrollIntensity = 0; let lastScrollTop = 0; let isScrolling = false; // Create initial fire particles createFireParticles(15); function createFireParticles(count) { for (let i = 0; i < count; i++) { createFireParticle(); } } function createFireParticle() { const particle = document.createElement('div'); particle.classList.add('fire-particle'); // Random size between 20 and 100 pixels const size = 20 + Math.random() * 80; particle.style.width = `${size}px`; particle.style.height = `${size}px`; // Random position, but more centered for larger particles const leftPosition = size > 60 ? 20 + Math.random() * 60 : Math.random() * 100; particle.style.left = `${leftPosition}%`; // Random animation duration const duration = 1.5 + Math.random() * 2; // Random delay const delay = Math.random() * 2; // Set animation particle.style.animation = ` fireRise ${duration}s ease-out ${delay}s infinite, fadeInOut ${duration}s ease-in-out ${delay}s infinite `; // Add to container fireContainer.appendChild(particle); // Remove after some time to prevent too many elements setTimeout(() => { if (particle.parentNode === fireContainer) { fireContainer.removeChild(particle); createFireParticle(); // Create a new one to replace } }, (duration + delay) * 1000 * 2); } // Create keyframes dynamically const style = document.createElement('style'); style.textContent = ` @keyframes fireRise { 0% { transform: translateY(0) scale(0.5) rotate(0deg); bottom: -10px; } 50% { transform: translateY(-150px) scale(1) rotate(5deg); } 100% { transform: translateY(-300px) scale(0.5) rotate(10deg); bottom: 100%; } } @keyframes fadeInOut { 0% { opacity: 0; } 20% { opacity: 0.8; } 80% { opacity: 0.3; } 100% { opacity: 0; } } `; document.head.appendChild(style); // Hover effects for highlight cards const highlightCards = document.querySelectorAll('.highlight-card'); highlightCards.forEach(card => { card.addEventListener('mouseenter', function() { // Create sparks animation createSparks(this); // Intensify fire animation createFireParticles(5); }); }); function createSparks(element) { const rect = element.getBoundingClientRect(); const numSparks = 10; for (let i = 0; i < numSparks; i++) { const spark = document.createElement('div'); spark.classList.add('spark'); // Position at the bottom of the card spark.style.left = `${Math.random() * rect.width}px`; spark.style.bottom = '0px'; // Animation const duration = 0.5 + Math.random() * 1; const delay = Math.random() * 0.5; spark.style.animation = ` sparkRise ${duration}s ease-out ${delay}s forwards `; element.appendChild(spark); // Remove after animation setTimeout(() => { if (spark.parentNode === element) { element.removeChild(spark); } }, (duration + delay) * 1000 + 100); } } // Add keyframes for sparks const sparkStyle = document.createElement('style'); sparkStyle.textContent = ` @keyframes sparkRise { 0% { transform: translateY(0) translateX(0); opacity: 1; } 100% { transform: translateY(-${20 + Math.random() * 30}px) translateX(${-10 + Math.random() * 20}px); opacity: 0; } } `; document.head.appendChild(sparkStyle); // CTA Button effects const ctaButton = document.querySelector('.cta-button'); ctaButton.addEventListener('mouseenter', function() { this.style.background = 'linear-gradient(135deg, #FF9800, #FF5722)'; createFireParticles(10); }); ctaButton.addEventListener('mouseleave', function() { this.style.background = 'linear-gradient(135deg, #FF5722, #FF9800)'; }); ctaButton.addEventListener('click', function(e) { e.preventDefault(); // Create a ripple effect const ripple = document.createElement('div'); ripple.style.position = 'absolute'; ripple.style.width = '10px'; ripple.style.height = '10px'; ripple.style.background = 'rgba(255, 255, 255, 0.7)'; ripple.style.borderRadius = '50%'; ripple.style.transform = 'scale(0)'; ripple.style.pointerEvents = 'none'; // Position at click point const rect = this.getBoundingClientRect(); ripple.style.left = `${e.clientX - rect.left}px`; ripple.style.top = `${e.clientY - rect.top}px`; // Add to button this.appendChild(ripple); // Animate ripple.animate( [ { transform: 'scale(0)', opacity: 1 }, { transform: 'scale(40)', opacity: 0 } ], { duration: 800, easing: 'ease-out' } ); // Create intense fire effect createFireParticles(30); // Remove ripple after animation setTimeout(() => { if (ripple.parentNode === this) { this.removeChild(ripple); } }, 800); }); // Scroll effect to intensify flames window.addEventListener('scroll', function() { const scrollTop = window.pageYOffset || document.documentElement.scrollTop; const scrollDelta = Math.abs(scrollTop - lastScrollTop); lastScrollTop = scrollTop; if (scrollDelta > 5) { scrollIntensity = Math.min(scrollIntensity + 1, 10); if (!isScrolling) { isScrolling = true; intensifyFire(); } } // Reset the timeout on every scroll event clearTimeout(window.scrollTimeout); window.scrollTimeout = setTimeout(function() { scrollIntensity = 0; isScrolling = false; }, 200); }); function intensifyFire() { if (scrollIntensity > 0 && isScrolling) { const particles = Math.floor(scrollIntensity / 2) + 1; createFireParticles(particles); setTimeout(intensifyFire, 100); } } // Make athlete silhouettes more dynamic const athletes = document.querySelectorAll('.athlete'); setInterval(() => { athletes.forEach(athlete => { // Small random movement const randomX = (Math.random() - 0.5) * 2; const randomSkew = (Math.random() - 0.5) * 2; athlete.style.transform = `translateX(${randomX}px) skew(${parseFloat(athlete.style.transform.match(/skew\(([^)]+)\)/)?.[1] || '0deg') + randomSkew}deg)`; }); }, 500); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Ember - Interactive Storytelling Through Fire</title> <style> @import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@300;400;600&family=Playfair+Display:wght@400;700&display=swap'); * { margin: 0; padding: 0; box-sizing: border-box; } :root { --ember-red: #ff3d00; --ember-orange: #ff8a00; --ember-yellow: #ffca28; --ember-blue: #0288d1; --light-text: #fff8f0; --dark-text: #1a1a1a; --bg-color: #0f0f13; } body { background-color: var(--bg-color); color: var(--light-text); font-family: 'Cormorant Garamond', serif; height: 100vh; width: 100%; overflow: hidden; display: flex; justify-content: center; align-items: center; position: relative; } .container { width: 100%; max-width: 700px; height: 700px; display: flex; flex-direction: column; position: relative; overflow: hidden; } .fire-container { position: absolute; width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; pointer-events: none; } .fire { width: 300px; height: 300px; background: radial-gradient(circle, rgba(255, 61, 0, 0.3) 0%, rgba(15, 15, 19, 0) 70%); position: absolute; bottom: 0; transform: translateY(50%); z-index: 1; filter: blur(5px); transition: all 1.5s ease-in-out; } .flames { position: absolute; bottom: 0; left: 50%; transform: translateX(-50%); width: 100px; height: 100px; z-index: 2; display: flex; justify-content: center; transition: all 1.5s ease-in-out; } .flame { position: absolute; bottom: 0; width: 15px; height: 15px; background: var(--ember-red); border-radius: 50% 0 50% 50%; transform: rotate(45deg) scale(2.5); box-shadow: 0 0 10px 5px rgba(255, 61, 0, 0.3), 0 0 20px 10px rgba(255, 61, 0, 0.1); animation: flicker 1.5s infinite alternate; filter: blur(1px); opacity: 0.9; transform-origin: center bottom; } .flame:nth-child(2) { left: -5px; height: 12px; background: var(--ember-orange); animation-delay: 0.2s; animation-duration: 1.7s; } .flame:nth-child(3) { left: 5px; height: 14px; background: var(--ember-yellow); animation-delay: 0.4s; animation-duration: 1.9s; } .flame:nth-child(4) { left: -10px; height: 10px; background: var(--ember-orange); animation-delay: 0.6s; animation-duration: 1.3s; } .flame:nth-child(5) { left: 10px; height: 11px; background: var(--ember-red); animation-delay: 0.8s; animation-duration: 1.6s; } @keyframes flicker { 0%, 100% { transform: rotate(45deg) scale(2.5); opacity: 0.9; } 25% { transform: rotate(40deg) scale(2.3) translateY(-2px); opacity: 0.8; } 50% { transform: rotate(50deg) scale(2.6) translateY(-1px); opacity: 1; } 75% { transform: rotate(45deg) scale(2.4) translateY(-3px); opacity: 0.85; } } .ember { position: absolute; width: 2px; height: 2px; background: var(--ember-yellow); border-radius: 50%; bottom: 80px; opacity: 0; filter: blur(1px); box-shadow: 0 0 3px 1px rgba(255, 202, 40, 0.6); } .content { position: relative; width: 100%; height: 100%; z-index: 3; display: flex; flex-direction: column; padding: 2rem; transition: all 0.8s ease; } header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 2rem; } .logo { font-family: 'Playfair Display', serif; font-size: 2rem; font-weight: 700; letter-spacing: 1px; background: linear-gradient(45deg, var(--ember-red), var(--ember-yellow)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; position: relative; } .logo::after { content: ""; position: absolute; left: 0; bottom: -5px; width: 100%; height: 1px; background: linear-gradient(90deg, transparent, var(--ember-orange), transparent); } .controls { display: flex; gap: 1rem; } .sound-toggle { background: none; border: 1px solid rgba(255, 255, 255, 0.2); color: var(--light-text); width: 40px; height: 40px; border-radius: 50%; display: flex; justify-content: center; align-items: center; cursor: pointer; transition: all 0.3s ease; } .sound-toggle:hover { background: rgba(255, 255, 255, 0.1); transform: scale(1.05); } .sound-toggle i { font-size: 1rem; } main { flex-grow: 1; display: flex; flex-direction: column; justify-content: center; padding: 1rem 0; max-width: 600px; margin: 0 auto; position: relative; } .story-title { font-family: 'Playfair Display', serif; font-size: 2.5rem; font-weight: 700; margin-bottom: 1.5rem; text-align: center; line-height: 1.2; opacity: 1; transform: translateY(0); transition: opacity 0.8s ease, transform 0.8s ease; } .story-text { font-size: 1.25rem; line-height: 1.6; max-height: 320px; overflow-y: auto; padding-right: 10px; margin-bottom: 2rem; position: relative; opacity: 1; transform: translateY(0); transition: opacity 0.8s ease, transform 0.8s ease; } .story-text::-webkit-scrollbar { width: 4px; } .story-text::-webkit-scrollbar-track { background: rgba(255, 255, 255, 0.05); border-radius: 10px; } .story-text::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.2); border-radius: 10px; } .story-text p { margin-bottom: 1rem; } .choice-container { display: flex; flex-direction: column; gap: 0.8rem; margin-top: auto; } .choice { background: rgba(255, 255, 255, 0.08); border: 1px solid rgba(255, 255, 255, 0.1); color: var(--light-text); padding: 1rem 1.5rem; border-radius: 8px; cursor: pointer; font-family: 'Cormorant Garamond', serif; font-size: 1.1rem; transition: all 0.3s ease; text-align: left; position: relative; overflow: hidden; } .choice:hover { background: rgba(255, 255, 255, 0.15); transform: translateY(-2px); } .choice:active { transform: translateY(0); } .choice::before { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: linear-gradient(90deg, rgba(255, 61, 0, 0), rgba(255, 61, 0, 0.1), rgba(255, 61, 0, 0)); transform: translateX(-100%); transition: transform 0.5s ease; } .choice:hover::before { transform: translateX(100%); } footer { font-size: 0.9rem; text-align: center; opacity: 0.6; padding: 1rem 0; } .hidden { opacity: 0; transform: translateY(20px); } /* Fire intensity states */ .calm-flame .fire { background: radial-gradient(circle, rgba(2, 136, 209, 0.2) 0%, rgba(15, 15, 19, 0) 70%); } .calm-flame .flame { background: var(--ember-blue); box-shadow: 0 0 10px 5px rgba(2, 136, 209, 0.3), 0 0 20px 10px rgba(2, 136, 209, 0.1); animation-duration: 2.5s; } .calm-flame .flame:nth-child(2) { background: var(--ember-blue); animation-duration: 2.7s; } .calm-flame .flame:nth-child(3) { background: var(--ember-blue); animation-duration: 2.9s; } .calm-flame .ember { background: var(--ember-blue); box-shadow: 0 0 3px 1px rgba(2, 136, 209, 0.6); } .intense-flame .fire { background: radial-gradient(circle, rgba(255, 61, 0, 0.5) 0%, rgba(15, 15, 19, 0) 70%); transform: translateY(40%) scale(1.2); } .intense-flame .flames { transform: translateX(-50%) scale(1.3); } .intense-flame .flame { animation-duration: 0.8s; } .intense-flame .flame:nth-child(2) { animation-duration: 1s; } .intense-flame .flame:nth-child(3) { animation-duration: 1.2s; } /* Responsive styles */ @media (max-width: 700px) { .container { height: 650px; } .story-title { font-size: 2rem; } .story-text { font-size: 1.1rem; max-height: 250px; } .choice { padding: 0.8rem 1.2rem; font-size: 1rem; } } @media (max-width: 500px) { .content { padding: 1.5rem; } .story-title { font-size: 1.8rem; margin-bottom: 1rem; } .story-text { font-size: 1rem; max-height: 200px; margin-bottom: 1.5rem; } .logo { font-size: 1.5rem; } } @media (max-height: 700px) { .container { height: 600px; } .story-text { max-height: 180px; } } </style> </head> <body> <div class="container"> <div class="fire-container"> <div class="fire"></div> <div class="flames"> <div class="flame"></div> <div class="flame"></div> <div class="flame"></div> <div class="flame"></div> <div class="flame"></div> </div> </div> <div class="content"> <header> <div class="logo">Ember</div> <div class="controls"> <button class="sound-toggle" id="soundToggle"> <i class="fas fa-volume-up">♪</i> </button> </div> </header> <main> <h1 class="story-title">The Last Light of Ashenvale</h1> <div class="story-text"> <p>The ancient forest of Ashenvale stood silent, shrouded in mist. Elara clutched the last ember of the ancestral flame, its warmth fading against her palm. She knew that when this flame died, so too would the magic that had protected her people for centuries.</p> <p>The Elder's prophecy rang in her ears: <em>"Only when faced with absolute darkness will the true nature of the flame reveal itself."</em></p> <p>A distant howl pierced the silence. The Shadow Wolves were drawing near, tracking the ember's dwindling light. Elara had mere moments to decide her course.</p> </div> <div class="choice-container"> <button class="choice" data-mood="calm"> Conceal the ember and seek refuge in the hidden grove </button> <button class="choice" data-mood="intense"> Channel your spirit into the ember, feeding it with your own energy </button> <button class="choice" data-mood="neutral"> Cast the ember into the sacred pool, as the ancient texts advised </button> </div> </main> <footer> Navigate through the narrative by choosing your path. Each choice alters the flame and the story's direction. </footer> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { // Story elements const storyTitle = document.querySelector('.story-title'); const storyText = document.querySelector('.story-text'); const choiceContainer = document.querySelector('.choice-container'); const choices = document.querySelectorAll('.choice'); const fireContainer = document.querySelector('.fire-container'); const soundToggle = document.getElementById('soundToggle'); // State variables let soundEnabled = false; let currentStoryIndex = 0; // Story data const storySegments = [ { title: "The Last Light of Ashenvale", text: "<p>The ancient forest of Ashenvale stood silent, shrouded in mist. Elara clutched the last ember of the ancestral flame, its warmth fading against her palm. She knew that when this flame died, so too would the magic that had protected her people for centuries.</p><p>The Elder's prophecy rang in her ears: <em>\"Only when faced with absolute darkness will the true nature of the flame reveal itself.\"</em></p><p>A distant howl pierced the silence. The Shadow Wolves were drawing near, tracking the ember's dwindling light. Elara had mere moments to decide her course.</p>", choices: [ { text: "Conceal the ember and seek refuge in the hidden grove", mood: "calm", next: 1 }, { text: "Channel your spirit into the ember, feeding it with your own energy", mood: "intense", next: 2 }, { text: "Cast the ember into the sacred pool, as the ancient texts advised", mood: "neutral", next: 3 } ] }, { title: "Sanctuary of Whispers", text: "<p>The concealed ember pulsed gently between Elara's fingers as she navigated the twisting path to the hidden grove. Ancient trees loomed overhead, their branches forming a protective canopy that seemed to bend light around her.</p><p>As she entered the clearing, the mist parted to reveal a circle of blue luminescent mushrooms. The air here felt different—charged with an old magic that made her skin tingle.</p><p>\"We've been expecting you, Flame Bearer,\" came a whisper from nowhere and everywhere. The ember in her palm turned a tranquil blue, resonating with the energies of the grove.</p>", choices: [ { text: "Place the ember upon the central stone altar", mood: "calm", next: 4 }, { text: "Ask the voices to reveal themselves", mood: "neutral", next: 5 }, { text: "Reach out with your mind to connect with the grove's consciousness", mood: "intense", next: 6 } ] }, { title: "The Burning Within", text: "<p>Elara closed her eyes and reached deep within herself, connecting to the ancestral memories coded in her bloodline. The ember responded immediately, drinking greedily from her life force.</p><p>Pain seared through her as brilliant orange flames engulfed her hand, forearm, and then her entire body. But it wasn't consuming her—she was becoming one with it.</p><p>\"The flame tests its carrier,\" she remembered the Elder saying. \"It must know if you can bear its full power.\"</p><p>Through the veil of fire, she could see the Shadow Wolves retreating, their eyes reflecting terror at the transformation occurring before them.</p>", choices: [ { text: "Surrender completely to the flame's will", mood: "intense", next: 7 }, { text: "Assert dominance over the flame, binding it to your command", mood: "intense", next: 8 }, { text: "Find balance, forming a partnership with the ancient power", mood: "neutral", next: 9 } ] }, { title: "Depths of Revelation", text: "<p>The sacred pool lay still as glass, reflecting the clouded sky above. Elara knelt at its edge, the ember hovering just above the water's surface. Ancient glyphs carved into the stone around the pool began to glow faintly.</p><p>\"May the waters return you to your source,\" she whispered, reciting the ritual words.</p><p>As the ember touched the water, it didn't extinguish as she had feared. Instead, it sank slowly, trailing a spiral of light that illuminated the surprising depth of the pool. Beneath the surface, something massive and ancient stirred.</p><p>The water began to bubble, not with heat, but with energy that sent ripples of golden light across the surface.</p>", choices: [ { text: "Step back and observe what emerges", mood: "neutral", next: 10 }, { text: "Wade into the pool, following the ember's descent", mood: "calm", next: 11 }, { text: "Call out to the ancient presence you sense below", mood: "intense", next: 12 } ] }, { title: "Guardians of the Cycle", text: "<p>As the blue ember settled onto the ancient altar, a soft chime resonated through the grove. The mushroom circle brightened, casting ethereal light upward that coalesced into translucent figures—the spirits of past Flame Bearers.</p><p>\"You have chosen wisely,\" spoke an elderly spirit, her form more defined than the others. \"The flame was never meant to burn eternally in one form. Like all things, it must transform to endure.\"</p><p>The ember unfurled into a gentle blue flame that spread across the altar, forming symbols that began to rewrite themselves into the history of Ashenvale.</p><p>\"Watch and learn,\" the spirits beckoned. \"For you will carry this new understanding back to your people.\"</p>", choices: [ { text: "Continue the journey into your second chapter...", mood: "calm", next: 0 } ] }, // Add more story segments as needed ]; // Audio elements const audioContext = new (window.AudioContext || window.webkitAudioContext)(); let fireSound; // Initialize initAudio(); createEmbers(); // Initialize audio function initAudio() { soundToggle.addEventListener('click', toggleSound); } // Toggle sound function toggleSound() { soundEnabled = !soundEnabled; if (soundEnabled) { soundToggle.innerHTML = '<i class="fas fa-volume-up">♪</i>'; startFireSound(); } else { soundToggle.innerHTML = '<i class="fas fa-volume-mute">♪</i>'; stopFireSound(); } } // Create and start fire sound function startFireSound() { if (!fireSound && audioContext) { // Create noise const bufferSize = 4096; fireSound = audioContext.createScriptProcessor(bufferSize, 1, 1); fireSound.onaudioprocess = function(e) { const output = e.outputBuffer.getChannelData(0); for (let i = 0; i < bufferSize; i++) { output[i] = Math.random() * 0.15 - 0.075; } }; // Create filter to shape noise into fire sound const filter = audioContext.createBiquadFilter(); filter.type = 'lowpass'; filter.frequency.value = 500; filter.Q.value = 0.5; // Create gain to control volume const gainNode = audioContext.createGain(); gainNode.gain.value = 0.2; // Connect nodes fireSound.connect(filter); filter.connect(gainNode); gainNode.connect(audioContext.destination); } } // Stop fire sound function stopFireSound() { if (fireSound) { fireSound.disconnect(); fireSound = null; } } // Create floating embers function createEmbers() { setInterval(() => { if (Math.random() > 0.7) { const ember = document.createElement('div'); ember.classList.add('ember'); const leftPos = 50 + (Math.random() * 40 - 20); ember.style.left = `${leftPos}%`; const size = Math.random() * 2 + 1; ember.style.width = `${size}px`; ember.style.height = `${size}px`; document.querySelector('.fire-container').appendChild(ember); // Animate the ember setTimeout(() => { ember.style.opacity = '0.7'; ember.style.bottom = `${100 + Math.random() * 100}px`; ember.style.left = `${leftPos + (Math.random() * 10 - 5)}%`; setTimeout(() => { ember.style.opacity = '0'; setTimeout(() => { ember.remove(); }, 1000); }, 1000 + Math.random() * 2000); }, 10); } }, 300); } // Update story based on choice function updateStory(nextIndex) { // Hide current content storyTitle.classList.add('hidden'); storyText.classList.add('hidden'); choiceContainer.classList.add('hidden'); setTimeout(() => { // Update content const segment = storySegments[nextIndex]; storyTitle.textContent = segment.title; storyText.innerHTML = segment.text; // Update choices choiceContainer.innerHTML = ''; segment.choices.forEach(choice => { const btn = document.createElement('button'); btn.classList.add('choice'); btn.textContent = choice.text; btn.dataset.mood = choice.mood; btn.addEventListener('click', () => { updateFireState(choice.mood); playTransitionSound(choice.mood); updateStory(choice.next); }); choiceContainer.appendChild(btn); }); // Show new content setTimeout(() => { storyTitle.classList.remove('hidden'); storyText.classList.remove('hidden'); choiceContainer.classList.remove('hidden'); }, 300); }, 800); currentStoryIndex = nextIndex; } // Update fire state based on mood function updateFireState(mood) { fireContainer.className = 'fire-container'; if (mood === 'calm') { fireContainer.classList.add('calm-flame'); } else if (mood === 'intense') { fireContainer.classList.add('intense-flame'); } else { // neutral state is default } } // Play transition sound based on mood function playTransitionSound(mood) { if (!soundEnabled || !audioContext) return; const oscillator = audioContext.createOscillator(); const gainNode = audioContext.createGain(); if (mood === 'calm') { oscillator.type = 'sine'; oscillator.frequency.setValueAtTime(392, audioContext.currentTime); // G4 gainNode.gain.setValueAtTime(0, audioContext.currentTime); gainNode.gain.linearRampToValueAtTime(0.2, audioContext.currentTime + 0.1); gainNode.gain.linearRampToValueAtTime(0, audioContext.currentTime + 1.5); } else if (mood === 'intense') { oscillator.type = 'sawtooth'; oscillator.frequency.setValueAtTime(220, audioContext.currentTime); // A3 gainNode.gain.setValueAtTime(0, audioContext.currentTime); gainNode.gain.linearRampToValueAtTime(0.15, audioContext.currentTime + 0.05); gainNode.gain.linearRampToValueAtTime(0, audioContext.currentTime + 0.8); } else { // neutral oscillator.type = 'triangle'; oscillator.frequency.setValueAtTime(329.63, audioContext.currentTime); // E4 gainNode.gain.setValueAtTime(0, audioContext.currentTime); gainNode.gain.linearRampToValueAtTime(0.1, audioContext.currentTime + 0.1); gainNode.gain.linearRampToValueAtTime(0, audioContext.currentTime + 1.2); } oscillator.connect(gainNode); gainNode.connect(audioContext.destination); oscillator.start(); oscillator.stop(audioContext.currentTime + 2); } // Set up initial choice event listeners choices.forEach(choice => { choice.addEventListener('click', function() { const mood = this.dataset.mood; updateFireState(mood); playTransitionSound(mood); const choiceIndex = Array.from(choices).indexOf(this); const nextIndex = storySegments[currentStoryIndex].choices[choiceIndex].next; updateStory(nextIndex); }); }); }); </script> </body> </html>