Skip to main content

Assorted APIs

Now that you are familiar with writing custom extensions, we will share some more APIs. These APIs are available in both sandboxed and unsandboxed extensions. They can all be used together.

color1, color2, color3

These three properties determine, respectively, the color of the blocks, the color of the block inputs, and the color of the block menus of each extension. The general advice is that color1 should be the brightest, color2 slightly darker, and color3 the darkest. They should be set to hex color codes.

class ColorExample {
getInfo() {
return {
id: 'colorexample',
name: 'Color Example',
// intentionally bad colors so that the effect is more clear
color1: '#ff0000', // pure red
color2: '#00ff00', // pure green
color3: '#0000ff', // pure blue
blocks: [
{
opcode: 'reporter',
blockType: Scratch.BlockType.REPORTER,
text: 'string [STRING] boolean [BOOLEAN] menu [MENU] field [FIELD]',
arguments: {
STRING: {
type: Scratch.ArgumentType.STRING,
defaultValue: '1'
},
BOOLEAN: {
type: Scratch.ArgumentType.BOOLEAN
},
MENU: {
type: Scratch.ArgumentType.STRING,
menu: 'MENU'
},
FIELD: {
type: Scratch.ArgumentType.STRING,
menu: 'FIELD'
}
}
},
],
menus: {
MENU: {
acceptReporters: true,
items: ['item 1', 'item 2']
},
// We're just including a field example for completion.
// Please do not use acceptReporters: false!
FIELD: {
acceptReporters: false,
items: ['item 1', 'item 2']
}
}
};
}

reporter() {
return 'This block does nothing';
}
}

Scratch.extensions.register(new ColorExample());

Different block color modes (such as High Contrast, Dark and any "Addons" presets) are automatically generated based on these values.

docsURI

docsURI adds a button at the start of the block list that opens a page for people to learn more about how your extension works.

hello-docs.js - Try this extension
class HelloDocs {
getInfo() {
return {
id: 'hellodocs',
name: 'Hello Docs!',
docsURI: 'https://docs.turbowarp.org/development/extensions/docsURI-demo',
blocks: [
{
opcode: 'hello',
blockType: Scratch.BlockType.REPORTER,
text: 'Hello!'
}
]
};
}

hello() {
return Math.random();
}
}

Scratch.extensions.register(new HelloDocs());

disableMonitor

Scratch will automatically show a checkbox to show a variable monitor by REPORTER blocks with no inputs. Note that in TurboWarp, this applies to BOOLEAN blocks too. To disable this, set disableMonitor to true on the block.

Note that, even if disableMonitor is set, it is still possible for someone to manually create a monitor using a modified (or old) version of the extension or other tools.

unmonitorable.js - Try this extension
class DisableMonitorExample {
getInfo() {
return {
id: 'disablemonitorexample',
name: 'disableMonitor Example',
blocks: [
{
opcode: 'monitorable',
blockType: Scratch.BlockType.REPORTER,
text: 'this block can be monitored'
},
{
opcode: 'unmonitorable',
blockType: Scratch.BlockType.REPORTER,
text: 'but this one can not',
disableMonitor: true
},
]
};
}

monitorable() {
return Math.random();
}

unmonitorable() {
return Math.random();
}
}

Scratch.extensions.register(new DisableMonitorExample());

Scratch.Cast

The way that Scratch handles operations such as type conversions or comparisons has numerous quirks. Instead of trying to write them yourself, you can use the Scratch.Cast.* APIs.

