Following this excellent tutorial on making a procedural solar system, and so far it's coming along nicely. My only problem with it is that the orbit speeds aren't as accurate as I'd like. I want the orbital periods to follow Kepler's Third Law. Only problem is that I don't know how to get it to work like that.
Here is the code related to the orbits. How do I get it to work how I want it to?
function drawPlanet(size, distance) {
const hue = randomInt(0, 360);
const saturation = randomInt(70, 100);
const lightness = randomInt(50, 70);
const color = `hsl(${hue}, ${saturation}%, ${lightness}%)`;
return `
<circle
cx="${width / 2 + distance}"
cy="${height / 2}"
r="${size}"
fill="${color}"
class="planet"
style="
--start-rotation:${randomInt(0, 0)}deg;
--rotation-speed:${distance * randomInt(40, 70)}ms;
"
/>
`;
}
Author of the tutorial here. I'm glad you enjoyed it!
This is a good question. I'm not an expert on astronomy or mathematics, but I'll do my best to answer.
Setting Speed Via Custom Property
The first thing to know is that the rotation speed is controlled by the --rotation-speed
custom property. We'll be updating that value from distance * randomInt(40, 70)
to a more accurate value. Right now this is set in milliseconds.
So we need to determine what value to set that custom property to.
Non-scientific caveats
I'm going to be taking a couple short-cuts in my math here:
Determining a more accurate speed
With those caveats in mind, let's find a formula we can use. I found this helpful article which describes how to calculate circular orbital speeds: https://www.nagwa.com/en/explainers/142168516704/
Here's a JS approximation of the formula they outline in the article:
function velocity(gravitationalConstant, starMass, orbitDistance) {
return Math.sqrt((gravitationalConstant * starMass) / orbitDistance);
}
This just gets us the velocity though. To set the animation speed we need to know how long the entire orbit will take. We could figure this out by determining the circumference of the orbit, and comparing that to the velocity. Here's how we can get the orbit circumference:
function circumference(radius) {
const diameter = radius * 2;
return Math.PI * diameter;
}
We can do something similar in the drawPlanet
function:
function drawPlanet(size, distance) {
const hue = randomInt(0, 360);
const saturation = randomInt(70, 100);
const lightness = randomInt(50, 70);
const color = `hsl(${hue}, ${saturation}%, ${lightness}%)`;
// Since we're only worried about _relative_ orbit speeds we can set
// this to whatever we like
const gravitationalConstant = 1;
// We could pass the size of the star into our function and use
// that to generate a star mass if we'd like.
// Then the orbit speeds would differ based on the star size.
// For now, let's hard code it.
const starMass = 1;
// We already know the orbit distance, so we have the radius.
const orbitRadius = distance;
// Apply our formulas
const velocity = Math.sqrt((gravitationalConstant * starMass) / orbitRadius);
const orbitCircumference = Math.PI * orbitRadius * 2;
// Compare the velocity to the orbit circumference to find the duration
// of a single orbit
const orbitDuration = orbitCircumference / velocity;
return `
<circle
cx="${width / 2 + distance}"
cy="${height / 2}"
r="${size}"
fill="${color}"
class="planet"
style="
--start-rotation:${randomInt(0, 0)}deg;
--rotation-speed:${orbitDuration}ms;
"
/>
`;
}
Here's a codepen showing it in action. As I mentioned above, I'm no astronomer, but I think this should point you in the right direction. Happy coding!