Loops
Do it again, and again. One counting loop draws a row, a grid or a whole scene — and the counter itself becomes part of the picture.
1Doing It Again
A loop runs the same lines over and over, and one number says how many times. Here the counter drives how big each shape is, so you can watch the loop count without anything moving.
Lesson — read before you start
A loop writes it once and says how many times to run it:
for (var i = 0; i < 5; i = i + 1) {
circle(200, 200, 100);
}
Three things sit in those round brackets, separated by semicolons, and they are always the same three.
var i = 0 makes a counter and starts it at nought.
i < 5 is the test. Before every turn the loop asks "is i still less than 5?", and the moment the answer is no it stops. So the loop runs with i as 0, 1, 2, 3 and 4 — five turns, and it never runs with i as 5.
i = i + 1 is what happens at the end of each turn. Without it the counter would never change, the test would never fail, and the loop would never finish.
Now look at what that loop actually draws: the same circle, in the same place, at the same size, five times over. You see one circle. A loop repeats the lines, not the picture — if nothing inside it changes from one turn to the next, you have drawn the same thing five times and wasted four of them.
What makes each turn different is i. It is an ordinary number and you can use it like one:
circle(200, 200, 100 + i * 30);
The first time round i is 0, so that is 100. Then 130, 160, 190, 220. One line, five different rings.
Everything in this topic uses i for the size, so you can see the loop counting without anything moving about. Rings are drawn with no fill and just an outline, which is why you can see all of them at once instead of the biggest one hiding the rest.
One warning. If the counter never reaches the value that ends the loop, the loop never finishes. Try a step of 0 and see: the sketch stops itself and tells you, rather than freezing. That message is worth meeting once on purpose.
Next topic the counter goes into a coordinate instead of a size, and the pile turns into a row.
2The Counter is a Position
The counter is a number like any other, so put it into a coordinate. One loop then draws a row, a column, a diagonal or a whole street, and two numbers decide the lot: where it starts and how far apart they are.
Lesson — read before you start
The counter fixes that, because i is an ordinary number and a position is an ordinary number:
for (var i = 0; i < 5; i = i + 1) {
circle(60 + i * 70, 200, 50);
}
The first time round i is 0, so that is 60. Then 130, 200, 270, 340. One line, five circles, evenly spread.
Two numbers decide a row and it is worth naming them. 60 is where it starts, because i is 0 on the first turn and nought lots of anything is nothing. 70 is the gap between one shape and the next. Change the start and the whole row slides; change the gap and it spreads out or bunches up.
Everything else in this topic is that idea somewhere else.
Put i into the down instead and you get a column. Put it into both and each shape moves along and down at once, which draws a diagonal — and notice that neither sum mentions a diagonal. It is just two rows of arithmetic that happen to be running at the same time.
Take the gap away instead of adding it and the row runs the other way, even though the counter still only counts up. A gap smaller than the shape makes them overlap, which is how you draw scales or a stack of coins.
And the loop does not care what its lines draw, so it can call one of your own functions. A row of trees is one call to drawTree with i in the across, and a whole street of houses is the same line with a different name in it.
One more move worth having. The loop can live inside a function of yours, and then the row takes its height as an input:
function drawRow(y) {
for (var i = 0; i < 5; i = i + 1) {
circle(60 + i * 70, y, 44);
}
}
Now drawRow(80) and drawRow(320) are two rows, and you wrote the loop once.
Next topic all three parts of the loop header are yours to change: where the counter starts, where it stops, and how big a step it takes.
3Start, Stop, Step
All three numbers in a loop header are yours: where the counter starts, where it stops, and how big a step it takes. Between them they decide exactly which numbers the counter takes, and so exactly what gets drawn.
Lesson — read before you start
for (var i = 0; i < 5; i = i + 1) {
You have changed what the body does with i, and that is most of what loops are for. But those three numbers were only ever a choice, and this topic is about choosing them.
var i = 0 is the start. Nothing says nought. Start at 2 and the first two turns simply never happen, so a row of six becomes a row of four and it begins further along.
i < 5 is the test, and the important word is "before". It is checked before every turn, so the loop runs with i as 0, 1, 2, 3 and 4 and never with i as 5. That is why from 0 up to 5 is five turns, and why from 3 up to 9 is six.
i = i + 1 is the step. Add two and the counter takes every other value, so half as many shapes are drawn and the ones that are drawn sit twice as far apart. Add three and you get a dashed line.
A counter can also count down, and then the test has to look the other way round:
for (var i = 5; i > 0; i = i - 1) {
That runs with i as 5, 4, 3, 2 and 1. Written with a less-than test it would stop before it began, because 5 is not less than 0.
Which brings up the one thing that can go wrong here. The loop only ends when the test fails, so something in the header has to be carrying the counter towards that. Take nothing away — a step of 0 — and i stays where it is for ever. The sketch will not freeze; it stops itself and tells you the loop never finished. One of the challenges here is built on exactly that, and it is worth reading the message once on purpose.
Two numbers are easy to mix up, so keep them apart in your head. The step is in the header and decides which values i takes. The gap is in your drawing and decides how far apart those values put the shapes. A step of 3 with a gap of 25 puts shapes 75 apart.
Next topic the counter goes through a sum on its way to the picture, and the gaps stop having to be even.
4Patterns with Maths
The counter is just a number, so everything Course 2 taught you about arithmetic works on it. Send it through a sum on its way into the picture and the gaps stop having to be even, and sizes can grow along a row as well as places.
Lesson — read before you start
You have been writing one of these sums already without naming it:
circle(40 + i * 60, 200, 40);
There are two numbers in there and they do quite different jobs. The 60 is the gap, and it decides how far apart the shapes land. The 40 is the offset, and it decides where the row begins. Change the offset and the whole row slides along with every one of its shapes intact, which is not what happens if you change where the counter starts.
Now the new part. Nothing says the sum has to be a gap and an offset.
Multiply the counter by itself and it climbs faster and faster: 0, then 1, 4, 9, 16, 25. A row built on i * i has gaps that widen as it goes, so the shapes bunch up at one end and spread out at the other. It is the first time a loop has drawn you something that is not evenly spaced.
Take away instead of adding and things run the other way. 360 - i * 40 walks to the left, and 86 - i * 12 makes each shape smaller than the one before it.
Divide, and you can share a distance out rather than choosing a gap. Watch the counting when you do, because five shapes have only four gaps between them, so it is 320 / 4 that puts the last one exactly 320 along.
And the sum need not end up in a position at all. A size is a number too:
circle(60 + i * 70, 210, 24 + i * 15);
That row of circles gets wider as it goes across, out of a sum that looks exactly like the one placing them. One counter can drive an across, a down and a size all at once, in three separate sums, and then a single wrong number moves and resizes everything together.
One thing to watch. A size worked out by a sum can end up at nought or below, and a shape of no size at all draws nothing. If part of your picture has gone missing, work out what the sum comes to on the last turn.
Next topic the drawing itself becomes yours. You write the function, and the loop calls it once for every turn.
5Loops and Functions
One function and one loop make a forest out of three lines. The function says what one of a thing looks like, the loop says how many and where, and neither of them needs to know anything about the other.
Lesson — read before you start
That split is the whole idea. The body says what one tree looks like. The loop says how many trees and where. Neither one needs to know anything about the other:
function drawTree(x, y) {
shapeColour(BROWN);
rectangle(x - 6, y - 60, 12, 60);
shapeColour(GREEN);
circle(x, y - 60, 54);
}
for (var i = 0; i < 5; i = i + 1) {
drawTree(40 + i * 80, 320);
}
Five trees, and the drawing is written once. Change the head's size and all five change together, which is the real reason to put it in a function at all.
Now the part that catches everybody. The counter belongs to the loop. It does not exist inside the body, and if you reach for i in there you get "i is not defined" — the body's only knowledge of the outside world is the numbers it was handed. That is what the inputs are for, and one of the challenges here is built on exactly that mistake so you meet the message once on purpose.
Which means every number in the body has to be measured from an input. Write 200 where you meant x and every turn draws in the same place, one shape on top of the last, so a five-turn loop looks like it ran once.
A turn can do more than one thing. Two calls in the loop draw two things per turn; one function called from two loops draws two rows; and a function of yours can call another function of yours, so one line in the loop can put a whole pair of trees on the canvas.
And not everything belongs inside. A fence has one long rail and a row of posts: the rail is drawn once, before the loop, and only the posts repeat. Ask what there is one of and what there are many of.
Next topic a loop goes inside another loop, and a row becomes a grid.
6Loops inside Loops
A loop can hold another loop, and then the inner one runs all the way through for every single turn of the outer one. Two counters instead of one, and a row becomes a grid, a wall, a chequerboard or a times table.
Lesson — read before you start
for (var row = 0; row < 3; row = row + 1) {
for (var col = 0; col < 5; col = col + 1) {
circle(50 + col * 76, 90 + row * 110, 44);
}
}
Three turns of five is fifteen circles, out of four lines. The counters go 0-0, 0-1, 0-2, 0-3, 0-4, then 1-0, 1-1 and on. Notice what col does when row moves on: it starts over. It always starts over, which is exactly why this draws a grid and not a diagonal.
The names matter more than they did. With one counter, i was fine. With two, row and col tell you which is which at a glance, and mixing them up is the mistake to expect: put the row counter in the across sum and you get a picture that is nothing like the one you wanted.
Which counter goes where is the whole design. The one that changes every turn, col, spaces things out along a row. The one that only moves when a whole row has finished, row, moves you down to the next one. Put a counter in a size instead and it is the same choice again: a size from row makes every row a different size, and a size from col makes every column one.
Both counters can go into the same sum. Add a little of row into the across sum and each row starts further along than the one above, so the grid leans.
The inner loop's stop can read the outer counter. Write col < row + 1 and the first row draws one shape, the second two, and you have a triangle instead of a rectangle.
And a nest needn't look like a nest. Put the inner loop in a function of its own and the outer loop just calls it once per row — same fifteen shapes, and often easier to read.
Next topic these grids start moving. The frame number joins the two counters, and everything you have built here can drift, grow or spin.
7Loops that Move
Everything you have built so far, moving. A loop draws the shapes, a number that changes every frame carries them along, and where those two meet is where a row becomes a wave, a grid becomes a wall sliding past.
Lesson — read before you start
The loop counter says which shape you are drawing. It goes 0, 1, 2, 3 and back to 0 on the next frame, over and over, and it never remembers anything.
A number kept outside everyFrame is the other kind. It is made once, above everyFrame, and every frame adds to it, so it carries on from where it got to:
var x = 0;
function everyFrame(){
backgroundColour(BLACK);
x = x + 3;
for (var i = 0; i < 4; i = i + 1) {
circle(30 + x + i * 70, 200, 44);
}
}
The counter spaces the circles out and x carries all four along together. Take x out and you have Topic 2's row. Take the loop out and you have Course 3's single mover. Together they are a row that moves.
Where you put x is the whole of it. In the across sum the row drifts sideways; in the down sum it falls; in a size every shape swells at once.
And you can multiply. Write x * i and every shape gets its own speed, because the counter is bigger further along the row — the first one stands still and the last one races away. That one line is the difference between a row that slides and a row that fans out, and it is worth trying both.
Two numbers can move at two rates. Two loops, two variables, two speeds, and the rows drift apart.
The loop's stop can read the frame number, and then the row gains a shape every frame and draws itself across the canvas.
One thing to know about the number kept outside. It has to live above everyFrame, not inside it. Put var x = 0 inside and it is made afresh every frame, so it goes back to 0 every time and nothing ever moves. That is the commonest way an animation comes out frozen.
Next topic is the last one, and the loops come out. You will write the headers, the bodies and the sums yourself, from a comment and a picture.
8Loop It Yourself
The loops come out and you write them yourself. A comment says what belongs on the missing line, or what the header has to count, and by the end you are writing a whole loop — start, stop, step and the drawing inside it — from nothing but a picture.
Lesson — read before you start
Up to here every loop was written for you and you filled in the numbers. From now on the lines themselves are missing, and each missing line has a comment sitting directly above it saying what belongs there. That comment is the instruction. Read it as a sentence:
// circle(across, down, wide) — 40 + i * 78 across, 250 down, 50 wide
It names the drawing you need, then the order its numbers go in, then what to put in each one. Everything it asks for is something you have written before.
An empty line is not an error. The loop still runs, it just draws nothing, so a canvas with the grass and no flowers on it is telling you exactly which line is missing rather than shouting at you.
Then the headers go too. A header has three parts and they are always in the same order:
for (var i = 0; i < 5; i = i + 1) {
Where the counter starts. When to carry on. What to add each turn. Read the target, count the shapes, and the three parts follow: five shapes means stopping after five, a row that starts partway along means starting partway along, and shapes two apart in the counting means a step of two.
Counting down flips the middle part. If the counter starts at 5 and takes 1 away each turn, then carrying on means being greater than the finish, not less than it: for (var i = 5; i > 0; i = i - 1).
Watch the step. A step that counts away from the stop never reaches it, and the loop runs forever — the page will tell you so, in words, and stop the sketch. That message is feedback, not a crash: read the line number, turn the step around, and run it again.
One thing to remember about the pen. shapeColour belongs inside the loop with the drawing it is for, or every shape keeps whatever colour was last picked up — which is how a row of suns comes out grass green.
The last five challenges move. The counter says which shape, a number kept above everyFrame says when, and both of those lines are yours as well.
That is the whole course: a loop repeats, its counter is a number you can use, its header says how the counting goes, sums make patterns out of it, functions hold the drawings, loops hold loops, and time carries the lot along.