This project is a JavaScript port of the classic galaxy screensaver,
a personal favorite for many from the XScreenSaver (xlockmore) collection of the 1990s.
Even today, it stands as a beautiful example of a simple 3D particle simulation that produces complex and aesthetically pleasing results. The animation showcases surprisingly realistic-looking galactic interactions, with recognizable features like tidal tails and mergers that emerge from simple physical laws.
While named "galaxy," the simulation more accurately models a system of massive central bodies (akin to super-massive black holes) whose gravitational forces influence a large number of surrounding, massless "stars."
The core of the simulation is a simplified n-body calculation:
- The gravitational pull of each galaxy's central mass on every single star in the simulation is calculated in each step.
- The gravitational forces between the central masses of the galaxies themselves are also calculated, causing the galaxies to orbit and interact with each other.
- To make the computation feasible, the gravitational interactions between individual stars are ignored.
The force is calculated using an inverse-square law, which is a direct application of Newton's Law of Universal Gravitation (
The simulation uses a numerical integration method to approximate the motion of celestial bodies over time. Specifically, it employs a Symplectic Euler integrator, a simple yet effective method that provides long-term stability for orbital simulations.
Unlike the standard Euler method, which can cause orbits to drift and gain energy over time, the Symplectic Euler method conserves energy much more accurately. This prevents stars from unnaturally spiraling out of their galaxies and ensures the simulation remains stable and realistic.
The update for each object (a star or a galaxy's central mass) happens in discrete time steps (Δt) and follows this sequence:
- 
Calculate Acceleration ( a): The simulation first calculates the total gravitational acceleration acting on an object. It sums up the acceleration vectors caused by every massive body in the system. The acceleration vector towards a single massMis given by:$$\vec{a} = G \frac{M}{r^3} \vec{r}$$ whereGis the gravitational constant,Mis the mass of the attracting body, andris the vector pointing from the object to the attracting body.
- 
Update Velocity ( v): The object's velocity is updated using the calculated acceleration and the time stepΔt:$$\vec{v}_{new} = \vec{v}_{old} + \vec{a} \cdot \Delta t$$ 
- 
Update Position ( p): Finally, the object's position is updated using this newly calculated velocity. This is the key step that defines the Symplectic Euler method and gives it its stability.$$\vec{p}_{new} = \vec{p}_{old} + \vec{v}_{new} \cdot \Delta t$$ 
This process is repeated for every object in every frame, creating the fluid and dynamic motion of the simulation.
The simulation's beautiful starting patterns are also rooted in physics:
- Galaxy Placement: Galaxies are initially "rewound" in time and given a slight sideways offset. This clever trick ensures they start off-screen and fly towards the center on paths that encourage near-misses and orbiting rather than direct, head-on collisions.
- Galaxy Shape: The classic spiral shape is formed by placing stars with an exponential decay in their height from the central plane, creating a thin disk with a central bulge.
- 
Orbital Velocity: Each star is given an initial velocity based on the formula for a stable circular orbit, $$v = \sqrt{\frac{GM}{r}}$$ , which makes the galaxies spin realistically from the very first frame.
The web component is a simple wrapper around the simulation that allows it to be used in a web page. It is a custom element that can be used like this:
<html>
    <head>
        <title>Galaxies</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script src="https://unpkg.com/@kajws/galaxy-js"></script>
    </head>
    <body>
        <h1>Galaxies</h1>
        <galaxy-simulation style="width: 600px; height: 400px;" >
    </body>
</html>The <galaxy-simulation> web component can be configured through the following properties, which can be set as HTML attributes on the <galaxy-simulation> tag.
- galaxy-count: The minimum number of galaxies to create. The actual number will be a random value between- galaxy-countand- max-galaxy-count. Defaults to- 3.
- max-galaxy-count: The maximum number of galaxies to create. Defaults to- 3.
- zoom: A number that adjusts the zoom level of the camera. Defaults to- 1.
- rotation: Controls the rotation of the view in degrees per frame. It can be set in two ways:- As a single number (e.g., rotation="0.2"): This sets the rotation speed around a randomly chosen axis.
- As a comma-separated string (e.g., rotation="0.1,0.2,0"): This sets the rotation speeds for the x, y, and z axes respectively.
 
- As a single number (e.g., 
- simulator: For advanced use cases, you can provide a custom JavaScript object to override the default physics engine. It must provide an- updateGalaxies(galaxies)method.
- galaxy-factory: Similarly, this allows providing a custom object to override galaxy creation. It must provide a- createRandomGalaxy()method.
In addition there is a <simple-galaxy-simulation> web component that is a bare-bones version of the simulation. This is a good starting point for anyone wanting to develop their own custom rendering of the simulation.
The simulation universe is a 3D space, which means we can render a fully 3D scene. A separate project @kajws/galaxy-vr shows how to use THREE.js and WebXR to render a VR-compatible 3D scene.
This code has a long and storied history, evolving through several platforms and authors. This port would not be possible without their original work. Full credit belongs to the creators and contributors who have maintained this program over the decades.
- Original Author: The simulation was first written by Uli Siegmund on an Amiga for EGS in Cluster.
- Early Ports: It was subsequently ported to C/Intuition by Harald Backert.
- X11 and Xlockmore: The version most people are familiar with was ported to X11 and incorporated into the xlockmorescreensaver collection by Hubert Feyrer in 1994. Hubert also added the initial color support.
There have also been previous ports to JavaScript, most notably rtoal/galaxies which provided a useful reference for this port.
However, unlike the other ports, I wanted this to be more 'simulator' than just a visualisation with the hope that it may also be useful for educational purposes. The physical objects, the vector and matrix operations, and the simulation itself are all separated in the hope that this can either be extended or used as a starting point for other projects.
Finally, I've also included a simple webcomponent as well as a more complex configurable version that can be dropped into any web page. If you are a teacher or a student using this project, I would love to hear from you!
In the spirit of the original, this JavaScript port is released under a permissive, non-restrictive license. The original C code included the following grant of permission:
Originally done by Uli Siegmund <[email protected]> on Amiga for EGS in Cluster
Port from Cluster/EGS to C/Intuition by Harald Backert
Port to X11 and incorporation into xlockmore by Hubert Feyrer <[email protected]>
Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. This file is provided AS IS with no warranties of any kind.
This port is a significant rewrite aimed at expanding the physics and making the code more accessible for modern educational purposes. To honor the permissive spirit of the original while providing legal clarity, this new work is released under the MIT License.
The MIT License is fully compatible with the original terms and is a widely recognized standard, ensuring the code remains easy to use, share, and build upon.