class CastingExample {
getInfo() {
return {
id: 'castexample',
name: 'Casting Example',
blocks: [
{
opcode: 'toNumber',
blockType: Scratch.BlockType.REPORTER,
text: 'convert [INPUT] to number',
arguments: {
INPUT: {
type: Scratch.ArgumentType.STRING,
defaultValue: '3.0'
}
}
},
{
// The opcode "toString" should work but given it's special
// treatment in JS, it seems a bit dangerous to use
opcode: 'castToString',
blockType: Scratch.BlockType.REPORTER,
text: 'convert [INPUT] to string',
arguments: {
INPUT: {
type: Scratch.ArgumentType.STRING,
defaultValue: 'Hello'
}
}
},
{
opcode: 'toBoolean',
blockType: Scratch.BlockType.BOOLEAN,
text: 'convert [INPUT] to boolean',
arguments: {
INPUT: {
type: Scratch.ArgumentType.STRING,
defaultValue: '1'
}
}
},
{
opcode: 'compare',
blockType: Scratch.BlockType.REPORTER,
text: 'compare [A] to [B]',
arguments: {
A: {
type: Scratch.ArgumentType.STRING,
defaultValue: '3'
},
B: {
type: Scratch.ArgumentType.STRING,
defaultValue: '5'
}
}
}
]
};
}

toNumber({INPUT}) {
return Scratch.Cast.toNumber(INPUT);
}

castToString({INPUT}) {
return Scratch.Cast.toString(INPUT);
}

toBoolean({INPUT}) {
return Scratch.Cast.toBoolean(INPUT);
}

compare({A, B}) {
const comparison = Scratch.Cast.compare(A, B);
// You need to use < 0, > 0, or === 0 here.
// Do not use === 1 or === -1! That will not work in some cases.
if (comparison === 0) {
return 'Equal';
} else if (comparison > 0) {
return 'A is greater';
} else if (comparison < 0) {
return 'B is greater';
}
}
}

Scratch.extensions.register(new CastingExample());

hideFromPalette

Sometimes you might want to hide a block from the flyout, but you don't want to remove it. This is useful to make sure that your changes are backward compatible. Blocks with the property hideFromPalette will be hidden from the flyout, but any copies of them that are already in the project will continue to work the same.

For example, load the first extension here, and save a project using its block:

hidden-1.js - Try this extension
class HideFromPaletteExample {
getInfo() {
return {
id: 'hidefrompaletteexample',
name: 'hideFromPalette Example',
blocks: [
{
opcode: 'hidden',
blockType: Scratch.BlockType.REPORTER,
text: 'example block (visible)'
},
]
};
}

hidden() {
return 'The block is still visible';
}
}

Scratch.extensions.register(new HideFromPaletteExample());

Then load this extension instead, and load the project from before:

hidden-2.js - Try this extension
class HideFromPaletteExample {
getInfo() {
return {
id: 'hidefrompaletteexample',
name: 'hideFromPalette Example',
blocks: [
{
opcode: 'hidden',
blockType: Scratch.BlockType.REPORTER,
text: 'example block (hidden)',
hideFromPalette: true
},
]
};
}

hidden() {
return 'The block is hidden from the palette but still works';
}
}

Scratch.extensions.register(new HideFromPaletteExample());

See that the copies of the block that already exist continue to work, but it is not listed in the toolbox.

filter

Some of your blocks may only work in sprites or only work in the stage. For these blocks, you can use the filter property to an array containing either Scratch.TargetType.STAGE or Scratch.TargetType.SPRITE to make it only visible in that type of target.

Note that it is still possible to get blocks that violate the filter property by, for example, using drag and drop or the backpack. Thus, your blocks must still check if the target is a stage or sprite with util.target.isStage.

filter.js - Try this extension
class FilterExample {
getInfo() {
return {
id: 'filterexample',
name: 'Filter Example',
blocks: [
{
opcode: 'all',
blockType: Scratch.BlockType.COMMAND,
text: 'available in ALL targets',
},
{
opcode: 'sprites',
blockType: Scratch.BlockType.COMMAND,
text: 'available in ONLY sprites',
filter: [Scratch.TargetType.SPRITE]
},
{
opcode: 'stage',
blockType: Scratch.BlockType.COMMAND,
text: 'available in ONLY the stage',
filter: [Scratch.TargetType.STAGE]
},
{
opcode: 'none',
blockType: Scratch.BlockType.COMMAND,
text: 'available in NEITHER sprites or the stage',
// NOTE: Use hideFromPalette instead of filter: []
filter: []
}
]
};
}

all() {
return 0;
}
sprites() {
return 0;
}
stage() {
return 0;
}
none() {
return 0;
}
}

Scratch.extensions.register(new FilterExample());

Icons

There are three different ways to add images to your extension:

  • menuIconURI for the whole extension. This sets the image that appears in the block palette. If not set, defaults to blockIconURI. If that's also not set, defaults to a circle of the extension's color.
  • blockIconURI for the whole extension. This will be the default for all blocks that don't override it.
  • blockIconURI for each block. This overrides the blockIconURI set on the entire extension.

Each of these properties should be inline data: URLs. SVG is preferred, but PNG or JPG at least 64x64 in size also works well. Icons should be square.

