Scratch and its inner workings

by RokCoder

How does Scratch run blocks of code simultaneously?

The short answer is that it doesn’t! First I’m going to explain how Scratch seems to run multiple blocks at the same time and then I’ll show why this is an important concept to grasp if you want to make games that run at 30fps without lagging at all.

The use of “threads”

The first thing to understand is that Scratch doesn’t run separate blocks of code at the same time. It takes turns between blocks (or threads as the Scratch source code refers to them). Before you execute any code in Scratch no threads are running. If you click the green flag then any block of code attached to a green flag hat block will be added to the active threads.

Consider a small project containing just the following green hat blocks and nothing else.

When the green flag is pressed, both of the above green hat blocks are added to the active threads. It’s important to realise that we don’t control which order they’re added in. It is possible to work out the order but it’s far more useful to consider it as arbitrary. So either the left block of code will run followed by the right or vice versa. In actual fact the left block will run and then be removed from the active threads after completion and then the same will happen to the right (or vice versa). Essentially we will have two active threads very briefly which will return to zero active threads after the blocks have been executed.

Blocks that can add threads

Hat blocks are unique because they are added to the active thread list in response to events whether that event is the green flag being pressed, the volume reaching a certain level or a broadcast message being received. These are the hat blocks that can be used in Scratch -

Maybe your project consists of nothing but the following block -

Because active threads are always being monitored and executed, pressing the space bar will trigger this event and add the attached block of code to the active threads even though the green flag has not been pressed to execute the project.

Similarly, clicking on a block of code will add it to the active threads. That’s why clicking on -

will cause the sound effect to be played. By clicking on the block of code you are adding it to the active threads.

Clones and threads

As shown in the previous section, the when I start as a clone hat block is also added to the active thread list in response to an event (with that event being the creation of a clone). I’m mentioning this separately not because it works differently but because it is the only hat block listed under the Control section rather than the Event section.

It is worth noting that when a clone is created, the data for that clone is initialised when the block is encountered and not when the queued thread is executed.

Consider the following project and examine the resultant lists produced.

[Note that the local clone id variable is created as for this sprite only and the Global clone id variable is created for all sprites. Local and global are how these types of variables are referred to in general programming and are much less of a mouthful than for this sprite only and for all sprites!]

This demonstrates that the data is cloned at the exact point of creation whereas the when I start as a clone thread is queued and executed as any other thread would be. Because of this, the clone is initialised with a copy of all local data at the point where create clone of myself is executed but the when I start as a clone thread is executed at the yield point (which is at the end of the loop). This explains why the local and global lists contain different values when the project is run.

Threads and yield points

So that covers how threads are added but obviously entire threads can’t execute from start to completion before passing control to the next thread so let’s look at when it jumps from one thread to the next.

Consider the following little project -

When the green flag is pressed both green hat blocks of code are added to the active threads list. They could be added in either order but, for the sake of this example, I’ll say that they are added with the left block first.

If the first thread were going to run to completion before execution passed on to the second then the sprite would bounce left to right for over three seconds before then starting to change colour. We know from experience that this is not what happens. Both blocks will appear to run simultaneously so how is this achieved?

I’m going to introduce a new term here called “yield”. The code in a stack of blocks will execute until it hits a yield point at which time execution will pass on to the next thread. This process continues from thread to thread (and then back to the first) until all threads have completed execution. Now, the points at which execution will yield are -

So, using the previous example, the following occurs -

  1. The user presses the green flag which adds the two green flag hat blocks to the active threads list.
  2. The first thread executes until it reaches a yield point (which is at the end of the first iteration of the repeat loop).
  3. Execution moves on to the next thread and continues until a yield point is reached (which is at the end of the first iteration of the forever loop).
  4. There are no more threads in the active thread list so execution moves back to the first thread until reaching the yield point (at the end of the second iteration of the repeat loop).
  5. This process continues moving between the two threads until we reach the end of the repeat loop in the left-hand block at which point there is no more code to run in that thread so it is removed from the active thread list and execution moves to the next thread.
  6. Note that although there is now only one thread running it will still yield at the end of every iteration of the forever loop.

At any point, more threads may be added by events (such as sprite clicked or space pressed) which explains in some part why even a single running thread still yields.

So we now know how the pseudo multithreading works in Scratch! Well, we know the basics. Now let’s see why this is important.

How we can use this knowledge to ensure projects run smoothly

