about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--html/bp-sand/index.html164
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>