// From the Box2D extension: https://github.com/TurboWarp/extensions/blob/master/extensions/box2d.js
const blocksIcon = "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiDQoJIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHhtbG5zOmE9Imh0dHA6Ly9ucy5hZG9iZS5jb20vQWRvYmVTVkdWaWV3ZXJFeHRlbnNpb25zLzMuMC8iDQoJIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iNDBweCIgaGVpZ2h0PSI0MHB4IiB2aWV3Qm94PSItMy43IC0zLjcgNDAgNDAiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgLTMuNyAtMy43IDQwIDQwIg0KCSB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxkZWZzPg0KPC9kZWZzPg0KPHJlY3QgeD0iOC45IiB5PSIxLjUiIGZpbGw9IiNGRkZGRkYiIHN0cm9rZT0iIzE2OUZCMCIgc3Ryb2tlLXdpZHRoPSIzIiB3aWR0aD0iMTQuOCIgaGVpZ2h0PSIxNC44Ii8+DQo8cmVjdCB4PSIxLjUiIHk9IjE2LjMiIGZpbGw9IiNGRkZGRkYiIHN0cm9rZT0iIzE2OUZCMCIgc3Ryb2tlLXdpZHRoPSIzIiB3aWR0aD0iMTQuOCIgaGVpZ2h0PSIxNC44Ii8+DQo8cmVjdCB4PSIxNi4zIiB5PSIxNi4zIiBmaWxsPSIjRkZGRkZGIiBzdHJva2U9IiMxNjlGQjAiIHN0cm9rZS13aWR0aD0iMyIgd2lkdGg9IjE0LjgiIGhlaWdodD0iMTQuOCIvPg0KPC9zdmc+";

