← back to Model Arena

data/artifacts/2ca024f41f3c/claude-code.html

305 lines

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Solar System Orrery</title>
<style>
  html, body {
    margin: 0;
    padding: 0;
    height: 100%;
    background: #05060f;
    overflow: hidden;
    font-family: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    color: #dfe6ff;
  }
  #wrap { position: relative; width: 100vw; height: 100vh; }
  canvas { display: block; width: 100%; height: 100%; cursor: default; }
  #controls {
    position: absolute;
    left: 50%;
    bottom: 18px;
    transform: translateX(-50%);
    display: flex;
    align-items: center;
    gap: 14px;
    background: rgba(10, 14, 30, 0.72);
    border: 1px solid rgba(120, 150, 255, 0.25);
    border-radius: 999px;
    padding: 10px 18px;
    backdrop-filter: blur(6px);
    user-select: none;
  }
  #controls label { font-size: 13px; letter-spacing: 0.04em; opacity: 0.85; white-space: nowrap; }
  #speed { width: 160px; accent-color: #6ea8ff; cursor: pointer; }
  #speedVal { font-size: 12px; min-width: 42px; text-align: right; font-variant-numeric: tabular-nums; opacity: 0.8; }
  #pauseBtn {
    background: #1d2b55;
    color: #dfe6ff;
    border: 1px solid rgba(120, 150, 255, 0.4);
    border-radius: 999px;
    padding: 6px 16px;
    font-size: 13px;
    cursor: pointer;
    min-width: 84px;
    transition: background 0.15s;
  }
  #pauseBtn:hover { background: #2a3c74; }
  #tooltip {
    position: absolute;
    pointer-events: none;
    background: rgba(8, 12, 28, 0.9);
    border: 1px solid rgba(140, 170, 255, 0.45);
    border-radius: 6px;
    padding: 4px 10px;
    font-size: 13px;
    letter-spacing: 0.03em;
    white-space: nowrap;
    transform: translate(-50%, -140%);
    opacity: 0;
    transition: opacity 0.12s;
  }
  #title {
    position: absolute;
    top: 16px;
    left: 50%;
    transform: translateX(-50%);
    font-size: 13px;
    letter-spacing: 0.35em;
    text-transform: uppercase;
    opacity: 0.55;
    user-select: none;
  }
</style>
</head>
<body>
<div id="wrap">
  <canvas id="cv"></canvas>
  <div id="title">Solar System Orrery</div>
  <div id="tooltip"></div>
  <div id="controls">
    <button id="pauseBtn">Pause</button>
    <label for="speed">Speed</label>
    <input type="range" id="speed" min="0" max="300" value="100">
    <span id="speedVal">1.0&times;</span>
  </div>
