Discuss Scratch

Wowfunhappy
Scratcher
27 posts

When does Scratch wait for the next screen refresh before continuing? Or, why do these simple loops result in different behavior?

I teach Scratch programming to young children. My beginner students often try to write the following code, and are surprised when it doesn't work:

Program 1:
when green flag clicked
forever
hide
show
end

This results in a sprite which never disappears. I usually explain to my students (indirectly via guided questions) that the computer is running too quickly, and they need to add wait blocks.

The thing is, this explanation is actually a load of bull poop!

You see, some students will write the code this way:

Program 2:
when green flag clicked
forever
show
hide
end

Whereas Program 1 will result in a sprite which is always visible, Program 2 will result in a sprite which is never visible!

Okay, I guess Scratch pauses execution after each iteration of a loop, probably to support animations. That's not particularly intuitive, but it does explains why the following code results in no movement…

Program 3:
when green flag clicked
forever
move (10) steps
move (-10) steps
end

…whereas this code causes the sprite to rapidly move back and fourth:

Program 4:
when green flag clicked
forever
if <(Should Move Forward) = [1]> then
move (10) steps
set [Should Move Forward] to [0]
else
move (-10) steps
set [Should Move Forward] to [1]
end
end

In that case, however, I'd expect the following code to make a sprite rapidly disappear and reappear:

Program 5:
when green flag clicked
forever
if <(Should Hide) = [1]> then
hide
set [Should Hide] to [0]
else
show
set [Should Hide] to [1]
end
end

Instead, Program 5 results in a sprite which is always visible! Furthermore, the variable “Should Hide” never visibly changes to 1 in the preview box.

But wait! Take a look at these three programs:

Program 6:
when green flag clicked
forever
if <(Should Hide) = [1]> then
set [Should Hide] to [0]
else
set [Should Hide] to [1]
end
end

Program 7:
when green flag clicked
forever
if <(Should Hide) = [1]> then
hide
set [Should Hide] to [0]
else
hide
set [Should Hide] to [1]
end
end

Program 8:
when green flag clicked
forever
if <(Should Hide) = [1]> then
show
set [Should Hide] to [0]
else
show
set [Should Hide] to [1]
end
end

Obviously, all of these programs will result in a static sprite. However, I can also see the “Should Hide” variable rapidly changing between 1 and 2 when I click the flag. The timing isn't consistent, but presumably that's because the execution of the loop isn't aligned to screen refreshes.

I guess Scratch skips over “Show” and “Hide” commands when a sprite is already visible or hidden respectively? That explains why programs 6 – 8 are the same, but not what “Hide” and “Show” are doing to the loop in the first place!

Can anyone explain what's going on here?

Separately, is there any chance this is something that should be fixed in a future version of Scratch? I feel like Hide and Show shouldn't be able to execute on the same sprite in the same frame when Turbo Mode is disabled. IE, without Turbo Mode, Programs 1 and 2 should both result in a sprite which is visible every other frame—and for that matter, Program 3 should result in a sprite which rapidly moves back and fourth. Presumably there's a reason Scratch doesn't work that way? I'm sure people smarter than me have thought about this…

Thank you!

Last edited by Wowfunhappy (May 23, 2022 17:11:52)

colinmacc
Scratcher
1000+ posts

When does Scratch wait for the next screen refresh before continuing? Or, why do these simple loops result in different behavior?

Hmm I didn't believe this at first so I had to try it, and you're right..

https://scratch.mit.edu/projects/695216684/

There are two ways that you can fix the behaviour, as documented in that project, but I would have expected it to work with hide and show.

The way scratch works is that it tries to maintain a frame rate of 30 frames per second, and so only updates the screen at certain points of execution, one of which being the end point of a loop such as a forever loop. If Scratch doesn't detect any screen changes for that frame, it just carries on with the next loop.

So for the workarounds, setting ghost effect, similar to your movement example, obviously triggers a screen change. Also forcing a wait 0 does that too, but I don't know why show/hide themselves don't, and yet you can see from the counter that it's still only running at 3060 fps (as an experiment take out the show and the hide, and see how fast the counter goes up!)

So answer is, I don't know, sorry! It's a bit weird though, and not what I would have expected.

Last edited by colinmacc (May 24, 2022 09:42:27)


Sample Projects

kriblo_test
Scratcher
4 posts

When does Scratch wait for the next screen refresh before continuing? Or, why do these simple loops result in different behavior?

colinmacc wrote:

Hmm I didn't believe this at first so I had to try it, and you're right..

https://scratch.mit.edu/projects/695216684/

…you can see from the counter that it's still only running at 30 fps (as an experiment take out the show and the hide, and see how fast the counter goes up!)
Actually, it appears to me, that the forever loop in your remix, @colinmacc, is running 60 iterations / second, which would explain the whole thing.
colinmacc
Scratcher
1000+ posts

