Discuss Scratch
- Discussion Forums
- » Developing Scratch Extensions
- » Guide to Creating TurboWarp Unsandboxed Extensions
- 8to16
- Scratcher
1000+ posts
Guide to Creating TurboWarp Unsandboxed Extensions
The Guide to Creating TurboWarp Unsandboxed Extensions
* This is the third guide to creating Unsandboxed Extensions on this forum. The second one can be found here. The first one can be found here. I have been given permission by the creator of the second guide to creating unsandboxed extensions.
** Most of this guide is an elaboration of the topics covered in the official sandbox documentation. Some things may be left out for the sake of being more user-friendly. If you have any question, please consult the original documentation before replying.
Section 0 - What Is an Unsandboxed Extension?
This guide was created on the basis that the reader has no former knowledge of sandboxed or unsandboxed extensions. If you already do, feel free to skip to section one.
With that being said, there are two types of Scratch extensions; the sandboxed and unsandboxed varieties. There's several difference between the two of them, such as the former not being allowed to access all APIs and taking a frame to run each time it's called on, but the general consensus is that unsandboxed extensions are far more powerful. Along with that, unsandboxed extensions are simply easier to share, due to the Official TurboWarp Unsandboxed Extension Gallery (which is an amazing site if you haven't checked it out beforehand). Other than that, you can also find all examples of TurboWarp Unsandboxed Extensions here.
That being said, great power comes with great responsibility. There's certain formatting and syntax that TurboWarp extensions require in order to run, just to keep the site safe and secure. Although it may seem daunting at first, it's pretty simple as long as you program carefully and follow a guide.
Section 1.1 - Loading Extensions via File
Originally, this step would consist of creating a local server and hosting our scripts via the command prompt. Luckily, we don't have to deal with that anymore. We can now load extensions via a file, or even through copied text.
Navigate to the TurboWarp Editor. At the bottom of the toolbox, click on the “Add Extension” button. This should look familiar– it's the same button for adding extensions such as Pen to Scratch projects.
There's a lot of options here, however we specifically need to select “Custom Extension,” which should be the last option before the first line.
This opens a menu with 3 tabs. For extension development, the last 2 tabs will prove useful to you. “File” can be used to open JS extension files, and “Text” can load any extension copied to your clipboard. As practice, navigate to “Text” and paste in this extension:
class HelloWorld { getInfo() { return { id: 'helloworld', name: 'It works!', blocks: [ { opcode: 'hello', blockType: Scratch.BlockType.REPORTER, text: 'Hello!' } ] }; } hello() { return 'World!'; } } Scratch.extensions.register(new HelloWorld());
Section 1.2 - Setting Up Our Script.
You'll likely want to create a new JS (JavaScript) file. If your operating system doesn't have a simple way to do this, create a text file and replace the letters “txt” with “js” and press ok through any pop-ups that may occur. Then open the file. If you have Visual Studio Code or other code editing software installed, use that. Otherwise, you'll have to use a notepad, which is okay, however it won't autofill scripts or catch errors for you.
The other option would be to repeatedly copy-paste our code into the “Text” tab, however this can be quite tedious and doesn't allow use of code editing softwares. Plus, to share the extension, you'll want it in a JS file anyways.
When we get into our script, we have to paste our template:
(function(Scratch) { 'use strict'; if (!Scratch.extensions.unsandboxed) { throw new Error('[NAME HERE] must run unsandboxed'); } class [ID HERE] { getInfo() { return { id: '[ID HERE]', name: '[NAME HERE]', blocks: [ // Palette Here ] }; } // Block Code Here } Scratch.extensions.register(new [ID HERE]()); })(Scratch);
Section 2.1 - Adding Blocks To Our Palette.
Now that we have our template down, we can start adding our blocks. I've put a comment (the double /) called “Palette Here” where you can define each of your blocks. Here's the syntax for each block of code:
{ opcode: 'hello', blockType: Scratch.BlockType.REPORTER, text: 'Hello!' },
As for the blocktype, there's 3 different values we can set this to. These correspond to the 3 types of Scratch blocks.
Block (Scratch.BlockType.COMMAND)
The basic Scratch block, doesn't return an output.
say []
Boolean (Scratch.BlockType.BOOLEAN)
The hexagonal block. Reports true/false values.
<[] = []>
Reporter (Scratch.BlockType.REPORTER)
Any block that returns a number/string output.
((0) + (0))
Unlike the other two, the blocktype does not need to be surrounded in quotes, as it is not a string (don't forget the comma after it, however).
The last variable we need to set it the blockname. Set it to something that represents what the block does. Any inputs, whether it be a dropdown, boolean input, or a menu, must be surrounded by [square brackets].
There's more that can be added to the block syntax to add extra functions, however none are particularly helpful for beginners and will be skipped over in this tutorial.
Section 2.2 - Defining Any Arguments We Made.
If your blocks don't have any arguments (which is rather unlikely), you may skip this section. Otherwise, we have to do a little bit more work so our arguments work. First, we have to add a comma after our “text” variable:
text: 'Hello!',
arguments: { WHAT YOU PUT IN BRACKET ONE: { type: Scratch.ArgumentType.STRING, defaultValue: 'Default' }
arguments: { WHAT YOU PUT IN BRACKET ONE: { type: Scratch.ArgumentType.STRING, defaultValue: 'Default' }, WHAT YOU PUT IN BRACKET TWO: { type: Scratch.ArgumentType.STRING, defaultValue: 'Default' }
String (Scratch.ArgumentType.STRING)
Leaves a spot for string to be typed.
say []
Boolean (Scratch.ArgumentType.BOOLEAN)
Leaves a spot for a boolean input.
wait until <>
String (Scratch.ArgumentType.NUMBER)
Leaves a spot for a number to be typed.
move () steps
Great! We have arguments! Some people may be wondering how you can program in dropdown menus, such as those used in the “move to the front/back layer” block. Although it may seem like it's own ArgumentType, it's actually quite a bit more complicated than that. To keep this guide as short as possible, I will not be including dropdown menus in this tutorial, however, further guidance can be found here.
Section 2.3 - Troubleshooting.
It's always a good idea to test your extension as you go to make sure no issues occur. Although code editors can point out JavaScript and JSON errors, TurboWarp is ultimately the program that's best for checking your code. Follow the same instructions as in Section 1.1. If your custom extension is not appearing in the toolbox, or looks incorrect, check your code for the following errors:
- Forgetting a comma or semicolon. Seriously, those things can mess up your entire script if you're not careful.
- Forgetting to define an argument.
- Accidentally using the same opcode for several different blocks.
Section 3.1 - Programming Our Blocks.
So, you've got your blocks in the pallet now, however they don't do anything. Luckily, Scratch makes it relatively simple to program these blocks using JavaScript. Let's have a look back at our code, specifically where a comment says “Block Code Here”. If it wasn't self-explanatory, that's where we're going to start coding. Let's erase that comment, and replace it with the following script:
REPLACE(args) { return 'Placeholder'; }
With booleans and reporters, of course you'll need to return a value when the block is processed. Luckily, Scratch is programmed so that returning any string, number, or bool will automatically relay that information to the user. If you're making a standard command block, there's no need for a reporter and you can delete the “return” line of code in the script.
Of course, most extensions will have more than one block. In that case, you can simply repeat the example script below the one you already wrote, remembering to replace the opcode. Refreshing the tab you have open with your extension at any point will reload whatever code you wrote and saved, so make sure to save and refresh often.
Section 3.2 - Events and Hat Blocks.
This section is work in progress.
Event Blocks and Hat Blocks are 2 different types of blocks that allow you to control when scripts can run. Note that even though they look the exact same, they are actually different types of blocks.
Event Blocks essentially do nothing on their own - they're just markers for what to run. They are intended to be triggered with other functions or blocks. For example, you may be familiar with this block:
when [space v] key pressed
This block does not do anything on its own as it is an event block. We can create our own block, with similar functionality, by using Scratch.BlockType.EVENT.
class WhenSpaceKeyPressed { getInfo() { return { id: 'eventexampleunsandboxed', name: 'Event Block Example', blocks: [ { blockType: Scratch.BlockType.EVENT, opcode: 'whenSpacePressed', text: 'when space key pressed', isEdgeActivated: false // required boilerplate } ] }; } // Notice: whenSpacePressed does not have a function defined! } document.addEventListener('keydown', (e) => { if (e.key === ' ') { Scratch.vm.runtime.startHats('eventexampleunsandboxed_whenSpacePressed'); } }); Scratch.extensions.register(new WhenSpaceKeyPressed());
when space key pressed :: #0FBD8C hatblock will be ran.
Section 3.3 - API Requests And Asynchronous Blocks.
Lots of extensions require access to different APIs across the web. Requesting information from a website using TurboWarp is actually very easy, however somewhat limited. The biggest 2 limiting factors are that:
- TurboWarp will ask from permission from the user you try to access any domain.
- Certain domains, such as the Scratch API, are off-limits to the likes of TurboWarp.
const response = await Scratch.fetch('url');
const textData = await response.text();
const jsonData = await response.json();
async REPLACE(args) { return 'Placeholder'; }
try { // CODE HERE } catch (error){ return ''; }
Section 3.4 - Keeping to TurboWarp Formatting.
If you're programming your extension all for yourself, TurboWarp formatting won't have too much of an affect on your code. However, if you want to submit your extension to the Official TurboWarp Unsandboxed Extension Gallery, there's a few rules you have to follow. For one, most if not all copyrighted material is usually difficult to add to an extension, there's some stuff you can fill out to gain access to it, however that's legal stuff and legal stuff is boring. Basically, stay away from copyrighted material or code snippets if at all possible.
As for the code itself, GarboMuffin prefers if you use semicolons and double spaces (which is absolutely preposterous but it what it is I guess).
GarboMuffin will also usually turn down extensions that can be deemed dangerous, such as two extensions that are currently in limbo, Local Storage and JavaScript Execution. As long as your extension isn't dangerous to the user, you shouldn't have to worry about that.
Any libraries that the extension uses must be embedded into the code of the extension, and the extension should be able to work on all browsers.
Extensions must not return empty in a boolean or reporter. An empty string (such as ‘ ’) is acceptable, but it needs to return something.
Section 4.1 - Customizing Your Extension.
You may have noticed that up until now your extension has been looking rather bland. Not to worry, this can be fixed using some built-in APIs! For one, we can change the color of our extension. Using a color to hex code converter online, you can get the hex codes for the outlines and the block colors of your extensions. We can insert some variables inside the getInfo part of our original template:
... getInfo() { return { id: 'yourextension', name: 'Your Extension', color1: '#000000', // Block Color color2: '#000000', // Outline Color color3: '#000000', // Dropdown color blocks: ...
... const icon = "insert very long base64 string here"; class YourExtension { getInfo() { return { id: 'yourextension', name: 'Your Extension', color1: '#000000', color2: '#000000', color3: '#000000', menuIconURI: icon, // Don't forget this! blocks: ...
... getInfo() { return { id: 'yourextension', name: 'Your Extension', color1: '#000000', color2: '#000000', color3: '#000000', menuIconURI: icon, docsURI: 'url', // Replace the text with the link to your documentation, perhaps. blocks: ...
Section 5 - Finishing Statements.
Congratulations on making an unsandboxed extension! What's outlined in this tutorial is by no means the full power of the unsandboxed extension maker, however, as you experiment more, you'll find more interesting features and tricks to level up your extensions. While I'd love to share how to share your extension, it's unfortunately against Scratch TOS to share links with outside sources. With that being said, you can freely post the JS of your extension in the forums, or other places.
Topics to be added to this guide:
- Scratch.Cast
- Menus and dropdowns
If any other topics are misrepresented, missed, or need to be elaborated on, please feel free to let me know as a reply to this post.
Last edited by 8to16 (Oct. 2, 2024 09:55:42)
^^^^^ Below this line is a signature. It doesn't have anything to do with the post above.
- 8to16
- Scratcher
1000+ posts
Guide to Creating TurboWarp Unsandboxed Extensions
here before anyone raids
https://scratch-mit-edu.ezproxy.canberra.edu.au/users/NamelessCat/#comments-349379025
https://scratch-mit-edu.ezproxy.canberra.edu.au/users/NamelessCat/#comments-349379025
^^^^^ Below this line is a signature. It doesn't have anything to do with the post above.
- BigNate469
- Scratcher
1000+ posts
Guide to Creating TurboWarp Unsandboxed Extensions
class HelloWorld { getInfo() { return { id: 'helloworld', name: 'It works!', blocks: [ { opcode: 'hello', blockType: Scratch.BlockType.REPORTER, text: 'Hello!' } ] }; } hello() { return 'World!'; } } Scratch.extensions.register(new HelloWorld());
Use [code=javascript] or [code=js]
This signature is designed to be as useful as possible.
How to make a signature & other useful info about them
The Official List of Rejected Suggestions (TOLORS)
The Announcements Directory
Lesser-known Scratch URLs: https://scratch-mit-edu.ezproxy.canberra.edu.au/discuss/topic/542480/
Why @Paddle2See's responses are so often identical: https://scratch-mit-edu.ezproxy.canberra.edu.au/discuss/topic/762351/
Ads Useful projects:
Raycaster & Maze 1.4.1 | Don't Break The Ice | Procedurally Generated Terrain | Basic Trigonometry | Comparing the fastest list sorters on Scratch
“if nobody can learn the programming language, it's just gibberish that does math.” -me, in a forum post
The original name of “loves” was “love-its”. Technically speaking, this hasn't changed.
© @BigNate469, some rights reserved
- 8to16
- Scratcher
1000+ posts
Guide to Creating TurboWarp Unsandboxed Extensions
(#3)I've added syntax highlighting to most (if not all) of the guide, if there's anything left let me know.It couldn't hurt to highlight it.class HelloWorld { getInfo() { return { id: 'helloworld', name: 'It works!', blocks: [ { opcode: 'hello', blockType: Scratch.BlockType.REPORTER, text: 'Hello!' } ] }; } hello() { return 'World!'; } } Scratch.extensions.register(new HelloWorld());
Use [code=javascript] or [code=js]
^^^^^ Below this line is a signature. It doesn't have anything to do with the post above.
- endyourenite
- Scratcher
100+ posts
Guide to Creating TurboWarp Unsandboxed Extensions
Congrats thanks for the sticky
found something silly
Suggestions I own:
Scrollable What I have been doing
that's all….
Posts I found Funny:
WHEN TOPIC FALLS OFF FRONT PAGEmy first ever post on scratch forums
- (src)
Bump it.- Get people to talk about this weird suggestion.
- Watch as the number of posts on this topic expands.
- Leave any rickrolls or evil kumquats behind.
(ignore this)
Anyways, just stop reading this, I am only a silly forumer.
But what do you want from me?
;
- BigNate469
- Scratcher
1000+ posts
Guide to Creating TurboWarp Unsandboxed Extensions
Interesting that the old one isn't unstickied yet.
I soooo want to destroy the 60-second rule
I soooo want to destroy the 60-second rule
This signature is designed to be as useful as possible.
How to make a signature & other useful info about them
The Official List of Rejected Suggestions (TOLORS)
The Announcements Directory
Lesser-known Scratch URLs: https://scratch-mit-edu.ezproxy.canberra.edu.au/discuss/topic/542480/
Why @Paddle2See's responses are so often identical: https://scratch-mit-edu.ezproxy.canberra.edu.au/discuss/topic/762351/
Ads Useful projects:
Raycaster & Maze 1.4.1 | Don't Break The Ice | Procedurally Generated Terrain | Basic Trigonometry | Comparing the fastest list sorters on Scratch
“if nobody can learn the programming language, it's just gibberish that does math.” -me, in a forum post
The original name of “loves” was “love-its”. Technically speaking, this hasn't changed.
© @BigNate469, some rights reserved
- TheCreatorOfUnTV
- Scratcher
1000+ posts
Guide to Creating TurboWarp Unsandboxed Extensions
How do you create C blocks?
This is the start of my signature. Ctrl+Shift+Down to see all of it.
Check out my about me project here! (I'm allowed to advertise here)
1st post
1000th post
;
- Discussion Forums
- » Developing Scratch Extensions
- » Guide to Creating TurboWarp Unsandboxed Extensions