</div>
<script>
(function () {
  "use strict";
  var canvas = document.getElementById("cv");
  var ctx = canvas.getContext("2d");
  var tooltip = document.getElementById("tooltip");
  var pauseBtn = document.getElementById("pauseBtn");
  var speedSlider = document.getElementById("speed");
  var speedVal = document.getElementById("speedVal");

  var W = 0, H = 0, DPR = 1, cx = 0, cy = 0, scale = 1;

  // Orbital periods in Earth years (proportional speeds), display radii/sizes stylized.
  var planets = [
    { name: "Mercury", period: 0.241, dist: 0.10, size: 3.2, color: "#b8a89a" },
    { name: "Venus",   period: 0.615, dist: 0.155, size: 5.2, color: "#e8c47a" },
    { name: "Earth",   period: 1.000, dist: 0.215, size: 5.6, color: "#5f9ce0" },
    { name: "Mars",    period: 1.881, dist: 0.275, size: 4.2, color: "#d1663d" },
    { name: "Jupiter", period: 11.86, dist: 0.40,  size: 12,  color: "#d9a878" },
    { name: "Saturn",  period: 29.46, dist: 0.53,  size: 10,  color: "#e3cf9a", ring: true },
    { name: "Uranus",  period: 84.01, dist: 0.65,  size: 7,   color: "#9fd8de" },
    { name: "Neptune", period: 164.8, dist: 0.76,  size: 6.8, color: "#5a7fe0" }
  ];
  planets.forEach(function (p, i) {
    p.angle = (i * 2.399963) % (Math.PI * 2); // spread starting positions
    p.x = 0; p.y = 0;
  });

  // Static starfield (regenerated on resize)
  var stars = [];
  function makeStars() {
    stars = [];
    var n = Math.floor((W * H) / 3800);
    for (var i = 0; i < n; i++) {
      stars.push({
        x: Math.random() * W,
        y: Math.random() * H,
        r: Math.random() * 1.1 + 0.2,
        a: Math.random() * 0.6 + 0.15
      });
    }
  }

  function resize() {
    DPR = window.devicePixelRatio || 1;
    W = window.innerWidth;
    H = window.innerHeight;
    canvas.width = Math.round(W * DPR);
    canvas.height = Math.round(H * DPR);
    ctx.setTransform(DPR, 0, 0, DPR, 0, 0);
    cx = W / 2;
    cy = H / 2;
    scale = Math.min(W, H) * 0.60; // max orbit dist 0.76 of half-min stays inside
    makeStars();
  }
  window.addEventListener("resize", resize);
  resize();

  var paused = false;
  var speedFactor = 1.0;
  var hovered = null;
  var mouseX = -1, mouseY = -1;

  pauseBtn.addEventListener("click", function () {
    paused = !paused;
    pauseBtn.textContent = paused ? "Resume" : "Pause";
  });

  speedSlider.addEventListener("input", function () {
    // 0..300 -> 0x..3x, quadratic-ish feel around 1x at 100
    speedFactor = speedSlider.value / 100;
    speedVal.innerHTML = speedFactor.toFixed(1) + "&times;";
  });

  canvas.addEventListener("mousemove", function (e) {
    var rect = canvas.getBoundingClientRect();
    mouseX = e.clientX - rect.left;
    mouseY = e.clientY - rect.top;
  });
  canvas.addEventListener("mouseleave", function () {
    mouseX = -1; mouseY = -1;
  });

  function pickHover() {
    hovered = null;
    if (mouseX < 0) return;
    var best = 1e9;
    for (var i = 0; i < planets.length; i++) {
      var p = planets[i];
      var dx = mouseX - p.x, dy = mouseY - p.y;
      var d = Math.sqrt(dx * dx + dy * dy);
      var hitR = Math.max(p.size + 6, 12);
      if (d < hitR && d < best) { best = d; hovered = p; }
    }
    canvas.style.cursor = hovered ? "pointer" : "default";
  }

  function draw(dt) {
    // background
    ctx.fillStyle = "#05060f";
    ctx.fillRect(0, 0, W, H);

    // stars
    for (var i = 0; i < stars.length; i++) {
      var s = stars[i];
      ctx.globalAlpha = s.a;
      ctx.fillStyle = "#ffffff";
      ctx.beginPath();
      ctx.arc(s.x, s.y, s.r, 0, 6.2832);
      ctx.fill();
    }
    ctx.globalAlpha = 1;

    // orbit rings
    ctx.lineWidth = 1;
    for (i = 0; i < planets.length; i++) {
      var p = planets[i];
      ctx.strokeStyle = (p === hovered) ? "rgba(150,180,255,0.55)" : "rgba(120,140,200,0.18)";
      ctx.beginPath();
      ctx.arc(cx, cy, p.dist * scale, 0, 6.2832);
      ctx.stroke();
    }

    // sun with glow
    var sunR = Math.max(14, scale * 0.045);
    var g = ctx.createRadialGradient(cx, cy, 0, cx, cy, sunR * 3);
    g.addColorStop(0, "rgba(255,220,130,0.9)");
    g.addColorStop(0.25, "rgba(255,180,70,0.35)");
    g.addColorStop(1, "rgba(255,150,40,0)");
    ctx.fillStyle = g;
    ctx.beginPath();
    ctx.arc(cx, cy, sunR * 3, 0, 6.2832);
    ctx.fill();
    var sg = ctx.createRadialGradient(cx - sunR * 0.3, cy - sunR * 0.3, sunR * 0.1, cx, cy, sunR);
    sg.addColorStop(0, "#fff6d8");
    sg.addColorStop(0.6, "#ffd257");
    sg.addColorStop(1, "#f39b1d");
    ctx.fillStyle = sg;
    ctx.beginPath();
    ctx.arc(cx, cy, sunR, 0, 6.2832);
    ctx.fill();

    // planets
    for (i = 0; i < planets.length; i++) {
      p = planets[i];
      if (!paused) {
        // Earth completes one orbit in 12s at 1x speed; others proportional to real periods
        p.angle += (dt * speedFactor * (2 * Math.PI / 12)) / p.period;
      }
      var r = p.dist * scale;
      p.x = cx + Math.cos(p.angle) * r;
      p.y = cy + Math.sin(p.angle) * r;

      // saturn ring
      if (p.ring) {
        ctx.save();
        ctx.translate(p.x, p.y);
        ctx.rotate(-0.45);
        ctx.strokeStyle = "rgba(220,200,150,0.75)";
        ctx.lineWidth = 2.4;
        ctx.beginPath();
        ctx.ellipse(0, 0, p.size * 1.9, p.size * 0.72, 0, 0, 6.2832);
        ctx.stroke();
        ctx.restore();
      }

      var pg = ctx.createRadialGradient(p.x - p.size * 0.35, p.y - p.size * 0.35, p.size * 0.15, p.x, p.y, p.size);
      pg.addColorStop(0, "#ffffff");
      pg.addColorStop(0.25, p.color);
      pg.addColorStop(1, shade(p.color, -55));
      ctx.fillStyle = pg;
      ctx.beginPath();
      ctx.arc(p.x, p.y, p.size, 0, 6.2832);
      ctx.fill();

      if (p === hovered) {
        ctx.strokeStyle = "rgba(255,255,255,0.85)";
        ctx.lineWidth = 1.5;
        ctx.beginPath();
        ctx.arc(p.x, p.y, p.size + 4, 0, 6.2832);
        ctx.stroke();
      }
    }
  }

  function shade(hex, amt) {
    var n = parseInt(hex.slice(1), 16);
    var r = Math.max(0, Math.min(255, (n >> 16) + amt));
    var gr = Math.max(0, Math.min(255, ((n >> 8) & 255) + amt));
    var b = Math.max(0, Math.min(255, (n & 255) + amt));
    return "rgb(" + r + "," + gr + "," + b + ")";
  }

  function updateTooltip() {
    if (hovered) {
      tooltip.textContent = hovered.name;
      tooltip.style.left = hovered.x + "px";
      tooltip.style.top = hovered.y + "px";
      tooltip.style.opacity = "1";
    } else {
      tooltip.style.opacity = "0";
    }
  }

  var last = performance.now();
  function frame(now) {
    var dt = Math.min((now - last) / 1000, 0.1);
    last = now;
    pickHover();
    draw(dt);
    updateTooltip();
    requestAnimationFrame(frame);
  }
  requestAnimationFrame(frame);
})();
</script>
</body>
</html>