// From the Pen Plus extension: https://github.com/TurboWarp/extensions/blob/master/extensions/penplus.js
const dangoIcon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABeCAYAAADc6BHlAAAAAXNSR0IArs4c6QAABItJREFUeF7tncuN2zAQhu0SctmCAuSQDnLZAlLCAjkG2BJSwF62gxwWSEG5pIQEFJYKRQ5f8+BzfLSoMfV/8yIl2/db+PrrvXUHxti3/LGJobeUndR5Sx9zRUmJWTquVCyF8a4UKOzj89Pt5em5VMxj3Idf34Pxfz5+y9nYHoQV4PR+I777ioGABM+pHQGyNYRqABjhfTAAiG0hRAH4ns8hvIIIc0RRcZUQ307Fi4btIiELQFJ8hXALevNLK9pC/N0h+CF/Amgp/s4QFECudxY+DtaAHt6/axQoAGEPz5kP1gE9vR+IguXb0tEBGCZLQ7gAGMH7jeI7Lc6GBOBB0AjIFRKJ404UKAAJgXM2dwLQdfUbA7ETAKPBAWGUIgwUYpfTUilpmCJccPsyFixTA+kOgCC8D2RKEN0AQMI//n7L1ebz+MvDpyUiostK2Be/RnhIdQDGNNHQFAC38C6MSEQMDyLYDZXqhCTFT4CYD4BUO+oCoKacXKHwomFoCOAdMe4oaCm+hTMLBPFbkj3EnwkCFJ5sWxM9xTcQZoiCWH5kgdAbwAwQsgAoRdkCkC66FUV5uIKcmhDpIa0RvH+GWpDzCDSEUbx/9DSUA3BuVbthnmtTR/J+IApKrjmX1diO10wG/ApT7psxvfP/SgDstdR8Me82IABzHTWOx+btkCHKRIpAKIA0PwoAyHIARQG0BXBJU6OIP3InxB0BQZ3oDSFx54w7JaNqxZIAKkXvemtzKQAp4b/8/BH10NfPX1PeK6XR8ZmSxo+C3CoFQeKnRI8pHoEhppOYYfuwlzQAX3iM6BAMAISIViJG3y/o/88fVDxuUlPJpMR35yANYloALcS3IDwIrJqxGgO8VywKXABcaScVfVIQpgTQWnzJSJAGYObOGgW9xJeC0AIAKwQLoEXaKeiOyPqRDRR2LZdNOmxr2tv7JaKgFYBLFGAXaL29f3YAJAijeD8AgeTEpJML048/DHXPYGAApC2dHgCCLWv7Rqo2jJJ+uNNQTwBBSoIiykJRAMh8U3ha0f1lY6tX+5lpSdGOjD6xUFjMsCQMBYCRlH7OCUUB0MXEWFAAGNWYzzkgaAQwq1phbigAXNvTIxbhGJNmAFxxYxG3LQDJNBR7OgKCoAAqclfJ0MyjKUHtccaTsgjp5JILYx4jloasoKn/T7CRwOX9pE0kZmFLzYm0o66gPgAzMfen/A2EnQGc+0ec7WgOgA/B8RZyBiEbKHVdxnFdoiACgawf2QCjsDWm2CH4RRhKRQAEsn5kAzWqMY69bNhxpaMeEGYFcNYCC3VWCDMDWALC7ACmh7ACAJuFWOpCakUsUZhXAhBEQ019yG1FWFvcEFYDAEYDsvtytbk+2ef93aO17/35XZG2RYOQFzDCacU3+wtWtyIQVgcQcwIjJuba2SFgJjGCZ/ecAysEBYBDyQZBAeAABB0XtjtSAHgALBAUAA0AGYICoAMgQVAAPADQEBQAHwAMhLsC4AVQBMHdslAA/ACyEBwAGgEy+h9WwcWav2GnESBIwIcAfJRGgKz+YST4u67/APKrBHxjStmdAAAAAElFTkSuQmCC";
const colorIcon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAF4AAABeCAYAAACq0qNuAAAAAXNSR0IArs4c6QAABTFJREFUeF7t3b2KFEEUBeAecBVkk8nERBSMBEGMTDcxN/IhfATDRdinMDSaV5gn8REEo40MlBqoobrmVtWtqvtzWnYzd3tmer46ffrOr7tlOz9/mbu6Y27nuhniTnKBe+Gg7ivKzmhhlxbH/X577UAT+smnD81EP3r9vLnN/d2huc2yLOYO1jdYBOdA54Ic+PwyjYUw87C6IRJ8BDuFHIFPL19ZBHUX9RtYluUCfRY84s3Cx+spLICqjeaVq4FLw3ssgAa8OrgWvOUCSMOv0KUqpTSWSFVN6fqJChLzEruivMu10QOWNny4DS18CXjTlEtONZwBv1I/U3ZTF/ZIuRe8dPpn4N2Srn1yrR0JUtUzCu+ObtXx1CJI4I/AQ6B7wkvUTi88DLo3/Cz+MLzFuNiaOizGydY+ZLXD9mRvmE4wCOgIiS+MmixT1kaI6EjwRO00XZsboKKjwffiP8C3Srzj7z1934I/TzEove75yJWzBlx8FjwiOmLVECfbom8N/pz26xfPlj/vX3IW3HQbhHGS8ciWNC7Br9DTK0daAET4xx/fnbh+33xN2S6cu+HDtaHgI8FH8Kg9Al9Me35YeS8AAnwOnhrV8KnEs+HjjXgtgCf8/d1htz/eVt+Y1QPfje6J7wVfS3neCiX8PPHD8B4LYA3fA97qehI+jI+zPxb1YwU/Al7o+rO3GrzF9GMBP4uejZYk/HTNlI4SrfRrwkuA1+omTfwJXqJmrBZAA14SvFY3pvDS9SMJrwVOpP5kHuHVakYz/VLw2ujUUwhu8BLj5yy8BXip51fwmv3eGk9HTsCj8JbgpZ6HgR/p/154L3Cq56Hge+unB94bPZ/nIeG5C8CBRwAvJd58omn1ffr3WvfX4JHAqRNsSDw0fC39FDwi+KbhqQXI4ZHR81l+M4mn6ifCo4P/F4lPF+Dq+5ee04X7tumLIptMfBC8un663Pz4dsI8/vrpjsrZgU3DB/D4E+Hjv9EXYJPwKXgJHj39m4On0ANynvj0cEdM/2bgS+C1xOddi7QAOXzYV/VXnzgnn9XEknR56bK1xCOmP4HfwT1X00p5CsqFRzn5QsL3gPdUDXW0eNUPHPwIeuvk2qo2D/wifNhZy1ehRsFnE+/R//lb+Vxec50Fl4S36v8SvNlkI4U+WzXW3Z/WTLhts/fVSIJrJF67fprw0j2vAa4NL10/1Fu1Vd+0qomuUTVa9ZOnPa8asZ7XBrdKvFT9sOFH68YK3AN+tH7UPxFijW5VNbP1w4U/1w039R7gnonvqZ+eD5+x4T3BUeBb9dML38RHQPesGqp+wu/S539S9P3xNnzSezVBdn2yGwUcLfFU/Yx8sjtez+odZmjoiImPcIe3n89rQaWdmuPzo+iEv3/zqnR0uf6+94UQq51N4E+f/s5rhg2Pio8In6a99n+PtL4oaHWiRUs+GjwXnZP4B/iOfpKGh8VHSnwPOjfxF1MOSuWgwPei98LDJR8BfgR9Ch5h0vGGz9C7PDlTDTnbx1961o4n/Ax61wpl+quvhPLC94KfRZ+BX/W9V+14wEugz8K741vDS6FLwLviW8JLokvBX8z5VtVjAS8NHrFGpprag2jTk642vBa6dOLJ5GumXwueABe3kk58ejRcfAup9NgpDW8BrlU1VA2pLYAUvCW4JfzF5CP1qHcWvgAuXitUGjWrhpX+mUUYga9gm4BbJ776fA+1QpzzAQe+Ae3mYJ340iha/Trw2vw6+Df3++2+AwSc1iJA3VeonWmkl7sgm7hP/wDwgcxktVR5CQAAAABJRU5ErkJggg==";