The Scratch language allows projects to run up to a speed of thirty frames a second. That means there can be 30 visual changes to the screen per second. For a project (and, arguably, especially a game) to run well it should be maintaining this maximum speed throughout. If you’re shooting space invaders you don’t want the movement of the player’s ship to suddenly slow down or for the whole game to speed up and slow down from time to time. You want to run at a consistent frame rate.

At 30 frames per second, a new frame occurs every 33.3ms. That doesn’t sound like a lot of time but, in terms of computing, a great deal of processing can be achieved. On my computer, for example, an empty loop in Scratch can run over 13000 times in that period!

But at that speed how do we see nice, smooth animations? With the following example, why does the sprite not zoom around the screen at lightning speed?

The key to this is that Scratch monitors if any graphical updates have taken place as it steps through the threads. Before starting the first thread a flag is set to false. As it moves through the threads that flag will be set to true if, at any point, a graphical update takes place. This can be a move block, turn block, graphics effect block, or any other block that causes a visual change on the screen. When Scratch has yielded from the final active thread (before it moves back to the first one again) it checks this flag and, if the flag indicates something visual has changed, it forces a screen refresh to take place. Or, rather, it forces processing to wait until a screen refresh has taken place.

That explains why the above loop will run 30 times a second whereas the loop in the script below (with no graphical updates) will run over 10000 times a frame (or well over 300000 times a second).

The really important thing to grasp from this is that any thread that causes the screen refresh flag to be set will cause all threads to wait for a screen refresh. If your project contains only the above block the counter will increase by far over 10000 each frame. But consider a project which has the following two blocks -

Pressing the green flag will spawn two threads, one for each green hat block, as we’ve already ascertained. But each traversal of the threads will set the screen refresh flag to true because of the second green flag hat. This, in turn, means that both threads will now be forced to wait for a screen refresh each frame so they will both now run at 30 frames per second. The sprite will bounce smoothly around the screen but the counter loop will now only increase by 1 each frame rather than by 10000+! This is a very important element to grasp when trying to create performant code in Scratch.

You’re certainly going to want to update the screen 30 times a second for a game but you will likely also have loops that need to run at a speed higher than 1 frame per second!

As an example, let’s consider a game that bounces the sprite around the screen and scores 10 points for every time the space bar is pressed. A very hi-tech game indeed!

This works smoothly with the sprite bouncing around at 30 fps (frames per second). But now let’s add some code to calculate the y position of the sprite to the nearest 10 when you press the space bar -

This could have been written in a much simpler way using maths but this example shows a loop that moves in increments of ten until it’s greater than or equal to the sprite’s y position. In theory it looks like it would work properly but in practice the actual height variable and the height variable can be 100 pixels or more apart. So why is this?

The assumption for this code to work is that the repeat until loop executes very quickly. It’s a simple bit of code and we want it to loop instantly until we have the value for height. But the other thread that is running is updating something on the screen which means that both threads are running at 30fps. Rather than the loop instantly finding the answer, the first iteration will compare the y position to -180 then the sprite will move and the second iteration will compare the y position to -170, and so on. We’re not comparing the height at which we pressed the spacebar - the sprite is continually moving at the same time! This is just an example but hopefully it clarifies why the script may not be running as fast as required. Essentially we need to make sure that every script that needs to be performed each frame isn’t being held up in this manner and the way to do this is to use custom blocks.

Why custom blocks are so useful

I’m a strong proponent of custom blocks anyway as they make code a lot clearer to read so consider changing the above example to this -

From a basic coding point of view, it looks to be doing the same thing except the code is easier to read. You now know exactly what is being achieved when the spacebar is pressed! And in actuality, the code will do exactly what it did before unless… we tick this box when creating (or editing) the custom block -

This is very important! It forces the custom block to run atomically. This means any code in that block will ignore the normal yield rules and will not pass execution to the next thread until the code has been completed. Now when the space bar is pressed the relevant block of code is added to the active thread. That relevant code calls the no-refresh custom block which will run to completion whilst ignoring the normal yield rules. If you run the project now it will return the value that you would expect in the height variable. It’s worth trying this example yourself to see the difference.

Here’s another example -

