Scripting Basics

Meta Spark Studio supports JavaScript for adding logic and interactivity to your effects. This guide will cover the basics to help get you started with scripting.

Meta Spark studio will open your scripts with the default editor assigned to JavaScript/TypeScript files on your macOS or Windows operating system. If you don't already have one installed, you can download an editor such as Visual Studio Code and set it as the default app for opening JavaScript/TypeScript files.



If you're using Visual Studio Code, there's a Meta Spark Studio extension available to make development easier.




Before you start

Below are some important points to consider when scripting in Meta Spark Studio:

Creating a script

To create a new script click + the Assets panel and then select Script. You can choose to create a new JavaScript or TypeScript file.

A new script file will appear in the Assets panel.

Opening a script

To open a script double-click on the script file in the Assets panel.

The script file will open in the default editor associated with JavaScript files on your computer.

Editing a script

To edit a script make the changes within the script editor and save them before returning to Meta Spark Studio.

The changes in your script will be reflected in Meta Spark Studio.

Scripting fundamentals

New scripts are pre-populated with code covering some fundamentals of scripting in Meta Spark Studio.

The example below is from a pre-populated JavaScript file. The contents of a newly created TypeScript file will differ slightly.

  //==============================================================================
  // Welcome to scripting in Meta Spark Studio! Helpful links:
  //
  // Scripting Basics - https://fb.me/spark-scripting-basics
  // Reactive Programming - https://fb.me/spark-reactive-programming
  // Scripting Object Reference - https://fb.me/spark-scripting-reference
  // Changelogs - https://fb.me/spark-changelog
  //
  // For projects created with v87 onwards, JavaScript is always executed in strict mode.
  //==============================================================================
  
  // How to load in modules
  const Scene = require('Scene');
  
  // Use export keyword to make a symbol available in scripting debug console
  export const Diagnostics = require('Diagnostics');
  
  // Enables async/await in JS [part 1]
  (async function() {
  
  // To use variables and functions across files, use export/import keyword
  // export const animationDuration = 10;
  
  // Use import keyword to import a symbol from another file
  // import { animationDuration } from './script.js'
  
  // To access scene objects
  // const [directionalLight] = await Promise.all([
  //   Scene.root.findFirst('directionalLight0')
  // ]);
  
  // To access class properties
  // const directionalLightIntensity = directionalLight.intensity;
  
  // To log messages to the console
  // Diagnostics.log('Console message logged from the script.');
  
  // Enables async/await in JS [part 2]
  })();

Loading Modules

Scripting is broken down into modules with each module implementing a particular functionality.

To load in a module's API to a JavaScript file you use the require method.

  // How to load in modules
  const Diagnostics = require('Diagnostics');
  const Scene = require('Scene');

In the example above we are loading in the Diagnostics and Scene modules and storing them in variables for use later on in the code.

In a TypeScript file the syntax changes slightly:

  // How to load in modules in a TypeScript file
  import Scene from 'Scene';
  import Diagnostics from 'Diagnostics';
          

For a complete list of modules, each with their own code example, check out the Scripting Object Reference.

Accessing Scene Objects

To access an object you have in your scene you can use the findFirst method of the root property provided by the Scene module.

  // Store a reference to a single scene object
  const focalDistance = await Scene.root.findFirst('Focal Distance');
  
            
  // Store references to multiple scene objects 
  const [directionalLight, plane] = await Promise.all([
      Scene.root.findFirst('directionalLight0'),
      Scene.root.findFirst('plane0'),
  ]);
          

In the example above we are accessing objects in the scene by referencing them by name and storing them within variables.

The findFirst method will return None if object is not found. If there’re more than one object named, the same arbitrary one will be used. Please use findAll to find all occurrences of given object.

Alternatively, you can use the findByPath method of the root property to access objects using their relative path.

  Scene.root.findByPath('Device/Camera/Focal Distance/plane0').then((results) => {
      const plane = results[0];
  });
          

The findByPath method allows usage of the wildcard character * to match any child name, and double wildcard ** to match any set of children.

  Scene.root.findByPath('*/*/*/plane0').then((results) => {
      // If there are multiple scene objects matching the given path, multiple results may be returned
      const plane = results[0];
  });
          

Note that if you store the reference as a const variable with the findByPath method as in the previous examples, the object must be modified within scope.

Attempting to modify it outside of scope will throw an error, as seen in the following example.

  Scene.root.findByPath('Device/Camera/Focal Distance/plane0').then((results) => {
      const plane = results[0];
  });
  
  // Will throw an error, as we are trying to modify the plane const outside of the scope it is defined in
  plane.width = 0.3;
          

You can either modify the object within the findByPath method's scope as seen below or use the findFirst method shown previously to create globally-accesible references.

  Scene.root.findByPath('Device/Camera/Focal Distance/plane0').then((results) => {
      const plane = results[0];
  
      // Since the plane const is modified within scope, no error is thrown
      plane.width = 0.3;
  });
          

Objects accessed from the scene, e.g. Scene.root.findFirst('directionalLight0'), do not currently support autocomplete.

Editing Scene Object Properties

Once you have access to a scene object, you can access its properties and methods.

   // How to access class properties
   const directionalLightIntensity = directionalLight.intensity; 

In the example above we use the directionalLight variable we created previously to access the light's intensity property.

Logging to the console

Messages can be logged from a script to the Console within Meta Spark Studio using the Diagnostics module.

  // How to log messages to the console (uncomment line below to activate)
   Diagnostics.log('I am a console message logged from the script');

In the pre-populated script the Diagnostics.log line needs to be un-commented in order to run.

Code autocomplete

Scripts can be edited in the editor of your choice such as Atom or Sublime, however VS Code additionally supports autocomplete functionality via IntelliSense.

On MacOS please make sure you have installed Meta Spark Studio in the /Applications directory for autocomplete to function properly.

Code Suggestion

Methods and properties will be suggested as you type.

Inline documentation

You can view the documentation for the highlighted method or property.

Type checking

If you pass the wrong type or number of arguments to a method you will be warned.

Objects accessed from the scene, e.g. Scene.root.find('directionalLight0'), do not currently support autocomplete.

Next steps

Now you've mastered the basics, it's the ideal time to start coding along at your own pace with our short video tutorial series.