class IconsExample {
getInfo() {
return {
id: 'iconsexample',
name: 'Icons Example',
menuIconURI: blocksIcon,
blockIconURI: dangoIcon,
blocks: [
{
opcode: 'defaultIcon',
blockType: Scratch.BlockType.REPORTER,
text: 'reporter with default icon [INPUT]',
arguments: {
INPUT: {
type: Scratch.ArgumentType.STRING,
defaultValue: '0'
}
}
},
{
opcode: 'overridden icon',
blockType: Scratch.BlockType.COMMAND,
text: 'command with overridden icon [INPUT]',
blockIconURI: colorIcon,
arguments: {
INPUT: {
type: Scratch.ArgumentType.STRING,
defaultValue: '0'
}
}
}
]
};
}

defaultIcon() {
return 0;
}
overriddenIcon() {

}
}

Scratch.extensions.register(new IconsExample());

Inline images

You can also put images anywhere in a block using an "argument" of type IMAGE and setting dataURI on the argument. Like the other images, this is a data: URL. SVG is preferred, but PNG or JPG at least 64x64 in size also works well. Icons should be square.

Additionally, if you set flipRTL to true, the image will be horizontally flipped in right-to-left languages.

inline-images.js - Try this extension
// From the Pen Plus extension: https://github.com/TurboWarp/extensions/blob/master/extensions/penplus.js
const dangoIcon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABeCAYAAADc6BHlAAAAAXNSR0IArs4c6QAABItJREFUeF7tncuN2zAQhu0SctmCAuSQDnLZAlLCAjkG2BJSwF62gxwWSEG5pIQEFJYKRQ5f8+BzfLSoMfV/8yIl2/db+PrrvXUHxti3/LGJobeUndR5Sx9zRUmJWTquVCyF8a4UKOzj89Pt5em5VMxj3Idf34Pxfz5+y9nYHoQV4PR+I777ioGABM+pHQGyNYRqABjhfTAAiG0hRAH4ns8hvIIIc0RRcZUQ307Fi4btIiELQFJ8hXALevNLK9pC/N0h+CF/Amgp/s4QFECudxY+DtaAHt6/axQoAGEPz5kP1gE9vR+IguXb0tEBGCZLQ7gAGMH7jeI7Lc6GBOBB0AjIFRKJ404UKAAJgXM2dwLQdfUbA7ETAKPBAWGUIgwUYpfTUilpmCJccPsyFixTA+kOgCC8D2RKEN0AQMI//n7L1ebz+MvDpyUiostK2Be/RnhIdQDGNNHQFAC38C6MSEQMDyLYDZXqhCTFT4CYD4BUO+oCoKacXKHwomFoCOAdMe4oaCm+hTMLBPFbkj3EnwkCFJ5sWxM9xTcQZoiCWH5kgdAbwAwQsgAoRdkCkC66FUV5uIKcmhDpIa0RvH+GWpDzCDSEUbx/9DSUA3BuVbthnmtTR/J+IApKrjmX1diO10wG/ApT7psxvfP/SgDstdR8Me82IABzHTWOx+btkCHKRIpAKIA0PwoAyHIARQG0BXBJU6OIP3InxB0BQZ3oDSFx54w7JaNqxZIAKkXvemtzKQAp4b/8/BH10NfPX1PeK6XR8ZmSxo+C3CoFQeKnRI8pHoEhppOYYfuwlzQAX3iM6BAMAISIViJG3y/o/88fVDxuUlPJpMR35yANYloALcS3IDwIrJqxGgO8VywKXABcaScVfVIQpgTQWnzJSJAGYObOGgW9xJeC0AIAKwQLoEXaKeiOyPqRDRR2LZdNOmxr2tv7JaKgFYBLFGAXaL29f3YAJAijeD8AgeTEpJML048/DHXPYGAApC2dHgCCLWv7Rqo2jJJ+uNNQTwBBSoIiykJRAMh8U3ha0f1lY6tX+5lpSdGOjD6xUFjMsCQMBYCRlH7OCUUB0MXEWFAAGNWYzzkgaAQwq1phbigAXNvTIxbhGJNmAFxxYxG3LQDJNBR7OgKCoAAqclfJ0MyjKUHtccaTsgjp5JILYx4jloasoKn/T7CRwOX9pE0kZmFLzYm0o66gPgAzMfen/A2EnQGc+0ec7WgOgA/B8RZyBiEbKHVdxnFdoiACgawf2QCjsDWm2CH4RRhKRQAEsn5kAzWqMY69bNhxpaMeEGYFcNYCC3VWCDMDWALC7ACmh7ACAJuFWOpCakUsUZhXAhBEQ019yG1FWFvcEFYDAEYDsvtytbk+2ef93aO17/35XZG2RYOQFzDCacU3+wtWtyIQVgcQcwIjJuba2SFgJjGCZ/ecAysEBYBDyQZBAeAABB0XtjtSAHgALBAUAA0AGYICoAMgQVAAPADQEBQAHwAMhLsC4AVQBMHdslAA/ACyEBwAGgEy+h9WwcWav2GnESBIwIcAfJRGgKz+YST4u67/APKrBHxjStmdAAAAAElFTkSuQmCC";
const colorIcon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAF4AAABeCAYAAACq0qNuAAAAAXNSR0IArs4c6QAABTFJREFUeF7t3b2KFEEUBeAecBVkk8nERBSMBEGMTDcxN/IhfATDRdinMDSaV5gn8REEo40MlBqoobrmVtWtqvtzWnYzd3tmer46ffrOr7tlOz9/mbu6Y27nuhniTnKBe+Gg7ivKzmhhlxbH/X577UAT+smnD81EP3r9vLnN/d2huc2yLOYO1jdYBOdA54Ic+PwyjYUw87C6IRJ8BDuFHIFPL19ZBHUX9RtYluUCfRY84s3Cx+spLICqjeaVq4FLw3ssgAa8OrgWvOUCSMOv0KUqpTSWSFVN6fqJChLzEruivMu10QOWNny4DS18CXjTlEtONZwBv1I/U3ZTF/ZIuRe8dPpn4N2Srn1yrR0JUtUzCu+ObtXx1CJI4I/AQ6B7wkvUTi88DLo3/Cz+MLzFuNiaOizGydY+ZLXD9mRvmE4wCOgIiS+MmixT1kaI6EjwRO00XZsboKKjwffiP8C3Srzj7z1934I/TzEove75yJWzBlx8FjwiOmLVECfbom8N/pz26xfPlj/vX3IW3HQbhHGS8ciWNC7Br9DTK0daAET4xx/fnbh+33xN2S6cu+HDtaHgI8FH8Kg9Al9Me35YeS8AAnwOnhrV8KnEs+HjjXgtgCf8/d1htz/eVt+Y1QPfje6J7wVfS3neCiX8PPHD8B4LYA3fA97qehI+jI+zPxb1YwU/Al7o+rO3GrzF9GMBP4uejZYk/HTNlI4SrfRrwkuA1+omTfwJXqJmrBZAA14SvFY3pvDS9SMJrwVOpP5kHuHVakYz/VLw2ujUUwhu8BLj5yy8BXip51fwmv3eGk9HTsCj8JbgpZ6HgR/p/154L3Cq56Hge+unB94bPZ/nIeG5C8CBRwAvJd58omn1ffr3WvfX4JHAqRNsSDw0fC39FDwi+KbhqQXI4ZHR81l+M4mn6ifCo4P/F4lPF+Dq+5ee04X7tumLIptMfBC8un663Pz4dsI8/vrpjsrZgU3DB/D4E+Hjv9EXYJPwKXgJHj39m4On0ANynvj0cEdM/2bgS+C1xOddi7QAOXzYV/VXnzgnn9XEknR56bK1xCOmP4HfwT1X00p5CsqFRzn5QsL3gPdUDXW0eNUPHPwIeuvk2qo2D/wifNhZy1ehRsFnE+/R//lb+Vxec50Fl4S36v8SvNlkI4U+WzXW3Z/WTLhts/fVSIJrJF67fprw0j2vAa4NL10/1Fu1Vd+0qomuUTVa9ZOnPa8asZ7XBrdKvFT9sOFH68YK3AN+tH7UPxFijW5VNbP1w4U/1w039R7gnonvqZ+eD5+x4T3BUeBb9dML38RHQPesGqp+wu/S539S9P3xNnzSezVBdn2yGwUcLfFU/Yx8sjtez+odZmjoiImPcIe3n89rQaWdmuPzo+iEv3/zqnR0uf6+94UQq51N4E+f/s5rhg2Pio8In6a99n+PtL4oaHWiRUs+GjwXnZP4B/iOfpKGh8VHSnwPOjfxF1MOSuWgwPei98LDJR8BfgR9Ch5h0vGGz9C7PDlTDTnbx1961o4n/Ax61wpl+quvhPLC94KfRZ+BX/W9V+14wEugz8K741vDS6FLwLviW8JLokvBX8z5VtVjAS8NHrFGpprag2jTk642vBa6dOLJ5GumXwueABe3kk58ejRcfAup9NgpDW8BrlU1VA2pLYAUvCW4JfzF5CP1qHcWvgAuXitUGjWrhpX+mUUYga9gm4BbJ776fA+1QpzzAQe+Ae3mYJ340iha/Trw2vw6+Df3++2+AwSc1iJA3VeonWmkl7sgm7hP/wDwgcxktVR5CQAAAABJRU5ErkJggg==";

