Skip to main content
Version: 5.x
Supported on
Snapchat
Spectacles
Camera Kit

Building Plugins

You can build your own plugins to support your own workflow, and even share it to help others!

You can also use the Yeoman Generator to accelerate your plugin development process. The generator will generate the necessary files that are described here, as well as boilerplate code to help you get started!

Core Concepts

Before beginning, it's crucial to understand the core concepts unique to plugin development, which differ from those in building Lenses. Grasping these fundamentals is key to comprehending the structure of plugins.

As you try these various capabilities, you can use console.log to print messages to the Lens Studio Logger panel.

The Editor namespace

The Editor namespace contains the majority of the Editor API. For example, you can access camera type strings via Editor.Components.CameraType.Orthographic; or create a new Path object by new Editor.Path().

Model object

The model object is a core concept in the plugin development environment. It serves as a central point for accessing key elements such as the scene, project, and assetManager.

The model object encapsulates the data model representing a Lens Studio project, and project manipulation. It brings together environment entities and functionalities that are essential for developing plugins. It plays a role analogous to the "Model" component found in Model-View-Controller architectural patterns, containing both data and business logic.

In order to get the model object, which many key objects are stored within, you need the pluginSystem object which is being passed into the constructor of the plugin class, along with the ID of the model component (which can be accessed through the Editor namespace)

const model = pluginSystem.findInterface(Editor.Model.IModel);
const assetManager = model.project.assetManager;
const scene = model.project.scene;

Scene object

The scene object handles access to the project scene. It allows interacting with scene objects programmatically. It hosts key functions such as findComponents(entityType: string): Editor.Components.Component[], sceneObjects : Editor.Model.SceneObject[], rootSceneObjects: Editor.Model.SceneObject[] and many more.

For example, to add a new scene object:

const newSceneObject = scene.createSceneObject('name');

AssetManager object

Whenever you need to interface with the assets in the project, you need the assetManager object. For example:

const assetManager = model.project.assetManager;

//import external file as assets
await assetManager.importExternalFileAsync(
path,
'',
Editor.Model.ResultType.Packed
);

//create a new material
assetManager.createNativeAsset(
'Material',
materialFileName,
new Editor.Path(materialFilePath)
);

//find a copy from the existing assets
assetManager.findImportedCopy(absolutePath);

Plugin Directory Structure

Within a single plugin modules directory, you may have multiple plugins (as denoted by a folder and a module.json file which specifies the entry script). When you load a plugin by adding a plugin directory in the Plugin Manager, each plugin will appear with its own checkbox. When this checkbox is enabled, the plugin will be enabled for the entire Lens Studio application.

Here is an example plugins directory containing two plugins (a CoreService plugin and a Preset plugin):

  • 📁 PluginModulesDirectory
    • 📁 CoreServicePlugin
      • 📄 main.js
      • 📄 module.json
    • 📁 PresetPlugin
      • 📄 icon.svg
      • 📄 main.js
      • 📄 module.json
    • 📁 Resources
      • 📄 example_image.png
      • 📄 example_material.ss_graph

Apart from module.json, the naming of all other files and directories listed above is flexible and can be changed.

In the example above, you would add the PluginsDirectory to Lens Studio, and you will see the CoreServicePlugin and PresetPlugin become available. (You might have to close and re-open the Plugin Manager for the changes to take effect).

Entry Script

The module.json file is the plugin's entry point. The naming is preset so Lens Studio could find it. It should include a JavaScript object that has a key called main, pointing to your entry JavaScript file. Access to certain modules needs permission, such as network, filesystem, shell, and secure_local_storage. A popup will be shown at the loading of the plugin asking the user to grant the needed permissions.

module.json
{
"name": "My Plugin Name",
"main": "main.js",
"permissions": [
"network",
"filesystem",
"shell"
// ...anything else you need
]
}

You can add additional files beyond the entry script. Using separate files can help modularize and organize your code. Interaction between scripts uses standard ECMA-style import/export schemes.

Class Structure

Import

Your plugin class should extend one of the base classes:

  • PanelPlugin
  • DialogPlugin
  • ScreenPlugin
  • Preset
  • CoreService

Ensure that these base classes are imported from their respective modules, which are LensStudio:PanelPlugin, LensStudio:DialogPlugin, LensStudio:ScreenPlugin, LensStudio:Preset, and LensStudio:CoreService.

Export

