RUNS ✓2.5D ellipse + z-sortingSaturn ring, moon, labels
Complete and quietly sophisticated — planets pass in front of and behind the sun via depth sorting nobody asked for.
An animated 2D solar system: glowing sun, 6 planets with distinct colors/orbits/speeds, at least one moon, faint orbit rings, starfield, planet labels. Starts immediately, runs forever, NO user input.
Complete and quietly sophisticated — planets pass in front of and behind the sun via depth sorting nobody asked for.
Spec-complete plus an uninvited comet. Charm counts; so does the fact that everything ran this time.
Renders beautifully — then invents its own planetary nomenclature. "A solar system" is defensible; the solar system was expected.
Works, composes badly: a giant sun and a planetary conga line.
The overall runner-up ships a corrupted string literal — it even tried to .replace() its own typo at runtime. The parser got there first. Nobody is safe from Round 5.
Silent failure — the hardest kind to debug, the easiest to rank.
Second parse error of the day for the local coder. Long files, fragile endings.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Solar System</title>
<style>
html, body {
margin: 0;
padding: 0;
height: 100%;
background: #05070f;
overflow: hidden;
}
canvas {
display: block;
width: 100vw;
height: 100vh;
}
</style>
</head>
<body>
<canvas id="sky"></canvas>
<script>
(function () {
"use strict";
var canvas = document.getElementById("sky");
var ctx = canvas.getContext("2d");
var W = 0, H = 0, DPR = 1;
var cx = 0, cy = 0, scale = 1;
// ---- Planet definitions -------------------------------------------------
// orbit / radius values are in "design units", scaled to the viewport.
var planets = [
{ name: "Mercury", orbit: 70, radius: 4.5, speed: 1.60, color: "#b9b0a4", glow: "#e6dccd", angle: 0.4 },
{ name: "Venus", orbit: 105, radius: 7.0, speed: 1.15, color: "#e0b070", glow: "#ffdca8", angle: 2.1 },
{ name: "Terra", orbit: 148, radius: 7.6, speed: 0.90, color: "#4d8fd6", glow: "#9fd0ff", angle: 4.0,
moons: [ { name: "Luna", orbit: 20, radius: 2.4, speed: 4.2, color: "#cfd3da", angle: 1.0 } ] },
{ name: "Mars", orbit: 196, radius: 6.0, speed: 0.68, color: "#c9563a", glow: "#ff9a78", angle: 0.9,
moons: [ { name: "Phobos", orbit: 15, radius: 1.6, speed: 5.6, color: "#9b8d84", angle: 2.6 } ] },
{ name: "Jupiter", orbit: 268, radius: 16.0, speed: 0.40, color: "#cfa070", glow: "#ffd9a8", angle: 3.3, banded: true,
moons: [
{ name: "Io", orbit: 27, radius: 2.2, speed: 3.1, color: "#e8d48a", angle: 0.2 },
{ name: "Europa", orbit: 38, radius: 2.0, speed: 2.2, color: "#dfe6ee", angle: 3.5 }
] },
{ name: "Saturn", orbit: 348, radius: 13.0, speed: 0.28, color: "#e3cf9b", glow: "#fff0c4", angle: 5.5, ring: true,
moons: [ { name: "Titan", orbit: 30, radius: 2.6, speed: 2.4, color: "#d9a86a", angle: 5.0 } ] }
];
// ---- Starfield ----------------------------------------------------------
var stars = [];
function buildStars() {
var count = Math.round((W * H) / 5200);
if (count < 140) count = 140;
if (count > 900) count = 900;
stars = [];
for (var i = 0; i < count; i++) {
var r = Math.random();
stars.push({
x: Math.random() * W,
y: Math.random() * H,
r: 0.35 + r * r * 1.5,
base: 0.25 + Math.random() * 0.6,
amp: 0.10 + Math.random() * 0.35,
phase: Math.random() * Math.PI * 2,
rate: 0.4 + Math.random() * 1.6,
hue: Math.random() < 0.18 ? (Math.random() < 0.5 ? "#bcd6ff" : "#ffe2c4") : "#ffffff"
});
}
}
function resize() {
DPR = Math.min(window.devicePixelRatio || 1, 2);
W = window.innerWidth;
H = window.innerHeight;
canvas.width = Math.round(W * DPR);
canvas.height = Math.round(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;
// Fit the outermost orbit (plus label room) inside the shorter axis.
scale = Math.min(W, H) / 2 / 385;
buildStars();
}
window.addEventListener("resize", resize);
// ---- Drawing helpers ----------------------------------------------------
function drawStars(t) {
for (var i = 0; i < stars.length; i++) {
var s = stars[i];
var a = s.base + s.amp * Math.sin(t * s.rate + s.phase);
if (a < 0.04) a = 0.04;
ctx.globalAlpha = a;
ctx.fillStyle = s.hue;
ctx.beginPath();
ctx.arc(s.x, s.y, s.r, 0, Math.PI * 2);
ctx.fill();
}
ctx.globalAlpha = 1;
}
function drawSun(t) {
var r = 26 * scale;
var pulse = 1 + 0.045 * Math.sin(t * 1.3);
var coronaR = r * 7.5 * pulse;
var corona = ctx.createRadialGradient(cx, cy, r * 0.4, cx, cy, coronaR);
corona.addColorStop(0.00, "rgba(255, 236, 170, 0.55)");
corona.addColorStop(0.18, "rgba(255, 190, 80, 0.26)");
corona.addColorStop(0.45, "rgba(255, 140, 40, 0.10)");
corona.addColorStop(1.00, "rgba(255, 120, 20, 0)");
ctx.fillStyle = corona;
ctx.beginPath();
ctx.arc(cx, cy, coronaR, 0, Math.PI * 2);
ctx.fill();
var body = ctx.createRadialGradient(
cx - r * 0.25, cy - r * 0.25, r * 0.1,
cx, cy, r * pulse
);
body.addColorStop(0.0, "#fffdf2");
body.addColorStop(0.5, "#ffd76a");
body.addColorStop(1.0, "#ff9422");
ctx.fillStyle = body;
ctx.beginPath();
ctx.arc(cx, cy, r * pulse, 0, Math.PI * 2);
ctx.fill();
}
function drawOrbitRings() {
ctx.lineWidth = 1;
for (var i = 0; i < planets.length; i++) {
var R = planets[i].orbit * scale;
ctx.strokeStyle = "rgba(150, 180, 230, 0.13)";
ctx.beginPath();
ctx.arc(cx, cy, R, 0, Math.PI * 2);
ctx.stroke();
}
}
function drawSphere(x, y, r, color, glow) {
if (glow) {
var g = ctx.createRadialGradient(x, y, r * 0.8, x, y, r * 3.2);
g.addColorStop(0, hexToRgba(glow, 0.30));
g.addColorStop(1, hexToRgba(glow, 0));
ctx.fillStyle = g;
ctx.beginPath();
ctx.arc(x, y, r * 3.2, 0, Math.PI * 2);
ctx.fill();
}
// Light comes from the sun: offset the highlight toward the centre.
var dx = cx - x, dy = cy - y;
var d = Math.sqrt(dx * dx + dy * dy) || 1;
var lx = x + (dx / d) * r * 0.38;
var ly = y + (dy / d) * r * 0.38;
var grad = ctx.createRadialGradient(lx, ly, r * 0.08, x, y, r);
grad.addColorStop(0, lighten(color, 0.45));
grad.addColorStop(0.55, color);
grad.addColorStop(1, darken(color, 0.55));
ctx.fillStyle = grad;
ctx.beginPath();
ctx.arc(x, y, r, 0, Math.PI * 2);
ctx.fill();
}
function drawBands(x, y, r, color) {
ctx.save();
ctx.beginPath();
ctx.arc(x, y, r, 0, Math.PI * 2);
ctx.clip();
ctx.globalAlpha = 0.22;
for (var i = -2; i <= 2; i++) {
ctx.fillStyle = (i % 2 === 0) ? darken(color, 0.4) : lighten(color, 0.35);
ctx.fillRect(x - r, y + i * r * 0.36 - r * 0.11, r * 2, r * 0.22);
}
ctx.restore();
ctx.globalAlpha = 1;
}
function drawSaturnRing(x, y, r, tilt) {
ctx.save();
ctx.translate(x, y);
ctx.rotate(-0.42);
ctx.scale(1, 0.30);
ctx.lineWidth = Math.max(1.2, r * 0.20);
ctx.strokeStyle = "rgba(240, 222, 178, 0.75)";
ctx.beginPath();
ctx.arc(0, 0, r * 1.75, 0, Math.PI * 2);
ctx.stroke();
ctx.lineWidth = Math.max(0.8, r * 0.10);
ctx.strokeStyle = "rgba(255, 244, 214, 0.45)";
ctx.beginPath();
ctx.arc(0, 0, r * 2.15, 0, Math.PI * 2);
ctx.stroke();
ctx.restore();
}
function drawLabel(text, x, y, r, color) {
ctx.font = "500 " + Math.max(9, Math.round(11 * Math.min(1.15, scale * 1.05))) + "px ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif";
ctx.textAlign = "center";
ctx.textBaseline = "top";
var ly = y + r + 6;
ctx.fillStyle = "rgba(0, 0, 0, 0.55)";
ctx.fillText(text, x + 1, ly + 1);
ctx.fillStyle = color;
ctx.fillText(text, x, ly);
}
// ---- Small colour utilities --------------------------------------------
function parseHex(hex) {
var h = hex.replace("#", "");
if (h.length === 3) h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2];
return [parseInt(h.substr(0, 2), 16), parseInt(h.substr(2, 2), 16), parseInt(h.substr(4, 2), 16)];
}
function hexToRgba(hex, a) {
var c = parseHex(hex);
return "rgba(" + c[0] + "," + c[1] + "," + c[2] + "," + a + ")";
}
function lighten(hex, amt) {
var c = parseHex(hex);
return "rgb(" +
Math.round(c[0] + (255 - c[0]) * amt) + "," +
Math.round(c[1] + (255 - c[1]) * amt) + "," +
Math.round(c[2] + (255 - c[2]) * amt) + ")";
}
function darken(hex, amt) {
var c = parseHex(hex);
return "rgb(" +
Math.round(c[0] * (1 - amt)) + "," +
Math.round(c[1] * (1 - amt)) + "," +
Math.round(c[2] * (1 - amt)) + ")";
}
// ---- Main loop ----------------------------------------------------------
var last = 0;
var t = 0;
function frame(now) {
if (!last) last = now;
var dt = (now - last) / 1000;
last = now;
if (dt > 0.1) dt = 0.1; // clamp after tab-switches
t += dt;
ctx.fillStyle = "#05070f";
ctx.fillRect(0, 0, W, H);
// subtle galactic haze
var haze = ctx.createRadialGradient(cx, cy, 0, cx, cy, Math.max(W, H) * 0.7);
haze.addColorStop(0, "rgba(30, 40, 80, 0.35)");
haze.addColorStop(1, "rgba(5, 7, 15, 0)");
ctx.fillStyle = haze;
ctx.fillRect(0, 0, W, H);
drawStars(t);
drawOrbitRings();
drawSun(t);
for (var i = 0; i < planets.length; i++) {
var p = planets[i];
p.angle += p.speed * dt * 0.6;
var R = p.orbit * scale;
var pr = Math.max(2, p.radius * scale);
var px = cx + Math.cos(p.angle) * R;
var py = cy + Math.sin(p.angle) * R * 0.94; // gentle perspective squash
if (p.ring) drawSaturnRing(px, py, pr);
drawSphere(px, py, pr, p.color, p.glow);
if (p.banded) drawBands(px, py, pr, p.color);
if (p.ring) {
// front half of the ring, drawn over the body
ctx.save();
ctx.translate(px, py);
ctx.rotate(-0.42);
ctx.scale(1, 0.30);
ctx.lineWidth = Math.max(1.2, pr * 0.20);
ctx.strokeStyle = "rgba(240, 222, 178, 0.75)";
ctx.beginPath();
ctx.arc(0, 0, pr * 1.75, 0.15, Math.PI - 0.15);
ctx.stroke();
ctx.restore();
}
if (p.moons) {
for (var m = 0; m < p.moons.length; m++) {
var mo = p.moons[m];
mo.angle += mo.speed * dt * 0.6;
var mr = Math.max(1.2, mo.radius * scale);
var moR = Math.max(pr + mr + 3, mo.orbit * scale);
var mx = px + Math.cos(mo.angle) * moR;
var my = py + Math.sin(mo.angle) * moR * 0.6;
ctx.strokeStyle = "rgba(170, 195, 235, 0.12)";
ctx.lineWidth = 1;
ctx.save();
ctx.translate(px, py);
ctx.scale(1, 0.6);
ctx.beginPath();
ctx.arc(0, 0, moR, 0, Math.PI * 2);
ctx.stroke();
ctx.restore();
drawSphere(mx, my, mr, mo.color, null);
}
}
drawLabel(p.name, px, py, pr + (p.ring ? pr * 1.1 : 0), "rgba(214, 228, 255, 0.85)");
}
requestAnimationFrame(frame);
}
resize();
requestAnimationFrame(frame);
})();
</script>
</body>
</html>