Skip to content

Scripting

You can add your own logic to realvirtual WEB in three places: inside the model, inside the project folder, or inside the viewer application. They differ in where the code lives, what it is bound to and how much it may do. Most model logic belongs in the first place, as a script in the GLB.

Where code lives The viewer application contains native TypeScript plugins that reach every model it loads. The project folder contains project.json, which lists each document with its GLB file and an optional scriptRef that points at a TypeScript module in the plugins folder. The GLB itself carries scripts on its nodes as WebComponent entries that run sandboxed. Viewer application native TypeScript plugins and behaviors, compiled into the viewer build every model it loads Project folder: copy it and the code comes along project.json documents: [{ file: line.glb scriptRef: plugins/line.ts }] plugins/line.ts project code, unsandboxed opens the model line.glb node Turntable WebComponent.Code script inside the model, sandboxed (QuickJS)
A model script belongs to its node, project code to the document row that names it, a viewer plugin to every model of that build.
Script in the modelProject codeViewer plugin
Lives inthe GLB, on a node (WebComponent)the project folder (plugins/*.ts)the viewer source, built with Vite
Bound tothe node it sits onthe document whose scriptRef names itevery model loaded into that build
LanguageJavaScript or TypeScript, saved as JavaScriptTypeScriptTypeScript
Runssandboxed in QuickJS, no DOM, network or wall clocknative, full access to the scene and the UInative, full access to the scene and the UI
Travels withthe GLB filethe project folderthe viewer deployment
DES snapshot and determinismyes (self.persist, self.random)nono
Typical usecomponent behaviour, interlocks, routing, cell coordinationone customer’s panels, KPIs or integrationsfeatures for every model, such as XR or new panels

Script in the model. The code is part of the model. Copy the GLB to another machine or another project and the behaviour comes with it, because the script is stored in the node’s rv_extras next to its Drive or Sensor data. Any primitive field you add to the same WebComponent appears in the script as self.prop, so one script can serve several instances with different settings.

Project code. A TypeScript module in the project’s plugins/ folder, bound to a document by scriptRef in project.json. You do not edit the JSON by hand: in the dashboard, drag the file onto the PLUGIN slot of the document card, or use Bind plugin…. Several documents can share one module, and renaming the GLB does not break the binding because the binding sits on the document row, not on a file name. The chip on the card shows whether the module is bound, loaded, missing or failed.

Viewer plugin. Part of the application itself. Use it for features that every model should get. It needs a build and a deployment of the viewer.

Loading scripts and project code When a document opens, two paths start. Model scripts found in the GLB wait at the trust gate until scripting is enabled, then each runs in its own QuickJS sandbox. Project code named by scriptRef is taken from the viewer build if it was bundled, otherwise from the compiled .js file next to the .ts file in the project; runtime code asks for consent once per project before it runs as a native plugin. Open document from the project Model scripts WebComponent in GLB Trust gate Enable scripting QuickJS sandbox per script, hot reload scriptRef in project.json Resolve module build, else .js file Native plugin runtime code: ask once
Both paths are guarded: model scripts by the trust gate, project code loaded at runtime by a consent per project.
  • Model scripts never run on their own. The viewer counts them and waits until you choose Enable scripting (or the link carries ?scripts). Switching it off stops every script; switching it on starts them again for the loaded model.
  • Project code is resolved in two ways. If the module was compiled into the viewer build, that copy is used. Otherwise the viewer loads the compiled .js file next to the referenced .ts file in the project folder (plugins/line.ts → plugins/line.js). Customer deliveries create these files automatically; locally you build them with npm run build:project-scripts -- <projectDir>. Code loaded this way is unsandboxed, so the viewer asks once per project before running it. Without consent the project still opens, only its code stays off.
  • A scriptRef that resolves to neither loads no code. There is no fallback by file name.
  • The logic describes how a part of the machine behaves: script in the model.
  • The logic belongs to one customer project, such as a panel, a KPI or a link to an external system: project code.
  • The logic is a feature for every model: viewer plugin.

Only scripts in the model take part in DES snapshots and in the reproducible random streams. Put state that a DES run must restore into a model script.

In the Editor, use Edit Script on the component, or open Script Editor (component scripts) from the side toolbar. The Script Editor checks code against the SDK types. If scripting is disabled, Enable scripting allows the model scripts to run.

This example logs a message after one simulated second:

function setup(self) {
self.in(1, 'hello');
return {
des: { on(hook) {
if (hook === 'hello') self.log('Hello at', self.now);
} },
};
}
UseSDK entry points
Configuration and simulation timeself.prop, self.now
Find nodes and componentsself.node, self.find, self.findAll, self.children
Equipment and signalsself.drive, self.sensor, self.belt, self.source, self.signal
Material flowself.inputs, self.outputs, self.transfer, self.spawn
Schedule eventsself.in, self.at, self.every, self.cancel
State and random numbersself.persist, self.setState, self.random
Script communicationself.component, self.broadcast
Statistics and messagesself.statValue, self.statState, self.log, self.warn, self.error
Geometry valuesself.vec3, self.quat, self.mat4, self.aabb

Scripts cannot access the DOM, network or wall-clock timers. The DES code check also looks for state that would be lost on restore. See Scripting in DES before adding changing state or recurring timers.