← back to Model Arena
data/artifacts/0e969e848172/gemma3-12b.html
137 lines
<!DOCTYPE html>
<html>
<head>
<title>Gravity Sandbox</title>
<style>
body { margin: 0; overflow: hidden; background-color: #111; }
canvas { display: block; }
#controls { position: absolute; top: 10px; left: 10px; font-family: sans-serif; color: white; }
</style>
</head>
<body>
<div id="controls">
Click to place planets. Clear button below. Trail toggle on/off.
<button id="clearButton">Clear</button>
<label for="trailToggle">Trails:</label>
<input type="checkbox" id="trailToggle" checked>
</div>
<canvas id="sandboxCanvas"></canvas>
<script>
const canvas = document.getElementById('sandboxCanvas');
const ctx = canvas.getContext('2d');
const clearButton = document.getElementById('clearButton');
const trailToggle = document.getElementById('trailToggle');
let bodies = [];
let trails = [];
const gravityConstant = 6.674e-11; // Reduced for performance
const pixelRatio = window.devicePixelRatio || 1;
canvas.width = canvas.getContext('2d').canvas.clientWidth * pixelRatio;
canvas.height = canvas.getContext('2d').canvas.clientHeight * pixelRatio;
ctx.scale(pixelRatio, pixelRatio);
function Body(x, y, mass, color) {
this.x = x;
this.y = y;
this.mass = mass;
this.color = color || '#fff';
this.vx = 0;
this.vy = 0;
this.trail = [];
}
function distance(x1, y1, x2, y2) {
const dx = x2 - x1;
const dy = y2 - y1;
return Math.sqrt(dx * dx + dy * dy);
}
function calculateAttraction(body1, body2) {
const dist = distance(body1.x, body1.y, body2.x, body2.y);
const force = gravityConstant * (body1.mass * body2.mass) / (dist * dist);
const dx = (body2.x - body1.x) / dist;
const dy = (body2.y - body1.y) / dist;
body1.vx += force * dx;
body1.vy += force * dy;
body2.vx -= force * dx;
body2.vy -= force * dy;
}
function update() {
for (let i = 0; i < bodies.length; i++) {
for (let j = i + 1; j < bodies.length; j++) {
calculateAttraction(bodies[i], bodies[j]);
}
// Apply velocity and update position
bodies[i].vx *= 0.98; // Damping to prevent runaway speed
bodies[i].vy *= 0.98;
bodies[i].x += bodies[i].vx;
bodies[i].y += bodies[i].vy;
// Trail recording
if (trailToggle.checked) {
bodies[i].trail.push({ x: bodies[i].x, y: bodies[i].y });
if(bodies[i].trail.length > 100){
bodies[i].trail.shift();
}
}
}
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let i = 0; i < bodies.length; i++) {
ctx.beginPath();
ctx.arc(bodies[i].x, bodies[i].y, Math.max(5, bodies[i].mass / 10), 0, 2 * Math.PI); // Size proportional to mass
ctx.fillStyle = bodies[i].color;
ctx.fill();
if (trailToggle.checked && bodies[i].trail.length > 0) {
ctx.beginPath();
ctx.moveTo(bodies[i].trail[0].x, bodies[i].trail[0].y);
for (let j = 1; j < bodies[i].trail.length; j++) {
ctx.lineTo(bodies[i].trail[j].x, bodies[i].trail[j].y);
}
ctx.strokeStyle = bodies[i].color;
ctx.lineWidth = 2;
ctx.stroke();
}
}
}
canvas.addEventListener('click', (event) => {
const x = event.clientX * pixelRatio;
const y = event.clientY * pixelRatio;
const mass = Math.random() * 10 + 1; // Random mass between 1 and 10
const color = '#' + Math.floor(Math.random()*16777215).toString(16);
bodies.push(new Body(x, y, mass, color));
});
clearButton.addEventListener('click', () => {
bodies = [];
trails = [];
});
trailToggle.addEventListener('change', () =>{
//No action needed here as trails are already being added or not based on checkbox state
})
function animate() {
update();
draw();
requestAnimationFrame(animate);
}
animate();
</script>
</body>
</html>