Orbital Mechanics
Build an N-body gravitational simulation with orbit trails, multiple planets, and velocity vectors.
Paste each step into main.js in the editor (or replace the file). Click Restart after pasting to re-run init.
Step 1: Single Planet Orbit
A planet orbits a star under Newtonian gravity.
// 1. State Setup — sun + planet with orbital velocity
if (!state.init) {
state.sun = { x: w * 0.5, y: h * 0.5, mass: 1000, r: 20, color: '#F59E0B' };
state.planet = {
x: w * 0.5 + 120,
y: h * 0.5,
vx: 0,
vy: -71,
mass: 1,
r: 8,
color: '#3B82F6',
};
state.G = 500;
state.trail = [];
state.init = true;
}
// 2. Physics Step — gravitational acceleration
const sun = state.sun;
const planet = state.planet;
const G = state.G;
const dx = planet.x - sun.x;
const dy = planet.y - sun.y;
const distSq = dx * dx + dy * dy;
const dist = Math.sqrt(distSq);
if (dist > 1) {
const F = G * sun.mass * planet.mass / distSq;
const ax = -F * dx / (dist * planet.mass);
const ay = -F * dy / (dist * planet.mass);
planet.vx += ax * dt;
planet.vy += ay * dt;
}
planet.x += planet.vx * dt;
planet.y += planet.vy * dt;
// Store trail
state.trail.push({ x: planet.x, y: planet.y });
if (state.trail.length > 300) state.trail.shift();
// 3. Render Return
return {
circles: [
{ cx: sun.x, cy: sun.y, r: sun.r + 10, color: 'rgba(245,158,11,0.15)', fill: 'rgba(245,158,11,0.05)' },
],
particles: [
{ x: sun.x, y: sun.y, r: sun.r, color: sun.color, label: 'Sun' },
{ x: planet.x, y: planet.y, r: planet.r, color: planet.color, label: 'Planet' },
],
vectors: [
{ x: planet.vx * 0.3, y: planet.vy * 0.3, ox: planet.x, oy: planet.y, color: '#22C55E', label: 'v' },
],
lines: state.trail.map((p, i) => ({
x1: state.trail[i - 1]?.x ?? p.x,
y1: state.trail[i - 1]?.y ?? p.y,
x2: p.x,
y2: p.y,
color: `rgba(59,130,246,${i / state.trail.length * 0.4})`,
width: 1,
})),
text: {
'Orbit radius': dist.toFixed(0) + ' px',
'Speed': Math.sqrt(planet.vx ** 2 + planet.vy ** 2).toFixed(1),
},
};📐 MATH CHECK Gravitational force: F = G * m1 * m2 / r² Acceleration: a = F / m (direction toward the attractor) The planet needs tangential velocity for a stable orbit. Too slow → falls in. Too fast → escapes.
Step 2: Multiple Planets
Extend the simulation to orbit multiple bodies around the same sun.
if (!state.init) {
state.sun = { x: w * 0.5, y: h * 0.5, mass: 1000, r: 20, color: '#F59E0B' };
state.planets = [
{ x: w * 0.5 + 100, y: h * 0.5, vx: 0, vy: -80, mass: 1, r: 6, color: '#3B82F6', name: 'Mercury' },
{ x: w * 0.5 + 160, y: h * 0.5, vx: 0, vy: -63, mass: 1.5, r: 8, color: '#EF4444', name: 'Venus' },
{ x: w * 0.5 + 230, y: h * 0.5, vx: 0, vy: -53, mass: 2, r: 10, color: '#22C55E', name: 'Earth' },
];
state.G = 500;
state.trails = state.planets.map(() => []);
state.init = true;
}
const sun = state.sun;
const G = state.G;
for (let i = 0; i < state.planets.length; i++) {
const p = state.planets[i];
const dx = p.x - sun.x;
const dy = p.y - sun.y;
const distSq = dx * dx + dy * dy;
const dist = Math.sqrt(distSq);
if (dist > 1) {
const F = G * sun.mass * p.mass / distSq;
p.vx -= F * dx / (dist * p.mass) * dt;
p.vy -= F * dy / (dist * p.mass) * dt;
}
p.x += p.vx * dt;
p.y += p.vy * dt;
state.trails[i].push({ x: p.x, y: p.y });
if (state.trails[i].length > 400) state.trails[i].shift();
}
return {
particles: [
{ x: sun.x, y: sun.y, r: sun.r, color: sun.color, label: 'Sun' },
...state.planets.map((p) => ({ x: p.x, y: p.y, r: p.r, color: p.color, label: p.name })),
],
lines: state.trails.flatMap((trail, pi) =>
trail.map((p, i) => ({
x1: trail[i - 1]?.x ?? p.x,
y1: trail[i - 1]?.y ?? p.y,
x2: p.x,
y2: p.y,
color: state.planets[pi].color,
width: 1,
dashed: i < trail.length - 1,
}))
),
text: {
'Planets': state.planets.length,
'G': G.toFixed(0),
},
};💡 PRO TIP The initial velocity for a circular orbit is
v = sqrt(G * M_sun / r). ForG = 500,M_sun = 1000,r = 160:v = sqrt(500 * 1000 / 160) ≈ 56. Use this to set stable starting velocities.
Step 3: Click to Add Planets
Use click interaction to spawn new planets dynamically.
// Add to the initialization block:
// state.nextColor = 0;
// state.colorPalette = ['#8B5CF6', '#EC4899', '#06B6D4', '#F59E0B', '#10B981'];
// After the physics loop, before the return:
if (state.clickX !== undefined) {
const dx = state.clickX - state.sun.x;
const dy = state.clickY - state.sun.y;
const dist = Math.sqrt(dx * dx + dy * dy);
const speed = Math.sqrt(G * state.sun.mass / Math.max(dist, 50));
// Tangential velocity (perpendicular to radius)
const color = state.colorPalette[state.nextColor % state.colorPalette.length];
state.planets.push({
x: state.clickX,
y: state.clickY,
vx: -dy / dist * speed,
vy: dx / dist * speed,
mass: 1,
r: 6,
color: color,
name: 'P' + (state.planets.length + 1),
});
state.trails.push([]);
state.nextColor++;
state.clickX = undefined;
state.clickY = undefined;
}Extending This Example
- Add planet-planet gravity (not just sun-planet) for true N-body dynamics
- Implement collision detection between planets
- Add a moon orbiting a planet (hierarchical orbits)
- Show orbital period by timing one full revolution