← back to Model Arena

data/artifacts/0e969e848172/hermes3-8b.html

138 lines

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>2D Gravity Sandbox</title>
    <style>
        body {
            margin: 0;
            padding: 0;
            overflow: hidden;
        }
        
        canvas {
            display: block;
            background-color: #f0f0f0;
        }
    </style>
</head>
<body>
    <canvas id="canvas"></canvas>
    <div>
        <button id="clear">Clear</button>
        <label><input type="checkbox" id="trailToggle"> Trails</label>
    </div>

    <script>
        const canvas = document.getElementById('canvas');
        const ctx = canvas.getContext('2d');

        canvas.width = window.innerWidth * 0.8;
        canvas.height = window.innerHeight * 0.8;

        let bodies = [];
        let trails = true;

        function Body(x, y, radius, mass) {
            this.x = x;
            this.y = y;
            this.radius = radius;
            this.mass = mass;
            this.speedX = 0;
            this.speedY = 0;
        }

        function placeBody(e) {
            if (e.button === 0) { // left click
                const radius = Math.random() * 10 + 1;
                const mass = radius * 2;
                bodies.push(new Body(e.pageX - canvas.offsetLeft, e.pageY - canvas.offsetTop, radius, mass));
            }
        }

        canvas.addEventListener('mousedown', placeBody);

        function updateBodies() {
            for (let i = 0; i < bodies.length; i++) {
                const bodyA = bodies[i];

                for (let j = i + 1; j < bodies.length; j++) {
                    const bodyB = bodies[j];
                    
                    const distanceX = bodyA.x - bodyB.x;
                    const distanceY = bodyA.y - bodyB.y;
                    const distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
                    const force = (bodyA.mass * bodyB.mass) / (distance * distance);
                    const forceX = force * distanceX / distance;
                    const forceY = force * distanceY / distance;

                    bodyA.speedX += forceX;
                    bodyA.speedY += forceY;

                    bodyB.speedX -= forceX;
                    bodyB.speedY -= forceY;
                }

                bodyA.x += bodyA.speedX;
                bodyA.y += bodyA.speedY;

                if (trails) {
                    for (let k = 0; k < bodies.length; k++) {
                        if (i === k) continue;

                        const trailBody = bodies[k];
                        
                        const distanceX = bodyA.x - trailBody.x;
                        const distanceY = bodyA.y - trailBody.y;
                        const distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY);

                        if (distance < bodyA.radius + trailBody.radius) {
                            ctx.beginPath();
                            ctx.moveTo(trailBody.x, trailBody.y);
                            ctx.lineTo(bodyA.x, bodyA.y);
                            ctx.strokeStyle = `hsl(${i}, 100%, 50%)`;
                            ctx.stroke();
                        }
                    }
                }
            }

            if (trails) {
                for (let i = 0; i < bodies.length; i++) {
                    const body = bodies[i];
                    
                    ctx.beginPath();
                    ctx.arc(body.x, body.y, body.radius, 0, Math.PI * 2);
                    ctx.fillStyle = `hsl(${i}, 100%, 50%)`;
                    ctx.fill();

                    if (trails) {
                        ctx.beginPath();
                        ctx.arc(body.x, body.y, body.radius / 3, 0, Math.PI * 2);
                        ctx.strokeStyle = `rgba(255, 255, 255, 0.7)`;
                        ctx.stroke();
                    }
                }
            }
        }

        function loop() {
            requestAnimationFrame(loop);

            ctx.clearRect(0, 0, canvas.width, canvas.height);
            updateBodies();
        }

        document.getElementById('clear').addEventListener('click', () => {
            bodies = [];
        });

        document.getElementById('trailToggle').addEventListener('change', (e) => {
            trails = e.target.checked;
        });

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