Adding a Block
This tutorial is for adding blocks as core blocks (blocks that are not included in an extensions) in a mod. If you want to add a custom extension to your Scratch mod, see the Adding Extensions tutorial. If you want just want make a Scratch extension and not a Scratch mod, you probably want to look at TurboWarp's custom extensions instead.
What block are we adding?
Let's add a very simple block, that uses the browser's alert() function to display a message.
Making the block
The first step to adding a block is actually making the block itself.
Since we are adding a looks blocks, we need to open the looks.ts file. This file is part of scratch-blocks and is located at src/blocks/looks.ts.
Anatomy of a block
Let's take a look at the first block in the file, which is looks_sayforsecs or . Explanations are included in the highlighted comments.
// "Blockly.Blocks" is the object that contains the blocks in Scratch.
// "looks_sayforsecs" is the ID of the block.
Blockly.Blocks['looks_sayforsecs'] = {
// A JSDoc comment, it provides documentation about the code and may help your code editor provide relevant autocomplete suggestions.
/**
* Block to say for some time.
*/
// The "init" function is called by Scratch Blocks/Blockly to initialise the block.
init: function(this: Blockly.Block) {
// this.jsonInit is a function that initialises the block with information provided in the form of a JavaScript object.
this.jsonInit({
// "message0" is the text the block displays. It currently is pointing to a translatable message (Blockly.Msg.).
// The contents of Blockly.Msg.LOOKS_SAYFORSECS is "say %1 for %2 seconds".
// The "%1" and "%2" are locations that will be replaced with something else, in this case input boxes.
//
// If a block has multiple parts, the number after "message" may be increased. An example is the forever block (CONTROL_FOREVER).
message0: Blockly.Msg.LOOKS_SAYFORSECS,
// These are the replacements for the "%1" and "%2" from the block message. In this case, both are "input_value"s
args0: [
{
type: "input_value",
name: "MESSAGE"
},
{
type: "input_value",
name: "SECS"
}
],
// A list of "extensions" for the block, this is unrelated to the Scratch extensions system. These extensions modify the appearance of the blocks.
// * "colours_looks": Applies the looks colour to the block.
// * "shape_statement": Applies the standard statement/stack block style to the block.
extensions: ["colours_looks", "shape_statement"]
});
}
};
Adding our own block
Choose a place in the file that you wish to add the block. I will add it near the top, under the import line.
You must not add the blocks above the import line.
// Your block's opcode must start with the category ID and an underscore: `looks_restofyouropcode`.
Blockly.Blocks['looks_tutorialmod_alert'] = {
init: function(this: Blockly.Block){
this.jsonInit({
// We will add the message next, however the contents of the message will be "alert %1".
message0: Blockly.Msg.LOOKS_TUTORIALMOD_ALERT,
args0: [
{
type: "input_value",
name: "MESSAGE",
},
],
extensions: ["colours_looks", "shape_statement"],
});
},
};
Adding the block message
We need to add the LOOKS_TUTORIALMOD_ALERT message we referenced in the block definition. For that, we need to open the msg/messages.js file in scratch-blocks.
Find the "Looks blocks" section of the file (you don't need to place it in the correct section, but it helps keep the file organised) and add this.
// Looks blocks
Blockly.Msg.LOOKS_TUTORIALMOD_ALERT = 'alert %1';
Blockly.Msg.LOOKS_SAYFORSECS = 'say %1 for %2 seconds';
Rebuilding Scratch Blocks
You need to rebuild Scratch Blocks every time you make a change to it.
Scripts to rebuild the messages
The scripts Scratch Blocks uses to build the translation files are outdated. Updating these scripts is considered out of scope for this tutorial, but updated versions are included here:
| File | Download |
|---|---|
i18n/js_to_json.js | js_to_json.js |
i18n/json_to_js.js | json_to_js.js |
i18n/create_scratch_msgs.js | create_scratch_msgs.js |
To make things easier to run, in the scratch-blocks package.json, add this script line:
"scripts": {
"build": "webpack --mode production",
"prepare": "husky || true",
"start": "webpack serve --open --mode development",
"test": "echo \"Error: no test specified\" && exit 1",
"test:lint": "eslint .",
"translate": "node i18n/js_to_json.js && node i18n/json_to_js.js && node i18n/create_scratch_msgs.js"
},
Actually rebuilding Scratch Blocks
- NPM
- Yarn
npm run translate
npm run build
yarn run translate
yarn run build
Adding the block to the toolbox
Now, we need to move to scratch-gui to add the block to the toolbox. The toolbox is defined in src/lib/make-toolbox-xml.js. The file has a function for each category (for example, looks for the Looks category).
const looks = function (isInitialSetup, isStage, targetId, costumeName, backdropName, colors) {
const hello = ScratchBlocks.ScratchMsgs.translate('LOOKS_HELLO', 'Hello!');
const hmm = ScratchBlocks.ScratchMsgs.translate('LOOKS_HMM', 'Hmm...');
// Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color.
return `
<category name="${ScratchBlocks.ScratchMsgs.translate(
'CATEGORY_LOOKS',
'Looks'
)}" toolboxitemid="looks" colour="${
colors.colourPrimary
}" secondaryColour="${colors.colourTertiary}">
<block type="looks_tutorialmod_alert">
<value name="MESSAGE">
<shadow type="text">
<field name="TEXT">${hello}</field>
</shadow>
</value>
</block>
${blockSeparator}
<!-- the rest of the blocks... -->
</category>
`;
}
Trying the block
If the scratch-gui development server isn't running, start it now. You should see your new block at the top of the Looks category.
Implementing your block
The block currently doesn't do anything. So let's implement the block. Implementations of blocks are located in scratch-vm, in the folder src/blocks. The looks blocks are in a file called scratch3_looks.js.
You should place your block's function inside the class, preferably below the getPrimitives function or the getMonitored and getHats functions if they are present. I will place the block above the say function, which is the loosk_say block. The name of the block function does not need to match the block's opcode.
// Note: "util" is included as an parameter to show that it exists, you can (and probably should) exclude it if you aren't using it.
tutorialModAlertBlock (args, util) {
// Note: scratch-vm isn't suppose to do GUI stuff, this ignores that eslint warning:
// eslint-disable-next-line
alert(args.MESSAGE);
}
We also need to add the block to the getPrimitives function, which is how Scratch knows what function to call for a block.
/**
* Retrieve the block primitives implemented by this package.
* @returns {object.<string, Function>} Mapping of opcode to Function.
*/
getPrimitives () {
return {
looks_tutorialmod_alert: this.tutorialModAlertBlock,
looks_say: this.say,
looks_sayforsecs: this.sayforsecs,
looks_think: this.think,
looks_thinkforsecs: this.thinkforsecs,
looks_show: this.show,
looks_hide: this.hide,
looks_hideallsprites: () => {}, // legacy no-op block
looks_switchcostumeto: this.switchCostume,
looks_switchbackdropto: this.switchBackdrop,
looks_switchbackdroptoandwait: this.switchBackdropAndWait,
looks_nextcostume: this.nextCostume,
looks_nextbackdrop: this.nextBackdrop,
looks_changeeffectby: this.changeEffect,
looks_seteffectto: this.setEffect,
looks_cleargraphiceffects: this.clearEffects,
looks_changesizeby: this.changeSize,
looks_setsizeto: this.setSize,
looks_changestretchby: () => {}, // legacy no-op blocks
looks_setstretchto: () => {},
looks_gotofrontback: this.goToFrontBack,
looks_goforwardbackwardlayers: this.goForwardBackwardLayers,
looks_size: this.getSize,
looks_costumenumbername: this.getCostumeNumberName,
looks_backdropnumbername: this.getBackdropNumberName
};
}
Completed Files
| Component | File | Download |
|---|---|---|
scratch-blocks | src/blocks/looks.ts | looks.ts |
scratch-blocks | msg/messages.js | messages.js |
scratch-gui | src/lib/make-toolbox-xml.js | make-toolbox-xml.js |
scratch-vm | src/blocks/scratch3_looks.js | scratch3_looks.js |
Scratch commit hashes at the time of this tutorial
scratch-editor: 810ac12eaa1a1eb19d05fcb37f486ad1c4c56314
scratch-blocks: bb2f7ce297e2552767b531c212b18ee4f046e92d