Block registration
Blocks reach the palette in two ways: the built-in categories are registered as internal block
packages when the runtime starts, and extensions register their blocks through the same pipeline
that turns a getInfo() descriptor into real scratch-blocks. This page describes how that
happens in scratch-vm/src/engine/runtime.js.
Built-in block packages
The core categories (motion, looks, sound, events, control, sensing, operators, variables, and
custom blocks) are plain classes in scratch-vm/src/blocks/scratch3_*.js. On construction, the
runtime calls _registerBlockPackages(), which instantiates each package and collects three
things from it:
getPrimitives(): a map of opcode to implementation function. These are stored inruntime._primitives, keyed by opcode (for examplemotion_movesteps), and are what the interpreter calls.getHats(): metadata for hat blocks (for example whether the hat restarts existing threads), stored inruntime._hats.getMonitored(): which reporters can be shown as stage monitors, merged intoruntime.monitorBlockInfo.
Each package is also registered for the compiler via compilerRegisterExtension(name, object),
which attaches it to the runtime as ext_<name> so compiled code can reach it.
An opcode is the category name and block name joined by an underscore, like
looks_sayforsecs. The palette's block shape, label, and inputs come from the
scratch-blocks definitions in the scratch-blocks package; the VM side only provides the opcode
and its behavior.
Extension blocks
Extensions do not edit those files. They describe their blocks in getInfo() and register with
Scratch.extensions.register (see the Extension API). The
extension manager runs getInfo() and hands the result to the runtime's
_registerExtensionPrimitives(extensionInfo), which:
- Builds a category descriptor from the extension's
id,name, colors (color1/color2/color3, falling back to defaults), and icons, and pushes it ontoruntime._blockInfo. - Fills the category by converting each block descriptor with
_convertForScratchBlocks, which turns thetext,blockType, andargumentsinto the scratch-blocks XML the editor needs. - Registers any custom field types, emitting
EXTENSION_FIELD_ADDEDfor each. - Emits
EXTENSION_ADDEDwith the finished category so the GUI can add it to the palette.
Reloading an extension's blocks calls _refreshExtensionPrimitives, which rebuilds the category
and emits BLOCKSINFO_UPDATE. Removing one calls _unregisterExtensionPrimitives and emits
EXTENSION_REMOVED. See Events.
A block descriptor
Each entry in the blocks array of getInfo() looks like this:
{
opcode: 'doThing',
blockType: Scratch.BlockType.COMMAND,
text: 'do thing with [INPUT]',
arguments: {
INPUT: {
type: Scratch.ArgumentType.STRING,
defaultValue: 'hello'
}
}
}
The runtime namespaces the opcode with the extension ID, so doThing on extension myext
becomes the opcode myext_doThing, and calls the extension instance's doThing method when the
block runs. Placeholders in text (like [INPUT]) are matched to keys in arguments.
Compiled blocks
The interpreter path uses the block methods directly. For extensions that want their blocks to
run through RemixWarp's JavaScript compiler, vm.exports.compiler.register(extensionId, blocks)
registers a descriptor per opcode with a type (one of any, number, numberOrNaN, string,
boolean, command) and a compile function. See
Compiled extensions.
See also
- Extension API
- Threads for how registered blocks execute
- Events
- Building custom C blocks