about summary refs log blame commit diff stats
path: root/html/bp-sand/index.html
blob: 48c98d8fa1aac225c5a8ad2c8254054ca66b52ea (plain) (tree)



































                                                                          



                                                    






                                                                     
                                                                     






































































































                                                                                            



                                                         
















                                                   
<!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">
        <div>
            <button id="clearSand">Clear</button>
            <button id="toggleButton">Start</button>
        </div>
    </div>

    <script>
        const canvas = document.getElementById('sandCanvas');
        const ctx = canvas.getContext('2d');
        const bpmInput = document.getElementById('bpmInput');
        const toggleButton = document.getElementById('toggleButton');
        const clearSandButton = document.getElementById('clearSand');
        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);

        clearSandButton.addEventListener('click', () => {
            sandParticles = [];
        });

        bpmInput.addEventListener('input', (e) => {
            bpm = parseInt(e.target.value);
            if (isRunning) {
                clearInterval(intervalId);
                toggleBPM();
            }
        });

        function animate() {
            gameLoop();
            requestAnimationFrame(animate);
        }

        animate();
    </script>
</body>
</html>