Scripting workshop (DPAC 2025)
Hint
This tutorial is designed to be followed with Gaia Sky 3.6.6+!
This page contains the notes of the Gaia Sky scripting workshop in the DPAC consortium meeting 2025 in Cambridge, UK (April 2025).
The main aim of this tutorial is to provide a general understanding of the scripting system in Gaia Sky, and to train the participants in the creation of scripts with the main aim to render videos.
Presentation (slides): Find the accompanying presentation for this tutorial here.
The topics covered in this tutorial are the following:
Gaia Sky introduction (see presentation).
Dataset manager.
Controls, movement, selection.
User interface.
Video tools (frame output, camera paths, keyframes).
Scripting workshop:
Estimated duration: 2.5 hours
Before starting…
Have a look at our quick start guide to learn the basic usage of Gaia Sky. This guide covers the first few slides of the presentation and more.
Scripting workshop
This is the start of the hands-on session.
Gaia Sky exposes an API that is accessible through Python (via Py4j) or through HTTP over a network (using the REST API HTTP server).
1. Installing the environment
In this tutorial, we focus on the writing of Python scripts that tap into the Gaia Sky API. You will need:
Python 3 to run the scripts.
Git to get the code.
On Linux, Git is probably already installed.
On macOS, use
brew install git
.On Windows, use Git for windows.
Pipenv to manage the environment.
You can install pipenv`
with pip
, which already comes with Python:
pip install --user pipenv
Now, get the workshop project from git:
git clone https://codeberg.org/gaiasky/gaiasky-workshop2025.git
cd gaiasky-workshop2025
Now, you can enter the virtual environment and install the dependencies.
pipenv shell
pipenv install
This should install numpy
and py4j
.
If you don’t have git
installed, you can just download the gaiasky-script.py
file and install the dependencies.
# Either one or the other
pipenv install py4j numpy
pipenv install -r requirements.txt
Once this is ready, you can run a script with the Python 3 interpreter in your virtual environment.
Hint
Before running a script, make sure that an instance of Gaia Sky is running in the same computer. Otherwise, the script will fail.
In order to run the script gaiasky-script.py
, you would run this in a terminal:
python3 gaiasky-script.py
2. API
Here is the full documentation of the API.
3. Our first script
You can either start with gaiasky-script-commented.py
and uncomment the required lines, or you can start with an empty file and copy-paste the code. We’ll assume you start with an empty file.
We’ll start simple. Copy paste the following code in a file named my-script.py
within your workshop
directory.
1 from py4j.clientserver import ClientServer, JavaParameters
2
3 # Create the gateway to connect to the Gaia Sky instance.
4 gateway = ClientServer(java_parameters=JavaParameters(auto_convert=True))
5 # gs is our entry point to call API methods.
6 gs = gateway.entry_point
7
8 # We just go to Mars.
9 gs.goToObject("Mars")
10
11 # Terminate the connection and exit.
12 gateway.shutdown()
This is a very simple script that moves the camera to Mars and stops. You can test it by running python my-script.py
. Remember that you need to open Gaia Sky first!
4. Orbiting around
Now we want our camera to go to Mars, and to orbit around for a while. To achieve this, we can edit our my-script.py
so that it looks like this:
1 from py4j.clientserver import ClientServer, JavaParameters
2
3 # Create the gateway to connect to the Gaia Sky instance.
4 gateway = ClientServer(java_parameters=JavaParameters(auto_convert=True))
5 # gs is our entry point to call API methods.
6 gs = gateway.entry_point
7
8 # We just go to Mars.
9 gs.goToObject("Mars")
10
11 # Make sure the camera is in cinematic mode so that it keeps its momentum.
12 gs.setCinematicCamera(True)
13 # Set the orbiting speed of the camera in focus mode (in [0,100]).
14 gs.setCameraRotationSpeed(40)
15
16 # Add a gentle push in X. First parameter is deltaX, second is deltaY.
17 gs.cameraRotate(-0.4, 0.0)
18
19 # Now we wait for 5 seconds, and stop the camera.
20 gs.sleep(5)
21 gs.cameraStop()
22
23 # Terminate the connection and exit.
24 gateway.shutdown()
Run again and see the result.
5. Backing up and restoring settings
You may have noted that after the previous script finishes, the camera is left in cinematic mode, even if the script started with the camera in non-cinematic mode. This is because settings modified in scripts are not rolled back when the scripts end. To solve this, you can backup and restore the settings at the beginning and end of your scripts:
backupSettings()
– backs up the current settings in the settings stack [link].restoreSettings()
– restores the most recent backup from the top of the settings stack [link].
To try it in our script, edit it like this:
1 from py4j.clientserver import ClientServer, JavaParameters
2
3 # Create the gateway to connect to the Gaia Sky instance.
4 gateway = ClientServer(java_parameters=JavaParameters(auto_convert=True))
5 # gs is our entry point to call API methods.
6 gs = gateway.entry_point
7
8 # Back up the current settings.
9 gs.backupSettings()
10
11 # We just go to Mars.
12 gs.goToObject("Mars")
13
14 # Make sure the camera is in cinematic mode so that it keeps its momentum.
15 gs.setCinematicCamera(True)
16 # Set the orbiting speed of the camera in focus mode (in [0,100]).
17 gs.setCameraRotationSpeed(40)
18
19 # Add a gentle push in X. First parameter is deltaX, second is deltaY.
20 gs.cameraRotate(-0.4, 0.0)
21
22 # Now we wait for 5 seconds, and stop the camera.
23 gs.sleep(5)
24 gs.cameraStop()
25
26 # Restore settings before exit.
27 gs.restoreSettings()
28
29 # Terminate the connection and exit.
30 gateway.shutdown()
Run it, and you should see the camera back in non-cinematic mode at the end (if this is how it started!).
An unfortunate side effect of restoreSettings()
is that the camera is put in free mode after
the call.
6. Playing with time
In Gaia Sky, you can also start and stop time so that satellites, planets, and stars move. The most important API calls that manipulate time are:
startSimulationTime()
– [link].stopSimulationTime()
– [link].setTimeWarp(double factor)
– [link].Set the current time (here and here), and get the current time (here and here).
setTimeWarp(double)
sets the pace at which time passes. Set it to 1 to use real world time speed. Increase it to make time run faster. start-
and stopSimulationTime()
start and stop the time respectively. Let’s edit our script to have a look at the rotation of Mars.
Docs
See the time pane section in the user manual.
1 from py4j.clientserver import ClientServer, JavaParameters
2
3 # Create the gateway to connect to the Gaia Sky instance.
4 gateway = ClientServer(java_parameters=JavaParameters(auto_convert=True))
5 # gs is our entry point to call API methods.
6 gs = gateway.entry_point
7
8 # Back up the current settings.
9 gs.backupSettings()
10
11 # We just go to Mars.
12 gs.goToObject("Mars")
13
14 # Make sure the camera is in cinematic mode so that it keeps its momentum.
15 gs.setCinematicCamera(True)
16 # Set the orbiting speed of the camera in focus mode (in [0,100]).
17 gs.setCameraRotationSpeed(40)
18
19 # Add a gentle push in X. First parameter is deltaX, second is deltaY.
20 gs.cameraRotate(-0.4, 0.0)
21
22 # Now we wait for 5 seconds, and stop the camera.
23 gs.sleep(5)
24 gs.cameraStop()
25
26 # Time pace is 800x faster than normally.
27 gs.setTimeWarp(800.0)
28 # Start time.
29 gs.startSimulationTime()
30 gs.sleep(8)
31 # Stop time.
32 gs.stopSimulationTime()
33
34 # Restore settings before exit.
35 gs.restoreSettings()
36
37 # Terminate the connection and exit.
38 gateway.shutdown()
Why always different?
You may have noted that the outcome is different every time you run the script. This is because we are not taking into account the initial state of Gaia Sky. In this case, the most important factors that play a role in how the script develops as it runs are:
Starting position and orientation of our camera.
Starting time.
As we’ll see later, those are not the only factors. But they are indeed the most important. Let’s control them by setting an initial camera location and initial time.
To achieve this, first we need to set a starting position (and orientation!) for our camera. The camera orientation is defined by two vectors, the direction vector (where the camera looks), and the up vector. To capture the position and orientation of the camera at any time, we can use these calls:
getCameraPosition(string units)
– returns the camera position vector in the given units [here]. If no parameters are passed, the position is given in Km. There is a test script here that prints out the camera position in various units and orientation.getCameraDirection()
– returns the direction unit vector of our current camera [here].getCameraUp()
– returns the up vector of our current camera [here].
We can set the state with the analogous setCamera[Position|Direction|Up]()
methods.
For the initial time, we can just set it like this:
setSimulationTime(int year, int month, int day, int hour, int min, int sec, int millisec)
– [here].
We use these methods to capture the following initial state:
Cam pos (km): [7765514.7261459418, 3367392.3812921559, -148604366.1028463840]
Cam direction: [0.7297110211, 0.3130747529, -0.6078700723]
Cam up: [-0.2821898299, 0.9476653108, 0.1493296977]
Time: 17 Mar 2025, 10:39:13 UTC
Now, we can initialize the state of our script:
1 from py4j.clientserver import ClientServer, JavaParameters
2
3 # Create the gateway to connect to the Gaia Sky instance.
4 gateway = ClientServer(java_parameters=JavaParameters(auto_convert=True))
5 # gs is our entry point to call API methods.
6 gs = gateway.entry_point
7
8 # Back up the current settings.
9 gs.backupSettings()
10
11 # Initialize state with position and time.
12 gs.setCameraFree()
13 gs.setSimulationTime(2025, 3, 17, 10, 39, 13, 0)
14 gs.setCameraPosition([7765514.7261459418, 3367392.3812921559, -148604366.1028463840])
15 gs.setCameraDirection([0.7297110211, 0.3130747529, -0.6078700723])
16 gs.setCameraUp([-0.2821898299, 0.9476653108, 0.1493296977])
17 gs.sleep(3)
18
19 # We just go to Mars.
20 gs.goToObject("Mars")
21
22 # Make sure the camera is in cinematic mode so that it keeps its momentum.
23 gs.setCinematicCamera(True)
24 # Set the orbiting speed of the camera in focus mode (in [0,100]).
25 gs.setCameraRotationSpeed(40)
26
27 # Add a gentle push in X. First parameter is deltaX, second is deltaY.
28 gs.cameraRotate(-0.4, 0.0)
29
30 # Now we wait for 5 seconds, and stop the camera.
31 gs.sleep(5)
32 gs.cameraStop()
33
34 # Time pace is 800x faster than normally.
35 gs.setTimeWarp(800.0)
36 # Start time.
37 gs.startSimulationTime()
38 gs.sleep(8)
39 # Stop time.
40 gs.stopSimulationTime()
41
42 # Restore settings before exit.
43 gs.restoreSettings()
44
45 # Terminate the connection and exit.
46 gateway.shutdown()
Run the script multiple times, and it should now always start from the same position, orientation and time.
There are other things that play a role in how a script runs. Some of the calls we are using (like goToObject()
) implement a lot of functionality in the background for us, and they use the current camera speed settings to move the camera around. It is possible to fully control the camera state frame-by-frame, but this requires some more advanced techniques that we’ll see later on. However, we offer some middle-ground which enables setting-up smooth camera (position, orientation) and time transitions.
7. Creating transitions
The family of API calls that starts with cameraTransition(pos, dir, up, seconds)
(here) enables the definition of smooth transitions that use different mapping functions (logistic sigmoid, logit) from the current state to a user-defined final camera position, camera orientation, or time. The full documentation for these transition methods is here.
If, after the end of the current script, we want to return to the Earth and do it with more granular control, we can use transitions.
1 from py4j.clientserver import ClientServer, JavaParameters
2
3 # Create the gateway to connect to the Gaia Sky instance.
4 gateway = ClientServer(java_parameters=JavaParameters(auto_convert=True))
5 # gs is our entry point to call API methods.
6 gs = gateway.entry_point
7
8 # Back up the current settings.
9 gs.backupSettings()
10
11 # Initialize state with position and time.
12 gs.setCameraFree()
13 gs.setSimulationTime(2025, 3, 17, 10, 39, 13, 0)
14 gs.setCameraPosition([7765514.7261459418, 3367392.3812921559, -148604366.1028463840])
15 gs.setCameraDirection([0.7297110211, 0.3130747529, -0.6078700723])
16 gs.setCameraUp([-0.2821898299, 0.9476653108, 0.1493296977])
17 gs.sleep(3)
18
19 # We just go to Mars.
20 gs.goToObject("Mars")
21
22 # Make sure the camera is in cinematic mode so that it keeps its momentum.
23 gs.setCinematicCamera(True)
24 # Set the orbiting speed of the camera in focus mode (in [0,100]).
25 gs.setCameraRotationSpeed(40)
26
27 # Add a gentle push in X. First parameter is deltaX, second is deltaY.
28 gs.cameraRotate(-0.4, 0.0)
29
30 # Now we wait for 5 seconds, and stop the camera.
31 gs.sleep(5)
32 gs.cameraStop()
33
34 # Time pace is 800x faster than normally.
35 gs.setTimeWarp(800.0)
36 # Start time.
37 gs.startSimulationTime()
38 gs.sleep(8)
39 # Stop time.
40 gs.stopSimulationTime()
41
42 # Back to Earth. We first need to set the camera free,
43 # as we are using a transition.
44 gs.setCameraFree()
45 gs.cameraTransition([7.6099302829, 3.3000312627, -148.6345636443], # pos
46 "internal", # units
47 [-0.8321802084, -0.2822031396, 0.4765726385], # dir
48 [-0.3583614028, 0.9301845136, -0.0749524287], # up
49 8.0, # duration (pos)
50 "logisticsigmoid", # mapping function (pos)
51 30.0, # smoothing factor (pos)
52 4.0, # duration (orient)
53 "logisticsigmoid", # mapping function (orient)
54 12.0, # smoothing factor (orient)
55 True) # sync
56
57 # Restore settings before exit.
58 gs.restoreSettings()
59
60 # Terminate the connection and exit.
61 gateway.shutdown()
Doing camera (and time!) motions like this is a bit more involved, but it typically results in much better looking scripts due to the smooth movement produced by the mapping functions. Keep in mind that you need to capture the final camera state with get-cam-state.py.
Docs
See the transitions section in the user manual.
8. Component visibility
Objects in Gaia Sky are organized into types, which we call component types. These component types can be hidden and shown all at once. Examples of component types are planets, moons, stars, grids, or labels (see full list here).
While scripting, we can toggle types on and off with:
setComponentTypeVisibility(String key, boolean visible)
– enables or disables (True
orFalse
) the component type identified with the givenkey
. The key is in the form"element.planets"
,"element.stars"
, etc. See call here.
Let’s modify our script so that we mute the labels half-way through.
1 from py4j.clientserver import ClientServer, JavaParameters
2
3 # Create the gateway to connect to the Gaia Sky instance.
4 gateway = ClientServer(java_parameters=JavaParameters(auto_convert=True))
5 # gs is our entry point to call API methods.
6 gs = gateway.entry_point
7
8 # Back up the current settings.
9 gs.backupSettings()
10
11 # Initialize state with position and time.
12 gs.setCameraFree()
13 gs.setSimulationTime(2025, 3, 17, 10, 39, 13, 0)
14 gs.setCameraPosition([7765514.7261459418, 3367392.3812921559, -148604366.1028463840])
15 gs.setCameraDirection([0.7297110211, 0.3130747529, -0.6078700723])
16 gs.setCameraUp([-0.2821898299, 0.9476653108, 0.1493296977])
17 gs.sleep(3)
18
19 # We just go to Mars.
20 gs.goToObject("Mars")
21
22 # Make sure the camera is in cinematic mode so that it keeps its momentum.
23 gs.setCinematicCamera(True)
24 # Set the orbiting speed of the camera in focus mode (in [0,100]).
25 gs.setCameraRotationSpeed(40)
26
27 # Add a gentle push in X. First parameter is deltaX, second is deltaY.
28 gs.cameraRotate(-0.4, 0.0)
29
30 # Disable labels.
31 gs.setComponentTypeVisibility("element.labels", False)
32
33 # Now we wait for 5 seconds, and stop the camera.
34 gs.sleep(5)
35 gs.cameraStop()
36
37 # Time pace is 800x faster than normally.
38 gs.setTimeWarp(800.0)
39 # Start time.
40 gs.startSimulationTime()
41 gs.sleep(8)
42 # Stop time.
43 gs.stopSimulationTime()
44
45 # Back to Earth. We first need to set the camera free,
46 # as we are using a transition.
47 gs.setCameraFree()
48 gs.cameraTransition([7.6099302829, 3.3000312627, -148.6345636443], # pos
49 "internal", # units
50 [-0.8321802084, -0.2822031396, 0.4765726385], # dir
51 [-0.3583614028, 0.9301845136, -0.0749524287], # up
52 8.0, # duration (pos)
53 "logisticsigmoid", # mapping function (pos)
54 30.0, # smoothing factor (pos)
55 4.0, # duration (orient)
56 "logisticsigmoid", # mapping function (orient)
57 12.0, # smoothing factor (orient)
58 True) # sync
59
60 # Restore settings before exit.
61 gs.restoreSettings()
62
63 # Terminate the connection and exit.
64 gateway.shutdown()
As you see, now labels disappear when the camera starts to orbit Mars. Note that after the script ends, labels are re-enabled due to the call to gs.restoreSettings()
!
Hint
You can enable a representation of star velocities as 3D vectors with
"element.velocityvectors"
! The length, number, and color map of those vectors can be tuned with the family of callssetProperMotions[...]()
(see here).
Docs
See the component types section in the user manual.
9. Camera paths
Gaia Sky includes a feature to record and play back camera paths. This comes in handy if we want to showcase a certain itinerary through a dataset, for example.
Recording a camera path – The system will capture the camera state at every frame and save it into a .gsc
(for Gaia Sky camera) file. We can start a recording by clicking on the icon in the camera pane of the control panel. Once the recording mode is active, the icon will turn red
. Click on it again in order to stop recording and save the camera file to disk with an auto-generated file name (default location is
$GS_DATA/camera
(see the folders section in the Gaia Sky documentation).
Playing a camera path – In order to playback a previously recorded .gsc
camera file, click on the icon and select the desired camera path. The recording will start immediately.
Tip
Mind the FPS! The camera recording system stores the position of the camera for every frame! It is important that recording and playback are done with the same (stable) frame rate. To set the target recording frame rate, edit the “Target FPS” field in the camcorder settings of the preferences window. That will make sure the camera path is using the right frame rate. In order to play back the camera file at the right frame rate, we can edit the “Maximum frame rate” input in the graphics settings of the preferences window.

Location of the controls of the camcorder in Gaia Sky UI.
Of course, you can also instruct your scripts to start and stop recording a camera path. These are the relevant methods:
startRecordingCameraPath(String fileName)
– starts recording a camera path file that will be saved with the given name [here].stopRecordingCameraPath()
– stops the current recording (if any) and saves it as a file [here].playCameraPath(String fileName)
– plays the given camera path file (absolute path) [here].
Camera paths are totally deterministic, as they save the full state (camera position and direction, time) at every frame. They have a specific frame rate.
Docs
See the camera paths section in the user manual.
10. Frame output system
Here we learn about the frame output system to produce high-quality videos. In order to create high-quality videos, Gaia Sky offers the possibility to export every single still frame to an image file using the frame output subsystem. The resolution of these still frames can be set independently of the current screen resolution.
We can start the frame output system by pressing F6. Once active, the system starts saving each still frame to disk (frame rate goes down, most probably). The save location of the still frame images is, by default, $GS_DATA/frames/[prefix]_[num].jpg
, where [prefix]
is an arbitrary string that can be defined in the preferences. The save location, mode (simple or advanced), and the resolution can also be defined in the preferences.

The configuration screen for the frame output system
Gaia Sky offers some calls to start, stop, and configure the frame output system:
setFrameOutput(boolean active)
– start and stop the frame output system [here].configureFrameOutput(int width, int height, double fps, String directory, String namePrefix)
– configure the frame output system with the resolution of the frames, the frame rate, the output directory, and the prefix to use for the image files [here].
Once we have the still frame images, we can convert them to a video using ffmpeg
or any other encoding software. You can convert the frames into a video with:
ffmpeg -framerate 30 -start_number [start_img_num] -i [prefix]%05d.jpg -vframes [num_images] -s 1280x720 -c:v libx264 -pix_fmt yuv420p -crf 23 -r 30 [out_video_filename].mp4
The input and output framerate (
-framerate
,-r
) must match that defined in Gaia Sky.The resolution (
-s
) must be the resolution of the still frames.Use
-crf
to change the quality, in [0-51]. Lower numbers lead to better quality videos, with 0 being lossless. 23 is a good default.
Additional information on how to convert the still frames to a video can be found in the capturing videos section of the Gaia Sky user manual.
Alternatively, we can use OBS to record the script directly. In this case, since we are capturing the window of our OS, we are limited by the resolution of our display.
Docs
See the frame output section in the user manual.
11. Advanced: syncing with main thread
If there’s still time, here we learn how to park runnables that run in sync with the main update-render cycle in Gaia Sky.
Let’s suppose we want to count the number of frames during the execution of our script, and print the number at the end. To that effect, we need a piece of code that runs after every frame to increment a variable. We can achieve this by parking a runnable in the main thread.
parkRunnable(String name, Runnable object)
– parks a runnable whoserun()
method will be executed after every frame, until it is unparked [here].removeRunnable(String name)
– unparks the runnable identified by the given name so that it does not run anymore [here].
With these two methods, we just need to create the runnable object with the code we require. Let’s edit our script with these lines:
1 from py4j.clientserver import ClientServer, JavaParameters
2
3 # Create the gateway to connect to the Gaia Sky instance.
4 gateway = ClientServer(java_parameters=JavaParameters(auto_convert=True))
5 # gs is our entry point to call API methods.
6 gs = gateway.entry_point
7
8 # Back up the current settings.
9 gs.backupSettings()
10
11 class FrameCounterRunnable(object):
12 def __init__(self):
13 self.nframes = 0
14
15 def run(self):
16 self.nframes = self.nframes + 1
17
18 class Java:
19 implements = ["java.lang.Runnable"]
20
21 # Create runnable object and submit it.
22 my_runnable = FrameCounterRunnable()
23 gs.parkRunnable("framecounter", my_runnable)
24
25 # Initialize state with position and time.
26 gs.setCameraFree()
27 gs.setSimulationTime(2025, 3, 17, 10, 39, 13, 0)
28 gs.setCameraPosition([7765514.7261459418, 3367392.3812921559, -148604366.1028463840])
29 gs.setCameraDirection([0.7297110211, 0.3130747529, -0.6078700723])
30 gs.setCameraUp([-0.2821898299, 0.9476653108, 0.1493296977])
31 gs.sleep(3)
32
33 # We just go to Mars.
34 gs.goToObject("Mars")
35
36 # Make sure the camera is in cinematic mode so that it keeps its momentum.
37 gs.setCinematicCamera(True)
38 # Set the orbiting speed of the camera in focus mode (in [0,100]).
39 gs.setCameraRotationSpeed(40)
40
41 # Add a gentle push in X. First parameter is deltaX, second is deltaY.
42 gs.cameraRotate(-0.4, 0.0)
43
44 # Disable labels.
45 gs.setComponentTypeVisibility("element.labels", False)
46
47 # Now we wait for 5 seconds, and stop the camera.
48 gs.sleep(5)
49 gs.cameraStop()
50
51 # Time pace is 800x faster than normally.
52 gs.setTimeWarp(800.0)
53 # Start time.
54 gs.startSimulationTime()
55 gs.sleep(8)
56 # Stop time.
57 gs.stopSimulationTime()
58
59 # Back to Earth. We first need to set the camera free,
60 # as we are using a transition.
61 gs.setCameraFree()
62 gs.cameraTransition([7.6099302829, 3.3000312627, -148.6345636443], # pos
63 "internal", # units
64 [-0.8321802084, -0.2822031396, 0.4765726385], # dir
65 [-0.3583614028, 0.9301845136, -0.0749524287], # up
66 8.0, # duration (pos)
67 "logisticsigmoid", # mapping function (pos)
68 30.0, # smoothing factor (pos)
69 4.0, # duration (orient)
70 "logisticsigmoid", # mapping function (orient)
71 12.0, # smoothing factor (orient)
72 True) # sync
73
74 # Remove runnable object and print frames.
75 gs.removeRunnable("framecounter")
76 print(f"Number of frames: {my_runnable.nframes}")
77
78 # Restore settings before exit.
79 gs.restoreSettings()
80
81 # Terminate the connection and exit.
82 gateway.shutdown()
Of course, this is a very simple example only. Much more complex operations can be performed with this technique, like adding or updating objects, manipulating the camera, etc. There are more examples in the showcase scripts directory of our repository. For instance:
camera-constant-turn.py
– creates a constant camera turn using a parked runnable.earth-nea-s.py
– demonstrates the motions of NEAs relative to the Earth-Moon system.earth-venus-dance.py
– creates and updates lines between the Earth and Venus at fixed time intervals.epycicles.py
– demonstrates the apparent retrograde motion of Marse from the geocentric reference system.lunar-libration.py
– demonstrates the libration of the Moon, viewed from the Earth’s surface.
There are many more examples in the sowcases directory.
Docs
See the synchronizing with the main loop section in the user manual.