When does Scratch wait for the next screen refresh before continuing? Or, why do these simple loops result in different behavior?

kriblo_test wrote:

colinmacc wrote:

Hmm I didn't believe this at first so I had to try it, and you're right..

https://scratch.mit.edu/projects/695216684/

…you can see from the counter that it's still only running at 30 fps (as an experiment take out the show and the hide, and see how fast the counter goes up!)
Actually, it appears to me, that the forever loop in your remix, @colinmacc, is running 60 iterations / second, which would explain the whole thing.

Yeah that would make sense, as every second loop would run super-fast without refreshing the screen.

Sample Projects

kriblo_test
Scratcher
4 posts

When does Scratch wait for the next screen refresh before continuing? Or, why do these simple loops result in different behavior?

colinmacc wrote:

kriblo_test wrote:

…it appears to me, that the forever loop in your remix, @colinmacc, is running 60 iterations / second…

Yeah that would make sense, as every second loop would run super-fast without refreshing the screen.

Yes, a forever loop with only “change counter by 1” & “hide” will run without screen refresh, whereas one with “change counter by 1” & “show” will run at 30 fps.
colinmacc
Scratcher
1000+ posts

When does Scratch wait for the next screen refresh before continuing? Or, why do these simple loops result in different behavior?

kriblo_test wrote:

colinmacc wrote:

kriblo_test wrote:

…it appears to me, that the forever loop in your remix, @colinmacc, is running 60 iterations / second…

Yeah that would make sense, as every second loop would run super-fast without refreshing the screen.

Yes, a forever loop with only “change counter by 1” & “hide” will run without screen refresh, whereas one with “change counter by 1” & “show” will run at 30 fps.

So this is a bit of a bug then.. i presume scratch gets to the end of the loop, and determines that no visible sprites have changed, so no need to refresh the screen, without taking into account that the sprite was visible at the start of the loop.

Turbowarp does the same.

Sample Projects

Maximouse
Scratcher
1000+ posts

When does Scratch wait for the next screen refresh before continuing? Or, why do these simple loops result in different behavior?

colinmacc wrote:

So this is a bit of a bug then.. i presume scratch gets to the end of the loop, and determines that no visible sprites have changed, so no need to refresh the screen, without taking into account that the sprite was visible at the start of the loop.

Turbowarp does the same.
I checked the code for the show and hide blocks and it does exactly that:
/**
 * Set visibility; i.e., whether it's shown or hidden.
 * @param {!boolean} visible True if should be shown.
*/
setVisible (visible) {
    if (this.isStage) {
        return;
    }
    this.visible = !!visible;
    if (this.renderer) {
        this.renderer.updateDrawableVisible(this.drawableID, this.visible);
        if (this.visible) {
            this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this);
            this.runtime.requestRedraw();
        }
    }
    this.runtime.requestTargetsUpdate(this);
}
requestRedraw() is only called if the sprite is visible, which means that “show” results in a redraw but “hide” does not. This explains why program 5 makes the sprite always visible.

Your explanation of programs 6 and 7 is correct: there are no blocks that would trigger a redraw, so the loop runs much faster than the variable display is being updated. Program 8 uses show instead of hide, which does redraw the screen, so the value of the variable changes at a consistent rate.

To answer Wowfunhappy's last question, I think it would make sense to revert this to the behavior of Scratch 2: showing a sprite would always request a redraw and hiding would only request a redraw if the sprite was not already hidden. This behavior is still inconsistent but it's less strange than the current one – program 5 would behave as expected but 7 and 8 would still behave differently from each other. There might be projects that rely on the current behavior though.

Last edited by Maximouse (May 24, 2022 07:26:21)



This is Maximouse's signature. Learn more about signatures.
Scratch-Minion
Scratcher
1000+ posts

When does Scratch wait for the next screen refresh before continuing? Or, why do these simple loops result in different behavior?

You are teaching young children who are beginners at Scratch, so I will try to write what you can say to them.
I agree with all the replies above but will try to be less technical … I hope! I am a bit verbose though!

Programs 1 to 4 work the way that Scratch intends them to work.

Let's look at program 1 first.

Program 1:
when green flag clicked
forever
hide
show
end

Scratch does not update the screen after every line/block is run!
The times when the screen is updated will be described below.

Only 1 script runs at a time in Scratch! (it is not a multiprocessor)
Scratch does not execute more than 1 script at a time but they take turns.
If they didn't take turns, Scratch would get stuck in the first forever loop it ran and never get out.

Scratch lets scripts take turns with “yield” points, places in scripts where they pause to allow other scripts a turn.
After all running scripts have reached a pause, Scratch updates the screen at a maximum 30 times per second.