In accordance with the configuration specified in 'module.json', ensure that your plugin class is properly exported in the entry script. It's important to note that multiple plugin classes can be exported within a single entry script.

//main.js minimal structure
import PanelPlugin from 'LensStudio:PanelPlugin';
export class YourPluginName extends PanelPlugin {
static descriptor() {
// ...
}
constructor(pluginSystem) {}
}

Aside from extending the appropriate base class, the following functions are required in your class, no matter the type of the plugin.

Descriptors

The static function static descriptor( ) : void contains fields needed to populate the plugin descriptor (refer to table below). All plugins are required to populate the id, interfaces, name, description fields.

For Preset plugins, additional fields are required: icon, section, and entityType.

If you include unsupported fields in the descriptor, an error will not occur but nothing will happen with them. If you miss a field or populate a field with an incorrect type, the descriptor will not be read and the plugin will not be processed.

  • id: String
    • Needs to be a unique identifier, suggested to follow Reverse Domain Name
  • interfaces: String[]
    • The IDs of the interfaces that the plugin depends on.
  • name: String
    • A more readable name for the plugin
  • description: String
    • A more verbose description of the plugin
  • dependencies: String[]
    • A list of modules that this plugin relies on to function properly. Example list of modules are:
      • Editor.IContextActionRegistry
      • Editor.IEntityPicker
      • Editor.IAuthorization
      • Editor.Model.IEntityPrototypeRegistry
      • Editor.Model.IEntityRegistry
      • Editor.Model.IModel
      • LensStudio:AssetLibrary.IAssetLibraryProvider
      • LensStudio:Ui.IGui Note: Model.IModel is not needed for Preset, since it’s always used, and thus automatically added.
  • icon: Icon
    • Preset only. This is the icon that appears in the “+” menu. Save in folder as icon.svg (currently svg is the only supported file type)
  • section: String
    • Preset only. You may specify any string and the preset will be shown in the corresponding section in the Scene Hierarchy or Asset Browser “+” dropdown menu. E.g., General, Effects, Face, Physics.
  • entityType: String
    • Preset only. Currently supported entities are: Asset and its derived types Component and its derived types SceneObject

Take a look at the Editor API to learn more.

Constructor

Within the constructor constructor(pluginSystem) : void of your plugin class, you must call the base type constructor first, i.e., super(pluginSystem). In addition to this, you can place any startup logic for the plugin in this function. The passed-in parameter pluginSystem will become the bridge towards the needed objects to carry out the operation of your plugin, See the Core Concepts section for specific information.

Using TypeScript

Plugins are meant to be tools to help you work more efficiently. Sometimes it can get a bit tricky to manage the complexity of your plugin. TypeScript can help you manage the complexity of your plugin by providing you with type checking and autocompletion. Although the plugin system doesn't support interpreting TypeScript files directly, you can still use TypeScript to write your plugin and then compile it to JavaScript. Here is how you can do it:

Make sure you have TypeScript installed

If your system doesn't have TypeScript installed, you can install it by referring to this link.

Create a tsconfig.json file

Inside of your plugin directory, create a tsconfig.json file with the following content:

{
"compilerOptions": {
"target": "ES2019",
"module": "ES2020",
"rootDir": "./src",
"outDir": "./dist",
"strict": true
},
"include": ["src/**/*"]
}

Feel free to edit and add more options to the tsconfig.json file. You can find more information about the options here.

Create a src directory

Inside of your plugin directory, create a src directory. This is where you will put your TypeScript files.

Compile your TypeScript files

Run the following command in your terminal to compile your TypeScript files:

tsc

or if you want to watch for changes and compile automatically, you can run:

tsc -w

This will compile your TypeScript files and output the compiled JavaScript files in the dist directory.

Set up the entry script

In your module.json file, make sure to point to the compiled JavaScript file as the entry script. For example, if your entry script is main.ts, which gets compiled as main.js, you should point to dist/main.js in the module.json file.

(Optional) Add TypeScript definitions

To add TypeScript definitions for the Lens Studio Editor API:

  1. Create a typings directory in your project.
  2. Download the latest Type Definitions file for the Editor Scripting API from here.
  3. Place the downloaded file (typically named as editor.d.ts) in the typings directory.
  4. In your tsconfig.json, add the path to this file in the "include" array:
{
"include": ["src/**/*", "typings/**/*"]
}

This will allow TypeScript to recognize and provide code completions for Lens Studio API functions and types in your code.

Was this page helpful?
Yes
No