log in
substrata logo

Scripting in Substrata

Luau scripting

The newest and the most powerful form of scripting in Substrata is Luau scripting. Luau scripts can achieve complex and rich behaviour, reacting to various types of events. You can even create complete games in Luau in Substrata.

Read more about Luau scripting

Winter Scripting

Winter scripting can achieve basic, non-reactive behaviour, such as rotating or periodically moving objects. Winter scripting uses our Winter programming language.

See the Winter language reference documentation on Github.

Client-side execution

Winter scripts in Substrata are executed in the Substrata client program (e.g. they are executed 'client-side'). Winter programs are restricted in what they can do, so are safe to execute client-side. (Although we can't rule out all bugs in the Winter execution environment)

Scripting an object

To make a script for an object, you edit code in the 'Script' text edit box in the object editor in the Substrata client, after selecting an object. You can only edit scripts on objects that you own (e.g. that you created).

Scriptable functions

To script the behaviour of an object, you can define either of two functions:

def evalRotation(float time, WinterEnv env) vec3

time is the current global time in the Substrata metaverse.

This function returns a 3-vector, where the direction of the vector defines the axis of rotation, and the length of the vector defines the counter-clockwise rotation around the axis, in radians.

For example, the rotating wind turbine blades use the following script:

def evalRotation(float time, WinterEnv env) vec3 : vec3(-0.6, 0.0, 0.0) * time

This rotates the blades clockwise (due to the minus sign) around the x axis at a constant rate.

def evalTranslation(float time, WinterEnv env) vec3

This function returns a 3-vector, which defines a spatial translation from the usual position of an object (as placed by a user or entered in the object editor). The translation can be a function of time to allow object movement.

For example, this script makes an object move back and forth along the x axis by a total of 1.6 metres:

def evalTranslation(float time, WinterEnv env) vec3 : vec3(sin(time * 1.5) * 0.8, 0, 0)

XML Scripting: Follow Path Script

You can also use XML to define some behaviours for an object, for example make the object follow a path around the world. (This is the kind of script that the monorail uses)

The following is a script that makes an object continuously follow a square shaped path:

<?xml version="1.0" encoding="utf-8"?>

		<waypoint><pos>0 0 1</pos>	<type>Stop</type>	<pause_time>1</pause_time>	</waypoint>
		<waypoint><pos>10 0 1</pos>	<type>Stop</type>	<pause_time>1</pause_time>	</waypoint>
		<waypoint><pos>10 10 1</pos>	<type>Stop</type>	<pause_time>1</pause_time>	</waypoint>
		<waypoint><pos>0 10 1</pos>	<type>Stop</type>	<pause_time>1</pause_time>	</waypoint>

Each waypoint has the position of the waypoint, what type of waypoint it is, and in the case of 'Stop' waypoints, how long the object will stop at the waypoint before continuing.

The <speed>3</speed> tag also sets the default speed of the object along the path, although this can be overridden for each waypoint.


This is an XML script, that allows any object in the world to behave like a hovercar, that can be entered and driven by a user.

<?xml version="1.0" encoding="utf-8"?>
		<model_to_y_forwards_rot_1>1.57079632679 0 0</model_to_y_forwards_rot_1>
		<model_to_y_forwards_rot_2>0 0 3.141592653</model_to_y_forwards_rot_2>

			<seat_position>0.45 0.43 -0.2</seat_position>

			<seat_position>-0.45 0.43 -0.2</seat_position>

First off we have two rotations that allow transforming from the model to world space. This is useful in cases like transforming from GLTF coordinates (y-up) to Substrata coordinates (z-up).

model_to_y_forwards_rot_1: This is an axis-angle rotation, from model coordinates (object space) to world coordinates (y-forwards space). (before overall transformation of hovercar)

The world coordinates have the following convention: the z-axis is up, the x-axis is right, and the y-axis is forwards.

The length of the model_to_y_forwards_rot_1 vector is the angle of rotation in radians, counterclockwise around the vector.

In the example above we rotate around the x axis by 90 degrees, to transform from y-up to z-up.

model_to_y_forwards_rot_2 allows defining a second rotation. In the example above the second rotation rotates around the z-axis by 180 degrees (pi radians).

The overall rotation is given by model_to_y_forwards_rot_2 * model_to_y_forwards_rot_1. This means the model_to_y_forwards_rot_1 rotation is applied first, then model_to_y_forwards_rot_2


Each vehicle can have one or more seats. The first seat is the driver's seat. Seats are defined with the following XML elements:

seat_position: This is the position in model coordinates where the hip bone of the avatar should be placed. You can get the position by opening the model in Blender, just be aware that the Blender GLTF importer changes the coordinate space from y-up of GLTF to z-up upon import.

upper_body_rot_angle: This is the rotation, in radians, of the upper body, from the default vertical orientation. A positive angle means the avatar will lean back.

upper_leg_rot_angle: This is the rotation, in radians, of the leg around the hip joint, from the default vertical orientation. A positive angle means the upper leg will rotate forwards toward the chest.

lower_leg_rot_angle: This is the rotation, in radians, of the lower leg around the knee joint. A negative angle means the lower leg will rotate backwards toward the heel.

Applying the hovercar script

You can just copy and paste the above example script into the script field of an object, however there are some requirements:

Dynamic Texture Updating

This is an XML script that allows image textures on objects in the world to be updated occasionally from an external web server.

In the example script below, the URL https://images.metaverse-billboards.com/space1.png is periodically checked for a new image.

<?xml version="1.0" encoding="utf-8"?>
		<material_index>0</material_index> <!-- optional -->
		<material_texture>colour</material_texture> <!-- optional, can be 'colour' or 'emission' -->

To make an object with a dynamically updating texture: From the Substrata client, add an image object in the usual way: 'Add Model/Image/Video' toolbar button, then select 'from disk' tab and select a placeholder image. Then edit the script field to add the script above.

Your user account needs to be whitelisted by a Substrata admin for this script to work. Message @nick on our Discord server, or contact us at contact@glaretechnologies.com to request this.

Images are checked from the URL given by 'base_url' for updates approximately every hour. If an updated image is returned by the webserver, then the updated image will be inserted into Substrata as a resource, and the updated image will be applied to the object.

< Home