class InlineImagesExample {
getInfo() {
return {
id: 'inlineimagesexample',
name: 'Inline Images Example',
blocks: [
{
opcode: 'reporter',
blockType: Scratch.BlockType.REPORTER,
text: 'some text [IMAGE] more text',
arguments: {
IMAGE: {
type: Scratch.ArgumentType.IMAGE,
dataURI: dangoIcon
}
}
},
{
opcode: 'command',
blockType: Scratch.BlockType.COMMAND,
blockIconURI: colorIcon,
text: 'some text [IMAGE] more text',
arguments: {
IMAGE: {
type: Scratch.ArgumentType.IMAGE,
dataURI: dangoIcon,
flipRTL: true
}
}
}
]
};
}

reporter() {
return 'Example block';
}
command() {

}
}

Scratch.extensions.register(new InlineImagesExample());

Separators

If your extension has a lot of blocks you may want to put some space between groups of blocks. To do this, include "---" in the blocks list:

separators.js - Try this extension
class SeparatorExample {
getInfo() {
return {
id: 'separatorexample',
name: 'Separator Example',
blocks: [
{
opcode: 'block1',
blockType: Scratch.BlockType.COMMAND,
text: 'group 1'
},
{
opcode: 'block2',
blockType: Scratch.BlockType.COMMAND,
text: 'also group 1',
},
'---',
{
opcode: 'block3',
blockType: Scratch.BlockType.COMMAND,
text: 'group 2'
},
'---',
{
opcode: 'block4',
blockType: Scratch.BlockType.COMMAND,
text: 'group 3',
},
]
};
}
block1() {}
block2() {}
block3() {}
block4() {}
}

Scratch.extensions.register(new SeparatorExample());

Terminal blocks

To prevent connecting a block underneath a COMMAND block, set isTerminal: true on the block.

While the block's shape will look similar to "stop this script" and "stop all", the behavior matches neither. If this block is placed at the end of a loop for example, the loop will continue to run unless additional code stops the current thread. This just prevents connecting a block underneath.

terminal.js - Try this extension
class TerminalExample {
getInfo() {
return {
id: 'terminalexample',
name: 'Terminal Example',
blocks: [
{
opcode: 'terminalBlock',
blockType: Scratch.BlockType.COMMAND,
isTerminal: true,
text: 'you can not connect another block under this one!'
}
]
};
}
terminalBlock() {

}
}

Scratch.extensions.register(new TerminalExample());

Next steps

Next, let's see how to make blocks like "when I receive" or "when timer greater than".