const canvas = document.getElementById("canvas"); const ctx = canvas.getContext("2d"); const densityInput = document.getElementById("density"); const curlInput = document.getElementById("curl"); const speedInput = document.getElementById("speed"); const regenBtn = document.getElementById("regen"); function resize() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; } window.addEventListener("resize", resize); resize(); let seed = Math.random() * 1000; let particles = []; function randNoise(x, y) { return Math.sin(x * 0.002 + seed) + Math.cos(y * 0.002 + seed); } function createParticles() { particles = []; for (let i = 0; i < densityInput.value; i++) { particles.push({ x: Math.random() * canvas.width, y: Math.random() * canvas.height, life: Math.random() * 200 }); } } function flowAngle(x, y) { return randNoise(x, y) * curlInput.value; } function draw() { ctx.fillStyle = "rgba(0, 0, 0, 0.08)"; ctx.fillRect(0, 0, canvas.width, canvas.height); for (const p of particles) { const angle = flowAngle(p.x, p.y); const vx = Math.cos(angle) * speedInput.value; const vy = Math.sin(angle) * speedInput.value; ctx.strokeStyle = `hsla(${(p.life * 3) % 360}, 100%, 60%, 0.8)`; ctx.beginPath(); ctx.moveTo(p.x, p.y); p.x += vx; p.y += vy; ctx.lineTo(p.x, p.y); ctx.stroke(); p.life--; if ( p.life <= 0 || p.x < 0 || p.x > canvas.width || p.y < 0 || p.y > canvas.height ) { p.x = Math.random() * canvas.width; p.y = Math.random() * canvas.height; p.life = 200; } } requestAnimationFrame(draw); } regenBtn.onclick = () => { seed = Math.random() * 1000; createParticles(); ctx.clearRect(0, 0, canvas.width, canvas.height); }; createParticles(); draw();