If you create the custom block without selecting the Run without screen refresh checkbox then when you press the space bar the speed that the sprite is moving at will double for the next ten frames. Both threads will move the sprite 10 steps each frame because of the normal yield and screen refresh rules. If, however, you tick the Run without screen refresh checkbox then the move quickly block will ignore the yield and screen refresh rules and will run without yielding execution to the next thread until it has completed. Rather than moving an extra ten steps for each of the next ten frames, it will move an extra 100 steps in that single frame. It will still cause the screen refresh flag to be set to true because it has modified something on the screen so after processing each thread the code will wait for a screen refresh. In this example it would anyway because of the other thread updating the screen but, even if the other thread had not done so, this custom block will still cause that to happen. The important thing to realise is that the custom block is running atomically and running to completion.

I should point out that non-refresh custom blocks should not run forever (e.g. in forever loops), should not perform wait blocks, should not perform broadcast and wait blocks, etc. As no yields take place during their execution, a forever loop will effectively lock the system as will any block that requires execution to yield to another thread (such as wait blocks, broadcast and wait, etc.). What happens in these cases is that the custom block will force a yield to occur after half a second. This is something you want to avoid! It will have a massive impact on the speed of your project, your custom block is yielding which is what you were trying to prevent and the Scratch editor itself will become fairly unresponsive as it is using all of its resources to run the non-refresh custom block.

So now you know how threading works in Scratch and how to ensure loops of code execute expediently regardless.

Are non-refresh custom blocks the same as turbo mode?

The short answer is no. When you enable turbo mode (by holding the shift key down while pressing the green flag to start a project) you are telling Scratch to ignore the flag that’s set to true if a screen update has been made during a run through all the active threads. The screen still refreshes 30 times a second but updating a visual element will not cause your scripts to wait for that refresh. This will make most projects run faster but at the cost of no longer being able to control your projects running at 30 fps. Games will effectively become unplayable as a spaceship that was previously gliding across the screen at a few pixels per frame will no longer be restricted to 30 updates per second and will likely move from the left side to the right side in an instant. It has its uses for complex maths, 3D graphics, etc. but, even in those cases, my preference is to make careful use of custom blocks and code flow to get the same (or better) speed benefits without the cost of not being able to tie your code to the screen refreshes.

Broadcasts and more

There are two types of broadcast blocks -

After either is encountered, a new thread is added to the end of the active thread list for each receiver it activates (because a broadcast message can activate any number of receivers for that specific message). The difference is that broadcast and wait will then yield, allowing the next thread to execute, and will pause the current thread until all receivers have finished executing. A broadcast, however, will continue running the current thread until it reaches a yield point. This is a very important difference for a number of reasons. Firstly, the code in the thread after a broadcast takes place will execute until a yield point is hit. I’ll show an example of this later. Secondly, because the broadcast and wait causes a yield to take place it can affect your execution speed (if graphics are being updated at the same time on a different thread). Again, I’ll show an example of this later.

One more point about adding to the active thread list

Something that wasn’t previously mentioned about threads being added is that the same thread can’t exist twice in the active thread list. Before a new thread is added it is checked against all current threads in the list. Usually, if it already exists in the list then, rather than a new thread being added to the end, the already existing thread in the list is restarted. There are a few caveats to this in that the following events simply continue without a new thread being added to the queue -

when [] key pressed

when [] > ()

So how does this knowledge help?

The click the green flag twice issue

Consider the following project -

We don’t know what order the green flag hat blocks will be in when added to the active threads. If you’ve just finished a game by pressing the spacebar and press the green flag to start another then you’ll get one of two possible results.

  1. The block on the left is executed first followed by the one in the middle. This will lead to the expected outcome of the game playing until the space bar is again pressed.
  2. The blocks are executed in the other order in which case the game over? variable is still true from the previous game so the middle block ends instantly and then the left-hand block runs and initialises things. If the green flag is then pressed a second time, game over? is now false so the game runs as expected. Basically, you need to press the green flag twice each time to get the game to play. Sound familiar?

Broadcast and wait slows down code execution

For this example, I’m going to use a case that I came across recently in one of my YouTube tutorials. I have a broadcast receiver that is used to display text on the screen. You set the string to display in a variable and broadcast to the receiver to display it. Consider the following example where I have four lines of text to display -

The script will do what I want but each broadcast and wait block will cause a yield to occur and because the Text display receiver will be updating the display and causing the screen refresh flag to be set to true, the above code will take four frames to display! If we’re wanting to display it in a single frame for a game, we will have to consider a different method.

Putting the code into a non-refresh custom block might be your first thought but, as previously mentioned, you shouldn’t use broadcast and wait blocks in a non-refresh custom block.

This is just one example of how broadcast and wait can slow execution. They are perfect to use for some situations or purposes but the fact that they cause a yield needs to be understood.

