javascriptorbital-mechanics

Getting a planet's speed to scale with the inverse square of it's distance?


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;
      "
    />
  `;
}


Solution

  • 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!