← back to Model Arena
data/artifacts/2ca024f41f3c/grok.html
355 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>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: #050510;
color: #e0e0ff;
font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
overflow: hidden;
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
canvas {
display: block;
cursor: crosshair;
}
#ui {
position: fixed;
bottom: 24px;
left: 50%;
transform: translateX(-50%);
display: flex;
align-items: center;
gap: 16px;
background: rgba(10, 12, 30, 0.85);
border: 1px solid rgba(120, 140, 255, 0.25);
border-radius: 12px;
padding: 12px 20px;
backdrop-filter: blur(8px);
box-shadow: 0 8px 32px rgba(0,0,0,0.5);
z-index: 10;
}
#ui label {
font-size: 13px;
opacity: 0.85;
white-space: nowrap;
}
#speed {
width: 140px;
accent-color: #6af;
}
#pause {
background: linear-gradient(135deg, #3a4a8a, #2a3a6a);
color: #eef;
border: 1px solid rgba(140,160,255,0.4);
border-radius: 8px;
padding: 8px 16px;
font-size: 13px;
cursor: pointer;
min-width: 72px;
}
#pause:hover { background: linear-gradient(135deg, #4a5aaa, #3a4a8a); }
#tooltip {
position: fixed;
pointer-events: none;
background: rgba(15, 18, 40, 0.92);
border: 1px solid rgba(140, 160, 255, 0.45);
color: #fff;
padding: 6px 12px;
border-radius: 6px;
font-size: 13px;
letter-spacing: 0.03em;
opacity: 0;
transition: opacity 0.12s;
z-index: 20;
white-space: nowrap;
}
#title {
position: fixed;
top: 18px;
left: 50%;
transform: translateX(-50%);
font-size: 14px;
letter-spacing: 0.2em;
text-transform: uppercase;
opacity: 0.5;
font-weight: 300;
}
</style>
</head>
<body>
<div id="title">Solar System Orrery</div>
<canvas id="c"></canvas>
<div id="tooltip"></div>
<div id="ui">
<label for="speed">Speed</label>
<input type="range" id="speed" min="0" max="5" step="0.05" value="1">
<button id="pause">Pause</button>
</div>
<script>
(function () {
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
const tooltip = document.getElementById('tooltip');
const speedSlider = document.getElementById('speed');
const pauseBtn = document.getElementById('pause');
let W, H, cx, cy;
let dpr = 1;
let paused = false;
let speedMul = 1;
let t = 0;
let last = performance.now();
let mouse = { x: -9999, y: -9999 };
let stars = [];
// Orbital periods in Earth years (approx). Distances scaled for visual layout.
// radius = visual orbit radius in "AU units", size = planet draw radius
const planets = [
{ name: 'Mercury', color: '#b0b0b0', radius: 0.39, size: 3.2, period: 0.241, angle: 0.8 },
{ name: 'Venus', color: '#e8cda0', radius: 0.72, size: 5.5, period: 0.615, angle: 2.1 },
{ name: 'Earth', color: '#6ab0ff', radius: 1.00, size: 5.8, period: 1.000, angle: 0.4 },
{ name: 'Mars', color: '#e07050', radius: 1.52, size: 4.2, period: 1.881, angle: 3.5 },
{ name: 'Jupiter', color: '#d4a86a', radius: 2.80, size: 12, period: 11.86, angle: 1.2 },
{ name: 'Saturn', color: '#e8d5a0', radius: 3.60, size: 10, period: 29.46, angle: 4.0, ring: true },
{ name: 'Uranus', color: '#7ec8e0', radius: 4.40, size: 7.5, period: 84.01, angle: 0.6 },
{ name: 'Neptune', color: '#4a6aff', radius: 5.10, size: 7.2, period: 164.8, angle: 2.8 }
];
const SUN_SIZE = 18;
function resize() {
dpr = Math.min(window.devicePixelRatio || 1, 2);
W = window.innerWidth;
H = window.innerHeight;
canvas.width = W * dpr;
canvas.height = H * dpr;
canvas.style.width = W + 'px';
canvas.style.height = H + 'px';
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
cx = W / 2;
cy = H / 2 - 10;
buildStars();
}
function buildStars() {
stars = [];
const n = Math.floor((W * H) / 3500);
for (let i = 0; i < n; i++) {
stars.push({
x: Math.random() * W,
y: Math.random() * H,
r: Math.random() * 1.2 + 0.2,
a: Math.random() * 0.6 + 0.2
});
}
}
function scale() {
// Fit outermost orbit with padding
const maxR = planets[planets.length - 1].radius;
const pad = Math.min(W, H) * 0.08;
return (Math.min(W, H) / 2 - pad - 20) / maxR;
}
function planetPos(p, s) {
const ang = p.angle + (t * Math.PI * 2) / p.period;
const r = p.radius * s;
return {
x: cx + Math.cos(ang) * r,
y: cy + Math.sin(ang) * r,
ang
};
}
function drawStars() {
for (const st of stars) {
ctx.beginPath();
ctx.fillStyle = `rgba(220,225,255,${st.a})`;
ctx.arc(st.x, st.y, st.r, 0, Math.PI * 2);
ctx.fill();
}
}
function drawOrbit(p, s) {
ctx.beginPath();
ctx.strokeStyle = 'rgba(120, 140, 200, 0.18)';
ctx.lineWidth = 1;
ctx.arc(cx, cy, p.radius * s, 0, Math.PI * 2);
ctx.stroke();
}
function drawSun() {
const g = ctx.createRadialGradient(cx, cy, 0, cx, cy, SUN_SIZE * 2.5);
g.addColorStop(0, '#fff8e0');
g.addColorStop(0.25, '#ffe060');
g.addColorStop(0.5, '#ff9020');
g.addColorStop(1, 'rgba(255,80,0,0)');
ctx.beginPath();
ctx.fillStyle = g;
ctx.arc(cx, cy, SUN_SIZE * 2.5, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.fillStyle = '#ffcc33';
ctx.arc(cx, cy, SUN_SIZE, 0, Math.PI * 2);
ctx.fill();
// core highlight
ctx.beginPath();
ctx.fillStyle = 'rgba(255,255,220,0.55)';
ctx.arc(cx - 4, cy - 4, SUN_SIZE * 0.35, 0, Math.PI * 2);
ctx.fill();
}
function drawPlanet(p, pos) {
// subtle glow
const glow = ctx.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, p.size * 2.2);
glow.addColorStop(0, p.color);
glow.addColorStop(0.4, p.color + '88');
glow.addColorStop(1, 'transparent');
ctx.beginPath();
ctx.fillStyle = glow;
ctx.arc(pos.x, pos.y, p.size * 2.2, 0, Math.PI * 2);
ctx.fill();
// Saturn ring behind
if (p.ring) {
ctx.save();
ctx.translate(pos.x, pos.y);
ctx.rotate(0.4);
ctx.scale(1, 0.35);
ctx.beginPath();
ctx.strokeStyle = 'rgba(210, 190, 140, 0.55)';
ctx.lineWidth = 2.5;
ctx.arc(0, 0, p.size * 1.9, 0, Math.PI * 2);
ctx.stroke();
ctx.beginPath();
ctx.strokeStyle = 'rgba(180, 160, 110, 0.35)';
ctx.lineWidth = 1.5;
ctx.arc(0, 0, p.size * 2.3, 0, Math.PI * 2);
ctx.stroke();
ctx.restore();
}
ctx.beginPath();
ctx.fillStyle = p.color;
ctx.arc(pos.x, pos.y, p.size, 0, Math.PI * 2);
ctx.fill();
// specular
ctx.beginPath();
ctx.fillStyle = 'rgba(255,255,255,0.35)';
ctx.arc(pos.x - p.size * 0.3, pos.y - p.size * 0.3, p.size * 0.35, 0, Math.PI * 2);
ctx.fill();
// Saturn ring in front (lower half)
if (p.ring) {
ctx.save();
ctx.translate(pos.x, pos.y);
ctx.rotate(0.4);
ctx.scale(1, 0.35);
ctx.beginPath();
ctx.strokeStyle = 'rgba(220, 200, 150, 0.5)';
ctx.lineWidth = 2.5;
ctx.arc(0, 0, p.size * 1.9, 0.05, Math.PI - 0.05);
ctx.stroke();
ctx.restore();
}
}
function hitTest(s) {
// Check sun
const ds = Math.hypot(mouse.x - cx, mouse.y - cy);
if (ds < SUN_SIZE + 4) return { name: 'Sun', x: cx, y: cy };
for (let i = planets.length - 1; i >= 0; i--) {
const p = planets[i];
const pos = planetPos(p, s);
const d = Math.hypot(mouse.x - pos.x, mouse.y - pos.y);
const hitR = Math.max(p.size + 6, 10);
if (d < hitR) return { name: p.name, x: pos.x, y: pos.y };
}
return null;
}
function frame(now) {
const dt = Math.min((now - last) / 1000, 0.1);
last = now;
if (!paused) t += dt * speedMul;
const s = scale();
ctx.clearRect(0, 0, W, H);
// deep space vignette
const vg = ctx.createRadialGradient(cx, cy, 0, cx, cy, Math.max(W, H) * 0.7);
vg.addColorStop(0, '#0a0a22');
vg.addColorStop(1, '#030308');
ctx.fillStyle = vg;
ctx.fillRect(0, 0, W, H);
drawStars();
for (const p of planets) drawOrbit(p, s);
drawSun();
const positions = [];
for (const p of planets) {
const pos = planetPos(p, s);
positions.push(pos);
drawPlanet(p, pos);
}
const hit = hitTest(s);
if (hit) {
tooltip.textContent = hit.name;
tooltip.style.opacity = '1';
let tx = hit.x + 14;
let ty = hit.y - 28;
if (tx + 80 > W) tx = hit.x - 80;
if (ty < 8) ty = hit.y + 18;
tooltip.style.left = tx + 'px';
tooltip.style.top = ty + 'px';
canvas.style.cursor = 'pointer';
} else {
tooltip.style.opacity = '0';
canvas.style.cursor = 'crosshair';
}
requestAnimationFrame(frame);
}
speedSlider.addEventListener('input', () => {
speedMul = parseFloat(speedSlider.value);
});
pauseBtn.addEventListener('click', () => {
paused = !paused;
pauseBtn.textContent = paused ? 'Resume' : 'Pause';
});
canvas.addEventListener('mousemove', (e) => {
mouse.x = e.clientX;
mouse.y = e.clientY;
});
canvas.addEventListener('mouseleave', () => {
mouse.x = -9999;
mouse.y = -9999;
});
window.addEventListener('resize', resize);
resize();
requestAnimationFrame(frame);
})();
</script>
</body>
</html>