Skip to main content

VM API

The VM (virtual machine) runs the project. It is available to unsandboxed extensions as Scratch.vm, and to block functions as util.runtime (the runtime is vm.runtime).

if (!Scratch.extensions.unsandboxed) {
throw new Error('This extension needs the VM');
}
const vm = Scratch.vm;
const runtime = vm.runtime;

These are internal objects, not a frozen public API. Guard for missing values (sprites get deleted, extensions unload) and prefer listening to events over polling. Most of these methods exist on both Bilup and TurboWarp; Bilup-specific behavior is noted where it matters.

Project control

vm.greenFlag(); // press the green flag
vm.stopAll(); // stop all scripts
vm.setTurboMode(true); // turbo mode on/off
vm.setFramerate(60); // target frames per second
vm.setInterpolation(true);
vm.setCompilerOptions({ enabled: true, warpTimer: false });
vm.setRuntimeOptions({ maxClones: 300, miscLimits: true, fencing: true });
vm.setStageSize(width, height);

Bilup compiles projects by default (compilerOptions.enabled is true). Framerate and stage size are not limited to Scratch's 30 FPS / 480x360.

Targets

A "target" is a sprite, a clone, or the stage.

runtime.targets; // array of all targets (stage + sprites + clones)
runtime.getTargetForStage(); // the stage target
runtime.getTargetById(id); // by internal id
runtime.getSpriteTargetByName('Sprite1'); // the original (non-clone) sprite by name
vm.editingTarget; // the sprite currently open in the editor

Target properties and methods:

const t = runtime.getSpriteTargetByName('Sprite1');
t.x; t.y; t.direction; t.size; t.visible;
t.getName();
t.isStage; t.isOriginal;
t.currentCostume; // index
t.sprite.costumes[t.currentCostume].name;

t.setXY(100, 50);
t.setDirection(90);
t.setSize(150);

Clones

const original = runtime.getSpriteTargetByName('Sprite1');
const clone = original.makeClone();
if (clone) {
runtime.addTarget(clone);
clone.setXY(100, 50);
}

// remove a clone (never the original)
if (!clone.isOriginal) runtime.disposeTarget(clone);

Variables and lists

Variables and lists live on a target. Use '' as the type for a variable, 'list' for a list.

const stage = runtime.getTargetForStage();

const score = stage.lookupVariableByNameAndType('score', '');
if (score) score.value = 100;

const items = stage.lookupVariableByNameAndType('items', 'list');
if (items) items.value.push('new item');

Global variables and lists live on the stage; sprite-local ones on the sprite. To enumerate, iterate target.variables and check each entry's .type.

Starting scripts

// Start every "when flag clicked" hat
runtime.startHats('event_whenflagclicked');

// Start hats matching a field
runtime.startHats('event_whenbroadcastreceived', { BROADCAST_OPTION: 'message1' });

startHats returns the started Thread objects. Inside a running block, use util.startHats instead. See Events and hats.

Threads

runtime.threads; // running threads
runtime.stopForTarget(target); // stop a target's scripts

thread.target; // the target running it
thread.topBlock; // id of the top block
thread.status; // see below

Thread status values:

ValueConstantMeaning
0STATUS_RUNNINGRunning
1STATUS_PROMISE_WAITWaiting on a Promise
2STATUS_YIELDYielded this frame
3STATUS_YIELD_TICKYielded until next tick
4STATUS_DONEFinished

From inside a block, util (the block utility) is your handle on the current thread rather than reaching into runtime.threads:

  • util.thread is the Thread running the block.
  • util.yield() yields the current thread for this frame (returning a Promise from an async block is the usual way to wait; util.yield() is the manual equivalent).
  • util.yieldTick() yields until the next tick.
  • util.startBranch(index, isLoop) runs a C-block branch (see Custom C blocks).

For the full thread model, statuses, and the sequencer, see the Threads reference.

Events

Listen with vm.on(...) or runtime.on(...):

EventFires when
PROJECT_RUN_STARTScripts begin running
PROJECT_RUN_STOPAll scripts have stopped
PROJECT_LOADEDA project finished loading
PROJECT_CHANGEDThe project was modified
targetWasCreatedA target (often a clone) is created; args (newTarget, sourceTarget)
TARGETS_UPDATEThe target list changed
BEFORE_EXECUTEEach frame, before scripts run (useful for predicate hats)
MONITORS_UPDATEMonitor values changed
runtime.on('targetWasCreated', (target) => console.log('created', target.getName()));
vm.on('PROJECT_RUN_START', () => console.log('running'));

The table above lists the events extensions reach for most often. For the full set of runtime events and lifecycle, see the Runtime reference.

Input devices

const mouse = runtime.ioDevices.mouse;
mouse.getClientX(); mouse.getClientY(); mouse.getIsDown();

const keyboard = runtime.ioDevices.keyboard;
keyboard.getKeyIsDown('space');

const clock = runtime.ioDevices.clock;
clock.projectTimer(); // seconds since timer reset
clock.resetProjectTimer();

Stage size

runtime.stageWidth; // current logical width
runtime.stageHeight; // current logical height
vm.setStageSize(640, 360);

Extension management

const em = vm.extensionManager;
em.isExtensionLoaded('pen'); // boolean
em.getExtensionURLs(); // loaded extension URLs
em.refreshBlocks('myextension'); // re-render the palette after changing block info

Per-project storage

runtime.extensionStorage is an object saved inside the project file. Store small JSON-serializable settings keyed by your extension ID:

runtime.extensionStorage.myextension = { volume: 0.5 };

Compiler internals

To generate JavaScript directly (native-speed blocks), Bilup exposes a supported compiler-extension API via vm.exports.compiler. That is its own topic; see Compiled extensions.

See also