Skip to content

Scripts

Most Opal games can be built with components and Flow. Use the Scripts workspace when visual graphs need a reusable bit of JavaScript or when a behavior is easier to express as code.

Scripts are project-level authoring tools. They are not required for ordinary objects, scenes, prefabs, animation, or UI.

When to use scripts

Use scripts for:

NeedExample
Reusable calculationDamage formula, score curve, distance check
Complex conditionInventory + quest + timer gate
Custom actionApply several component calls as one named operation
Prototype logicTry behavior before turning it into a component
Utility functionConvert coordinates, clamp values, format text

Prefer Flow when the behavior is simple and designer-readable. Prefer a component when state and behavior should live together on objects.

Scripts vs components vs Flow

ToolBest at
FlowEvent wiring, readable gameplay logic, object and scene behavior
ComponentsReusable stateful capabilities attached to objects
ScriptsReusable code helpers or advanced graph nodes

A healthy project often uses all three: components hold behavior, Flow wires events, scripts handle the few calculations that are awkward as nodes.

Behavior scripts (write logic on an object, no graph)

If you would rather write behavior as code than wire nodes, give an opal.component lifecycle hooks. This is the text-first alternative to visual scripting: attach the component to any object from the inspector's Add Component menu and it runs — no events, no wires.

Click + Behavior in the Scripts workspace for a starter, or write one directly:

js
export default opal.component("Spinner", {
  category: "Behaviors",
  fields: {
    speed: { type: "number", default: 90, label: "Speed (deg/sec)" },
  },
  onTick: { spin: "self.speed" },
});

Declarative sugar (recommended for common motion):

SugarMeaning
onTick: { spin: "self.speed" }Rotate at degrees/sec (Motion-safe)
onTick: { drift: { x: "60", y: "0" } }Move at pixels/sec on each axis

Imperative helpers inside "onTick: \"...\"" strings:

CallMeaning
helpers.spin(thing, self.speed, dt)Spin at degrees/sec
helpers.moveBy(thing, dx, dy)Nudge position (syncs Motion targets)
helpers.deg(90)Degrees → radians
HookRunsUse for
onStartOnce when Play beginsInitialize state, seed values
onTickEvery frame while playingMovement, timers, per-frame logic
onEndOnce when Play stopsCleanup, final bookkeeping

Inside a hook body you have:

NameWhat it is
selfThis component's fields — editable in the inspector, saved per object
statePer-instance scratch — not saved; reset each Play
thingThe object the component is on (thing.x, thing.rotation, …)
dtSeconds since the last frame (onTick only)
get / run / checkRead, call, and query sibling components
emitFire one of this component's declared events
helpersspin, moveBy, rotateBy, deg, rad — Motion-safe motion helpers

thing.rotation is in radians. Prefer onTick: { spin: "self.speed" } or helpers.spin(thing, self.speed, dt) instead of hand-rolling targetRotation sync.

The bodies run in the shipped game exactly as they do in the editor, and onTick never runs while you are editing. Keep them short: for/while loops and arrow functions are intentionally disallowed — for iteration, reach for a component action or Flow.

Open the Scripts workspace

Click Scripts in the workspace tabs.

The workspace includes:

AreaPurpose
Script listProject scripts and templates
EditorJavaScript source
Docs / tutorials drawerAPI reference and starter examples
Validation feedbackCompile or syntax messages where available

Start with a small script. If it grows large or stores object state, consider turning it into a component.

Use scripts from Flow

Flow includes a Script node. The script node can access runtime context such as the current object, event params, and components.

Typical use:

text
On Tap
  -> Script
  -> Play Audio

Keep Script nodes named and documented. A graph full of anonymous code blocks is harder to maintain than either pure Flow or reusable project scripts.

Script safety habits

  1. Keep scripts short.
  2. Name inputs and outputs clearly.
  3. Avoid hidden global state.
  4. Prefer component actions for gameplay state changes.
  5. Use Scene Flow for scene transitions and global rules.
  6. Test scripts in Play mode after every change.

Good script candidates

ScriptWhy
formatScoreTurns a number into display text
damageRollCombines attack, defense, and randomness
isBelowWorldReusable out-of-bounds check
pickWeightedRewardSelects a random item from weighted options

When not to use scripts

Avoid scripts for:

  • Simple On Tap behavior
  • Plain variable changes
  • Basic scene transitions
  • Standard Health, Inventory, Physics, or Motion behavior
  • Anything a built-in component already does

The manual teaches Flow first because it remains inspectable by non-programmers.

Opal Engine — MIT licensed