Consecutive broadcasts aren’t being called

After ruling out a non-refresh custom block for the previous example, it crossed my mind to replace the broadcast and wait blocks with broadcast blocks which do not cause yields to occur. This would look like this -

But consider what was said before. The same thread cannot exist more than once in the active thread list. What the above code does is -

  1. Set Text string to Some text
  2. Adds all Text display receivers to the active threads list
  3. Set Text string to Some more text
  4. Adds all Text display receivers to the active threads list but because those threads already exist in the list they are restarted rather than added
  5. Set Text string to A piece of info
  6. Adds all Text display receivers to the active threads list but because those threads already exist in the list they are restarted rather than added
  7. Set Text string to Yet something else
  8. Adds all Text display receivers to the active threads list but because those threads already exist in the list they are restarted rather than added
  9. Yields at end of the block, so it passes execution to the next thread in the active thread list.

There is only one receiver for this particular message and each of the latter three broadcasts have simply replaced the existing thread in the list so the outcome is that we effectively perform only one broadcast to Text display with Text string containing Yet something else.

Code continues to execute after a broadcast

This is another issue that can be quite hard to spot but is explained by knowing how code execution works in Scratch. Consider this example -

We’re assuming we have broadcast receivers that do operations that use the variable index. What happens here is -

  1. index is set to 1
  2. Something that uses the index variable broadcast receivers are added to the active threads list
  3. index is modified to 2
  4. Something else that uses the index variable broadcast receivers are added to the active threads list
  5. This thread reaches the end so yields execution to the next thread but rather than it being called with index = 1, it is being called with index = 2.

This example is contrived, but I have encountered similar issues on many occasions and they tend to be difficult to track down.

Event block is restarting

IMPORTANT NOTE: This example is incorrect as the keyPressed event does not restart (as mentioned earlier). I will be modifying this with the spriteClicked event soon…

I’ve seen several projects where people have added a lot of code to the keypress event blocks (though this could equally affect any event block). Let’s use the following as an example where the space bar is being used to fire a missile -

At first inspection, this may look fine. The player presses the spacebar and a missile fires towards the top of the screen. The issue here is what happens if the space bar is pressed again while the missile is already in flight? The process is as follows -

  1. The space bar is pressed and the event block is added to the active threads
  2. Each frame the thread causes the missile to move towards the top of the screen
  3. The space bar is pressed again
  4. Active threads are scanned to see if this thread already exists. As it does, the thread is reset so that it starts from the beginning again on its next execution

The outcome is that a second press of the space bar, while the event code is already running, will cause the missile to start movement from the player’s position again. Note that this applies equally to broadcast receivers or any other event block. For a key event, it is important to ensure all scripts are executed in a single frame to prevent this from happening. One possible method to achieve this would be to modify the code as follows -

With this modification the key event can complete execution in a single frame so you can press space as quickly as you want but, because of the use of the boolean variable, only one missile can be fired at a time.

As noted earlier in the document, key pressed events are no longer restarted in Scratch 3.0. You can still see the problems with the above if you use when sprite clicked events to see the problems outlined in this example.

A couple of examples

Bad Space Invaders

Don’t forget that the when I start as a clone block can set the graphics-updated flag. Even something as simple as show or go to x, y will cause the flag to be set. In this example (which uses the default Scratch Cat sprite) that means the loop that sets up the space invader clones takes over one second to complete! The way I’ve started each clone moving as soon as it is created really hits home the issue here.

Simple wrapping the code from the green flag hat block in a non-refresh custom block will fix this example and get it running as you would want.

Message order madness

The key to remember with broadcast blocks is that they add threads to the end of the active queue. This means the broadcast receivers don’t get executed immediately. As previously explained, at a yield point, execution passes to the next thread. Any receivers have to wait for their turn!

Note from the Debug watcher that things may not be executed in the order you would have initially expected. Though hopefully, after having read this document, it is the order you would expect them to be executing in now.

In conclusion

Knowing the underlying systems used by Scratch isn’t essential to making Scratch projects but it is extremely useful in identifying why things work the way they do. A Scratcher may have identified that their project only works if you press the green flag a few times but with this information you can see the underlying reasons for this.

Using this information, controlling code flow, and implementing non-refresh custom blocks where needed it is possible to run extremely complicated projects at a full and steady frame rate.

This tutorial in video (YouTube)

Once I’m completely happy with this document and have acted on all feedback, I will be creating an accompanying video which will be available at -

https://www.youtube.com/rokcoder