← back to Model Arena

data/artifacts/0e969e848172/kimi.html

261 lines

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Gravity Sandbox</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
            background: #000;
            font-family: sans-serif;
        }
        canvas {
            display: block;
        }
        #controls {
            position: absolute;
            top: 10px;
            left: 10px;
            background: rgba(0,0,0,0.8);
            padding: 15px;
            border-radius: 8px;
            color: white;
            user-select: none;
            border: 1px solid #333;
        }
        button {
            margin: 5px;
            padding: 8px 15px;
            cursor: pointer;
            background: #222;
            color: white;
            border: 1px solid #444;
            border-radius: 4px;
            font-size: 14px;
        }
        button:hover {
            background: #333;
            border-color: #666;
        }
        button.active {
            background: #2a5;
            border-color: #4c7;
        }
        #info {
            margin-top: 10px;
            font-size: 12px;
            color: #888;
        }
    </style>
</head>
<body>
    <canvas id="canvas"></canvas>
    <div id="controls">
        <button id="clearBtn">Clear All</button>
        <button id="trailBtn" class="active">Trails: ON</button>
        <div id="info">Click & drag to launch planets</div>
    </div>
    
    <script>
        const canvas = document.getElementById('canvas');
        const ctx = canvas.getContext('2d');
        
        function resize() {
            canvas.width = window.innerWidth;
            canvas.height = window.innerHeight;
        }
        resize();
        window.addEventListener('resize', resize);
        
        const G = 0.8;
        const SOFTENING = 10;
        const TRAIL_LENGTH = 150;
        const TIME_STEP = 0.4;
        
        let planets = [];
        let trails = true;
        let isDragging = false;
        let dragStart = {x: 0, y: 0};
        let dragCurrent = {x: 0, y: 0};
        
        class Planet {
            constructor(x, y, vx, vy, mass) {
                this.x = x;
                this.y = y;
                this.vx = vx;
                this.vy = vy;
                this.mass = mass;
                this.radius = Math.cbrt(mass) * 2.5;
                this.color = `hsl(${Math.random() * 360}, 80%, 60%)`;
                this.history = [];
            }
            
            update() {
                if (trails) {
                    this.history.push({x: this.x, y: this.y});
                    if (this.history.length > TRAIL_LENGTH) {
                        this.history.shift();
                    }
                } else {
                    this.history = [];
                }
                
                this.x += this.vx * TIME_STEP;
                this.y += this.vy * TIME_STEP;
                
                if (this.x < -1000 || this.x > canvas.width + 1000 || 
                    this.y < -1000 || this.y > canvas.height + 1000) {
                    return false;
                }
                return true;
            }
            
            draw() {
                if (trails && this.history.length > 1) {
                    ctx.lineWidth = 2;
                    for (let i = 0; i < this.history.length - 1; i++) {
                        const alpha = (i / this.history.length) * 0.6;
                        ctx.strokeStyle = this.color.replace(')', `, ${alpha})`).replace('hsl', 'hsla');
                        ctx.beginPath();
                        ctx.moveTo(this.history[i].x, this.history[i].y);
                        ctx.lineTo(this.history[i+1].x, this.history[i+1].y);
                        ctx.stroke();
                    }
                }
                
                ctx.beginPath();
                ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
                ctx.fillStyle = this.color;
                ctx.fill();
                
                const gradient = ctx.createRadialGradient(
                    this.x, this.y, 0,
                    this.x, this.y, this.radius * 3
                );
                gradient.addColorStop(0, this.color.replace(')', ', 0.4)').replace('hsl', 'hsla'));
                gradient.addColorStop(1, 'transparent');
                ctx.fillStyle = gradient;
                ctx.beginPath();
                ctx.arc(this.x, this.y, this.radius * 3, 0, Math.PI * 2);
                ctx.fill();
            }
        }
        
        function applyGravity() {
            for (let i = 0; i < planets.length; i++) {
                for (let j = i + 1; j < planets.length; j++) {
                    const p1 = planets[i];
                    const p2 = planets[j];
                    
                    const dx = p2.x - p1.x;
                    const dy = p2.y - p1.y;
                    const distSq = dx * dx + dy * dy;
                    const dist = Math.sqrt(distSq);
                    
                    if (dist < p1.radius + p2.radius) {
                        const overlap = (p1.radius + p2.radius - dist) * 0.5;
                        const nx = dx / dist;
                        const ny = dy / dist;
                        p1.x -= nx * overlap;
                        p1.y -= ny * overlap;
                        p2.x += nx * overlap;
                        p2.y += ny * overlap;
                    }
                    
                    const force = G * p1.mass * p2.mass / (distSq + SOFTENING * SOFTENING);
                    const fx = force * dx / dist;
                    const fy = force * dy / dist;
                    
                    p1.vx += fx / p1.mass;
                    p1.vy += fy / p1.mass;
                    p2.vx -= fx / p2.mass;
                    p2.vy -= fy / p2.mass;
                }
            }
        }
        
        function animate() {
            if (trails) {
                ctx.fillStyle = 'rgba(0, 0, 0, 0.15)';
                ctx.fillRect(0, 0, canvas.width, canvas.height);
            } else {
                ctx.clearRect(0, 0, canvas.width, canvas.height);
            }
            
            applyGravity();
            
            for (let i = planets.length - 1; i >= 0; i--) {
                const alive = planets[i].update();
                if (!alive) {
                    planets.splice(i, 1);
                } else {
                    planets[i].draw();
                }
            }
            
            if (isDragging) {
                ctx.beginPath();
                ctx.moveTo(dragStart.x, dragStart.y);
                ctx.lineTo(dragCurrent.x, dragCurrent.y);
                ctx.strokeStyle = 'rgba(255, 255, 255, 0.8)';
                ctx.lineWidth = 2;
                ctx.setLineDash([5, 5]);
                ctx.stroke();
                ctx.setLineDash([]);
                
                ctx.beginPath();
                ctx.arc(dragStart.x, dragStart.y, 8, 0, Math.PI * 2);
                ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
                ctx.fill();
            }
            
            requestAnimationFrame(animate);
        }
        
        canvas.addEventListener('mousedown', (e) => {
            isDragging = true;
            dragStart.x = e.clientX;
            dragStart.y = e.clientY;
            dragCurrent.x = e.clientX;
            dragCurrent.y = e.clientY;
        });
        
        canvas.addEventListener('mousemove', (e) => {
            if (isDragging) {
                dragCurrent.x = e.clientX;
                dragCurrent.y = e.clientY;
            }
        });
        
        canvas.addEventListener('mouseup', (e) => {
            if (isDragging) {
                const vx = (dragStart.x - e.clientX) * 0.08;
                const vy = (dragStart.y - e.clientY) * 0.08;
                const mass = 15 + Math.random() * 50;
                planets.push(new Planet(dragStart.x, dragStart.y, vx, vy, mass));
                isDragging = false;
            }
        });
        
        document.getElementById('clearBtn').addEventListener('click', () => {
            planets = [];
        });
        
        document.getElementById('trailBtn').addEventListener('click', (e) => {
            trails = !trails;
            e.target.textContent = trails ? 'Trails: ON' : 'Trails: OFF';
            e.target.classList.toggle('active');
            if (!trails) {
                for (const p of planets) {
                    p.history = [];
                }
            }
        });
        
        planets.push(new Planet(canvas.width/2, canvas.height/2, 0, 0, 300));
        animate();
    </script>
</body>
</html>