diff options
-rw-r--r-- | html/bp-sand/index.html | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/html/bp-sand/index.html b/html/bp-sand/index.html new file mode 100644 index 0000000..e34e33c --- /dev/null +++ b/html/bp-sand/index.html @@ -0,0 +1,164 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>BP Sand</title> + <style> + body, html { + margin: 0; + padding: 0; + overflow: hidden; + display: flex; + flex-direction: column; + height: 100%; + } + canvas { + display: block; + width: 100%; + background-color: beige; + } + .controls { + display: flex; + justify-content: space-around; + padding: 10px; + background-color: white; + box-shadow: 0 -2px 5px rgba(0,0,0,0.1); + } + input, button { + font-size: 1.5rem; + } + </style> +</head> +<body> + <canvas id="sandCanvas"></canvas> + <div class="controls"> + <input type="number" id="bpmInput" value="60" min="10" max="300"> + <button id="toggleButton">Start</button> + </div> + + <script> + const canvas = document.getElementById('sandCanvas'); + const ctx = canvas.getContext('2d'); + const bpmInput = document.getElementById('bpmInput'); + const toggleButton = document.getElementById('toggleButton'); + const controls = document.querySelector('.controls'); + + let bpm = 60; + let isRunning = false; + let sandParticles = []; + let intervalId; + + // Set canvas size to avoid overlapping with controls + function resizeCanvas() { + canvas.width = window.innerWidth; + canvas.height = window.innerHeight - controls.offsetHeight; + } + + window.addEventListener('resize', resizeCanvas); + resizeCanvas(); + + const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); + + function playTone() { + const oscillator = audioCtx.createOscillator(); + oscillator.type = 'sine'; + oscillator.frequency.setValueAtTime(440, audioCtx.currentTime); // 440 Hz tone + oscillator.connect(audioCtx.destination); + oscillator.start(); + oscillator.stop(audioCtx.currentTime + 0.1); // Short beep + } + + function createSandParticle() { + return { + x: Math.random() * canvas.width, + y: 0, + size: 5 + Math.random() * 5, // Vary size a bit + velocityY: 0, + atRest: false // Track if the particle is at rest + }; + } + + // Update sand particle positions + function updateSand() { + for (let particle of sandParticles) { + if (!particle.atRest) { + particle.velocityY += 0.1; // Accelerate! Gravity! + particle.y += particle.velocityY; + + // Check if sand particle has hit the bottom + if (particle.y + particle.size >= canvas.height) { + particle.y = canvas.height - particle.size; + particle.atRest = true; // Mark the particle as at rest + } + + // Check if sand particle has landed on another particle + for (let otherParticle of sandParticles) { + if (otherParticle !== particle && otherParticle.atRest) { + let distY = otherParticle.y - (particle.y + particle.size); + let distX = Math.abs(otherParticle.x - particle.x); + if (distY <= 0 && distX < particle.size) { + particle.y = otherParticle.y - particle.size; + particle.atRest = true; + break; + } + } + } + } + } + } + + // Draw sand particles on the canvas + function drawSand() { + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.fillStyle = 'teal'; + + for (let particle of sandParticles) { + ctx.beginPath(); + ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2); + ctx.fill(); + } + } + + // Main loop + function gameLoop() { + updateSand(); + drawSand(); + } + + // Control the BPM and start/stop the loop + function toggleBPM() { + isRunning = !isRunning; + if (isRunning) { + let interval = (60 / bpm) * 1000; // Convert BPM to interval in milliseconds + intervalId = setInterval(() => { + sandParticles.push(createSandParticle()); // Generate new sand + playTone(); // Play the tone + }, interval); + toggleButton.textContent = 'Stop'; + } else { + clearInterval(intervalId); + toggleButton.textContent = 'Start'; + } + } + + // Event Listeners + toggleButton.addEventListener('click', toggleBPM); + + bpmInput.addEventListener('input', (e) => { + bpm = parseInt(e.target.value); + if (isRunning) { + clearInterval(intervalId); + toggleBPM(); + } + }); + + function animate() { + gameLoop(); + requestAnimationFrame(animate); + } + + animate(); + </script> +</body> +</html> |