← 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>