Gravity Animation


Introduction

While I was designing the new version of my personal website. I was struggling with the fact that most pages looked rather boring. In order to counter that, I planned to do a header animation. Furthermore, it was also a nice opportunity to learn to work with HTML5 Canvas. Of course, the animation needed to be techy / sciency, minimalistic, and naturally, something I could look at for several minutes without getting bored. The idea for this gravity graph animation is actually based on a discussion with a friend on a hypothetical game which involved the gravitational pull of bodies and spaceships. However, the difference here is, that the animation is actually a gravitational simulation of a certain amount of particles which only interact from a certain distance (this interaction is visualized by an edge, i.e., a line, between two particles). The limitation of the interaction distance is not a physical aspect, it is only put into place because of 'artistic' reasons, else the Canvas would have been totally clogged up with edges.

Implementation

In order to start discussing our implementation, we first need a data-structure in which we can easily handle the properties of all point-masses. First of all, we require all point masses to be initialized uniformily (random) across the canvas with random speed vectors. Second, if a point mass leaves the Canvas, make sure a new point mass is randomly generated from one of the four sides (top, right, bottom, left), with speed vectors which are appropriate to the side the point-mass got generated on. One can imagine that it is rather pointless to generate a point-mass which is generated on the right side, with a speed vector which is pointing to the right. This would result in the immediate removal of the point mass, and the generation of a new one. Thus, making sure that our speed vector is correctly initialized not only prevents this behavior, but will also save computational complexity. The code snippet below shows the initialization of a point-mass when it is being generated at one of the sides.

 var Particle = function(id) {
          this.id = id;
          this.size = randomInteger(minParticleSize, maxParticleSize);
          this.acceleration = [0, 0];
          this.location = getRandomInitialLocationFromSide();
          this.speed = getRandomSpeedVector(this.location[2]);
          this.neighbors = [];
};
      
        

Now we have a way to generate point-masses on the go, we will continue by describing how the gravitational interactions are handled. For two point-masses we can use the well-known Newton's law of universal gravitation, as described in Equation 1.

F=Gm1m2r2\displaystyle F = \frac{G m_1 m_2}{r^2}(1)

However, since we are dealing with multiple bodies in two dimensions, we will have to calculate the net-force of all the interacting point-masses. One way of doing this is to compute the acceleration of the point-mass in the direction of the net-force. In order to do this, we iterate through all interacting (nearby) point-masses and execute the following steps: first, we compute the force as described in Equation 1 where m1m_1 is the point-mass of interest. Next, we compute the acceleration since we know the force FF, and the mass of the point-masses, m1m_1 and m2m_2. Afterwards, we obtain the angle of the force between both point-masses by applying the atan2 function to the difference in coordinates between the point-masses. Finally, the acceleration-vector is decomposed, and the acceleration in every dimension is added to the net-acceleration vector as shown in Equation 2 and Equation-3.

ax=i=1naicos(θi)\displaystyle a_x = \sum_{i = 1}^{n} a_i \cdot \cos(\theta_i)(2)
ay=i=1naisin(θi)\displaystyle a_y = \sum_{i = 1}^{n} a_i \cdot \sin(\theta_i)(3)

However, since we are not implementing collisions, it is possible that multiple point-masses are passing each other very close to the center. This will cause some numerical errors (sometimes even a divide by 0), which in turn result in very high accelerations (animation looks kinda messy) of some point-masses. In order to prevent this issue, we added a minimum distance between two point-masses.