The main “yield” points where scripts pause are:
- end of any loop (includes: forever and all repeat blocks)
- end of any script
- any wait block (includes: wait … seconds, wait until …, say … for … seconds, think … for … seconds, ask … and wait, broadcast … and wait, play sound until done, and a few more)

* Note that the end of any “If” block is not a “yield” point.

So in the script above, the hide and show both happen in the loop before the loop yields/pauses at its end, then all other scripts run until they yield/pause, and now the screen is updated! ie. the screen is not updated after every Scratch line, but only at the yield points!

The sprite will always show as the “show” block comes after the “hide” block, so is the last block run before the end of the loop when the screen is updated.


Program 2:
when green flag clicked
forever
show
hide
end

The explanation for program 2 is the same as for program 1.
The sprite will always hide as the “hide” block comes after the “show” block, so is the last block run before the end of the loop when the screen is updated.


Program 3:
when green flag clicked
forever
move (10) steps
move (-10) steps
end

The explanation for program 3 is the same as for programs 1 and 2.
The sprite will always stay still as it has moved 10 and -10 steps = 0 steps before the end of the loop when the screen is updated.


Program 4:
when green flag clicked
forever
if <(Should Move Forward) = [1]> then
move (10) steps
set [Should Move Forward] to [0]
else
move (-10) steps
set [Should Move Forward] to [1]
end
end

In program 4, the sprite alternately moves 10 steps or -10 steps before reaching the end of the loop when the script yields/pauses and the screen is updated. This is why the sprite races back and forth.
Scratch-Minion
Scratcher
1000+ posts

When does Scratch wait for the next screen refresh before continuing? Or, why do these simple loops result in different behavior?

Part 2. Not for the kids!

Program 5:
when green flag clicked
forever
if <(Should Hide) = [1]> then
hide
set [Should Hide] to [0]
else
show
set [Should Hide] to [1]
end
end

This script worked perfectly in Scratch 2!
The sprite flickered as it should.

It does not work as it should in Scratch 3, online or offline versions.
Instead, Program 5 results in a sprite which is always visible!

It is a Bug! Someone should raise it in the “Bugs and Glitches” forum.


@colinmacc is quite right, the test project ran 60 iterations / second!
@kriblo is right!
@Maximouse is right!

The solution is not to explain what is happening to the young beginner kids!

It is a Bug and totally illogical to programmers like me.
I hope it gets raised and fixed soon.
Wowfunhappy
Scratcher
27 posts

When does Scratch wait for the next screen refresh before continuing? Or, why do these simple loops result in different behavior?

Scratch-Minion wrote:

Scratch lets scripts take turns with “yield” points, places in scripts where they pause to allow other scripts a turn.
After all running scripts have reached a pause, Scratch updates the screen at a maximum 30 times per second.

The main “yield” points where scripts pause are:
- end of any loop (includes: forever and all repeat blocks)
- end of any script
- any wait block (includes: wait … seconds, wait until …, say … for … seconds, think … for … seconds, ask … and wait, broadcast … and wait, play sound until done, and a few more)

Got it, thanks! This was initially my assumption, but then I made Program 5 to test my assumption, and ran into the bug and got confused. I went ahead and made a post in the Bugs and Glitches forum: https://scratch.mit.edu/discuss/topic/607335/

I have to say, while the explanation of yield points clarifies a lot of weirdness I've been seeing over the past year, I fundamentally hate it. For educational purposes, it would be a lot clearer if every block was a yield point, at least for the purposes of refreshing the screen. Loops should not have side effects; five move blocks should behave the same way as five iterations of a single move block. That's a fairly fundamental programming concept.

But it does make sense, so thank you for that!

Last edited by Wowfunhappy (May 24, 2022 13:57:27)

Scratch-Minion
Scratcher
1000+ posts

When does Scratch wait for the next screen refresh before continuing? Or, why do these simple loops result in different behavior?

One more note:

I agree that “wait 0” as suggested by @colinmacc is a great idea to show how it all works.

when green flag clicked
forever
hide
wait (0) secs
show
end

Notes:
“wait 0” does not mean zero wait but means pause the minimum time for a screen refresh.
You may prefer wait 0.01

Note that “wait 0.1” is not good as this waits a whole 1/10 second when Scratch refreshes the screen every 1/30th (0.0333) second.



when green flag clicked
forever
show
wait (0) secs
hide
end



Unfortunately with the bug, the code above does not work as the “hide” before the screen refresh at the end of the loop is not actioned.
It would need another “wait 0” as below.

when green flag clicked
forever
show
wait (0) secs
hide
wait (0) secs
end


Just a note that I think yield points at the end of loops, wait blocks, and scripts makes complete sense.

Powered by DjangoBB