Category Logo Coding Tutorials

Tutorial: Fancy a game of darts?

Consider the below game of ‘pub darts’:

You can open it in our Javascript-based IDE by clicking here

If you hover over the various primitives in the editor, a popup will tell you what they do.

The game consists of three main parts: the dartboard, the darts and the game itself.

The dartboard is constructed primarily using the ringarc primitive and a number of repeat loops. It uses the oddp boolean to decide which color each segment of each ring should be, allowing us to match the colors of a standard dartboard.

TO board
  ;create the dart board
  rt 9 setfc 1 polyspot 5 20
  lo 0.1 setfc 4 polyspot 10 20
  ;outer bullseye
  repeat 20 [if oddp repcount [setfc 13] [setfc 0] ringarc 25 10 20 1 rt 18]
  repeat 20 [if oddp repcount [setfc 4] [setfc 1] ringarc 5 35 20 1 rt 18]
  repeat 20 [if oddp repcount [setfc 13] [setfc 0] ringarc 20 40 20 1 rt 18]
  repeat 20 [if oddp repcount [setfc 4] [setfc 1] ringarc 5 60 20 1 rt 18]
  lo 0.1 setfc 0 cylinder 80 10 20 lt 9 penup setfc 15 rt 180

The numbers around the outside of the dartboard are created using the inscribe and orbitleft primitives. We offset the numbers as required to center them on their relevant wedges. We also use the orbit / pullin / pullout functionality to create the wire frame overlaid on the dartboard.

 dropanchor pullout 65 ra 2
  foreach "i [20 1 18 4 13 6 10 15 2 17 3 19 7 16 8 11 14 9 12 5] [
    lt 90 if :i > 9 [bk 10] [bk 5] inscribe :i
    if :i > 9 [fd 10] [fd 5] rt 90 orbitleft 18
  home setpw 1.2 setpc 5 setfc 5
  dropanchor tether pullout 5 orbitleft 9
  repeat 6 [
    repeat 20 [orbitright 18 ico 0.6]
    pu switch {repcount}
    case 1 [pullout 5] case 2 [pullout 25]
    case 3 [pullout 5] case 4 [pullout 20]
    case 5 [pullout 5]
  ;metal rings
  home pullout 10 orbitleft 9
  repeat 20 [pd pullout 55 pu pullin 55 orbitright 18]
  ;metal lines

The darts are assembled using cylinders, cones, cutcones and poilyspots (for the fletchings).

TO dart :color
  ;create the dart models
  lower 50 setfillcolor 10 cone 1 4 20
  raise 20 cylinder 1 20 20
  ra 10 setfc 5 cutcone 3.5 2 10 20
  ra 20 setfc 10 cylinder 3 20 20
  ra 5 setfc 5 cylinder 3.5 5 20
  setfc item :color [11 6]
  ra 20 cutcone 2.5 3 20 20
  ra 5 setfc 10 cutcone 2 2.5 5 20
  setfc 5 ra 5 cutcone 1.5 2 5 20
  ra 10 setfc 0 cutcone 1 1.5 10 20
  ra 20 cutcone 1 1 20 20 up 180
  cone 1 15 20 rr 90
  repeat 3 [
    up 60 setfc 10 twosided
    polyspot 13 6 setfc :color
    ring 2 13 6

The game itself consists of a setup section and the main game loop. Inside the game loop we have the aiming section and the scoring section. Every three darts we switch between players. If one of the players score exceeds 301 then they are declared the winner and the game ends.

TO game
  ;world's smallest dart game
  print |Welcome to darts! Two players take turns until one scores more than 301.|
  print |Try to aim the dart with the mouse and press the mouse button to throw...|

The setup section creates the dart ‘room’, draws and ‘freezes’ the board (breaks it off into its own ‘turtle track’) and creates the dart models using the dart procedure. Default containers (variables) are created using surnames (container classes), one for each player. Surnames enable us to use the same code for each player without needing to use lists or tables.

  pu sl 300 bk 300 ra 600
  setfc pick [3 5 8 9] setfs 10
  setbs fillshade setbg fillcolor
  voxel -607 setfs 0 home
  ;create dart room
  ;create board
  cam:pullout 180
  freeze "board
  newmodel "dart1 [dart 1]
  newmodel "dart2 [dart 2]
  ;create dart models
  setmodel "dart1
  setmodelscale 0.7
  setanchor [0 0 0]
  foreach "i [red blue] [setsurname :i
    rt 180 make "down forwarddir rt 180
    make "updown random 2 make "leftright random 2
    make "dart 0 make "frame 0 make "total 0
    make "oldx mousex make "oldy mousey
  setsurname "red
  ;define player containers
  print word surname |'s turn...|
  setposition [0 0 250]

The main game loop moves the dart according to the mouse position. The dart moves up and down and side to side to simulate shaky hands, in an exaggerated fashion to make the game more challenging. The player pushes the left mouse button to launch the dart. The camera follows the dart in a method dictated by a random number.

  forever [
    ;main game loop
    if or :oldx != mousex :oldy != mousey [
      make "oldx mousex make "oldy mousey
      setposition {-100 + 200 * mousex / 100 50 - 100 * mousey / 100 250}
      cam:sety myrtle:ypos
      ;move the dart with the mouse
    move 1 random 360

    if :updown = 0 [up 1 if and pitch < 350 pitch > 20 [make "updown 1]]
    if :updown = 1 [dn 1 if and pitch < 350 pitch > 20 [make "updown 0]]
    if :leftright = 0 [rl 0.5 if and roll < 350 roll > 10 [make "leftright 1]]
    if :leftright = 1 [rr 0.5 if and roll < 350 roll > 10 [make "leftright 0]]
    ;shaky hands, maybe try drinking herbal tea?

    wait 1

    if buttonp 0 [
      ;if button clicked:

      make "camdir random 4
      ;pick random camera action

      dountil (item 3 extrapolate position vectorsub updir [0 0 0] 35) < 0 [
        ;until the tip of the dart hits the board (basically):

        setpremodel {"lt loopcount}
        ;spin the dart model
        lo 1 dn 0.1 cam:pullin 1
        ;toward the board and down a little

        if :camdir != 3 [cam:sety myrtle:ypos]
        if :camdir = 1 [cam:orbitleft 0.3]
        if :camdir = 2 [cam:orbitright 0.3]
        if :camdir = 3 [cam:orbitup 0.3]
        ;camera actions

Once the dart reaches the wall / board, we calculate if the dart hit the board or the wall by checking its distance from the center of the dart board [0 0 0]. If it actually hit the board then we 'stamp' the model in place.

      ;stop rendering graphics while we deal with things

      ht lo 34
      make "vec vectors
      setvectors originvectors
      ;need to update the state for towards to work
      ;with rendering disabled

      make "dir towards [0 0]
      ;where did we land relative to the center?

      if and :dir >= 171 :dir < 189 [make "score 20]
      if and :dir >= 189 :dir < 207 [make "score 1]
      if and :dir >= 207 :dir < 225 [make "score 18]
      if and :dir >= 225 :dir < 243 [make "score 4]
      if and :dir >= 243 :dir < 261 [make "score 13]
      if and :dir >= 261 :dir < 279 [make "score 6]
      if and :dir >= 279 :dir < 297 [make "score 10]
      if and :dir >= 297 :dir < 315 [make "score 15]
      if and :dir >= 315 :dir < 333 [make "score 2]
      if and :dir >= 333 :dir < 351 [make "score 17]
      if or :dir >= 351 :dir < 9 [make "score 3]
      if and :dir >= 9 :dir < 27 [make "score 19]
      if and :dir >= 27 :dir < 45 [make "score 7]
      if and :dir >= 45 :dir < 63 [make "score 16]
      if and :dir >= 63 :dir < 81 [make "score 8]
      if and :dir >= 81 :dir < 99 [make "score 11]
      if and :dir >= 99 :dir < 117 [make "score 14]
      if and :dir >= 117 :dir < 135 [make "score 9]
      if and :dir >= 135 :dir < 153 [make "score 12]
      if and :dir >= 153 :dir < 171 [make "score 5]
      ;calculate dart position and assign score

      make "dist distance extrapolate position vectorsub updir [0 0 0] zpos [0 0 0]
      ;how far away from the center is the tip of the dart?
      if :dist <= 5 [make "score 50]
      if and :dist > 5 :dist < 10 [make "score 25]
      ;half bullseye
      if and :dist >= 35 :dist <= 40 [make "score :score * 3]
      ;triple ring
      if and :dist >= 60 :dist <= 65 [make "score :score * 2]
      ;double ring
      if :dist > 65 [make "score 0]

We use the towards primitive (an original Apple Logo II primitive!) to determine the number of degrees the landed dart (which is in reality pointed upwards toward the ceiling, the dart 'descending' towards the board along the Z axis) would have to turn to face [0 0]. This, combined with the distance from the center allows us to calculate the dart's score.

      (print |Dart| word :dart + 1 |:| :score) make "frame :frame + :score
      setvectors :vec
      if :dist < 79 [
        ;'stick' a dart to the board using stamp
        playsound "click2
        ra 34
        run premodel
        if surname = "red [stamp "dart1] [stamp "dart2]
        render wait 120

      else [
        playsound "knock
        pr "Missed! ra 34 st render
        repeat (ypos + 300) / 4 [drift 4 :down cam:lo 4 wait 1]
      ;drop the dart to the floor

      ;reset the camera

      setheading 0 setpitch 0 setroll 0
      setvectors originvectors
      ;reset state

      make "updown random 2
      make "leftright random 2
      ;pick random starting wobble directions
      ;for the next dart

      inc "dart
      ;add one to :dart

      if :dart = 3 [
        ;if we've thrown three darts:

        (print |Frame Score: | :frame)
        make "total :total + :frame
        (print word surname |'s Total Score: | :total)
        make "dart 0 make "frame 0
        cam:run pick [[repeat 30 [orbitleft 1 wait 1]] [repeat 30 [orbitright 1 wait 1]]]
        wait 120 cs cam:popturtle

        if :total > 301 [(print surname "wins!) finish]
        ;the end

        if surname = "red [setsurname "blue setmodel "dart2]
        else [setsurname "red setmodel "dart1]
        print word surname |'s turn...|
        ;switch players

      setpremodel [] cam:pullout 180 setposition [0 0 250] showturtle
      ;position camera, reset 'spin', show the next dart

    ;end of throw

  ;end of main game loop


Logo code is like poetry! It's easy to read and describes what the computer is doing in fairly broad terms. This is why it has always been great as a first text-based coding language.


A Starry turtleSpaces Logo Introduction Part Three: Starwarp Improvements

In the previous episode, we created a progressively-generated starfield we moved through with the camera turtle, creating a cool flying-through-space effect.

But it has a few issues we should address. Firstly, it is possible for a star to end up flying through the windscreen of our spaceship! Which is cool, but looks a bit strange. Second, as the program runs it piles up all of these stars behind us, which can slow everything down. We need to get rid of those. Finally, it would be pretty neat if we could have the stars ‘pop’ into view, so we’ll explore how we can do that.

Improvement One: Spaced Out Stars

So, firstly we want to ensure that stars aren’t placed too near to the center, where they could possibly fly through our windshield. Ideally we want to detect if we’re going to place a star within the narrow barrel our ship is flying through, and if so don’t place it there, place it somewhere else.

There are a few new tools we can use to accomplish this:

distance – takes two lists of coordinates, eg [x1 y1 z1] [x2 y2 z2], and returns the distance between them. This will be useful to us, because we are going to provide the prospective new spot position as one list, and {0 0 zpos} as the second list, giving us the distance between the new spot and the XY center of the space at Myrtle’s current Z position.

dountil – repeats a list of commands until it gets the desired results. In this case, we’re going to want to have dountil pick a random position, and then use the distance function to check if that position is outside of our no-go range. dountil executes the list of instructions it is provided before checking the condition it needs to stop executing, while its cousin until checks first.

So, to ensure we don’t place a star within that ‘barrel’, instead of the existing setposition command, we do the following:

dountil 100 < distance position {0 0 zpos} [
  setposition {-1500 + random 3000 -500 + random 1000 zpos}

So, until 100 is less than the distance between the turtle’s position and the XY center of the turtle’s current Z position, keep choosing a new position — and choose a new position before doing the first check.

Note: because of the way Logo’s parser works, if you do a comparison operation (<, >, = etc) where one side is a single parameter (eg a number) you’re best to put that FIRST and the complex parameter second.

Why? Because Logo collects things up right to left, and while Logo will evaluate the stuff to the right of the operator correctly, passing that to the operator, the parser will give the operator the first ‘complete’ thing it sees to the left of it, which if you switched things around would be {0 0 zpos} and is not what we want!

So to solve this you would need to put round brackets () around distance position {0 0 zpos} to ensure that Logo evaluated and gave < what we really wanted to give it. It’s easier in this case just to put the single parameter on the left.

This solves our problem! Stars will keep out of our way. However, this method also moves the turtle every time we try a new position, and all of these false jumps will stay in the turtle’s ‘turtle track’ or list of things the turtle has done.

There are a few methods we could use to stop this from happening, but this one is probably the simplest:

dountil 100 > distance :position {0 0 zpos) [
  make "position {-1500 + random 3000 -500 + random 1000 zpos}
setposition :position

Rather than set the position every time we try, we make a container (variable) called position containing the prospective coordinates, and test that instead. Then, once we have a good set of coordinates, we set that :position using the setposition command.

A colon before a word indicates to the parser that it is meant to pass the contents of a container with that name to the next command or function in the chain.

The colon is shorthand for thing, which returns the value of the container named passed to it. So, for example, you could have used setposition thing “position instead. Note that the name of the variable is preceded by a quote, not a colon. If you used a colon, thing would return the value of the container with the name CONTAINED inside of the container you referenced with the colon!

The mind boggles, doesn’t it?

This is also why you generally make “container rather than make :container — if you did the second, you would make a container with the name contained inside of the container you referenced, which does have practical applications but can be a bit confusing at first.

For now, just remember you make with a quote, retrieve with a colon.

Okay, moving on…


Improvement Two: Galactic Janitorial

This procedure creates a lot of stars. Like, a lot. While you can have a lot of things in turtleSpaces (so many things!) they can start to gum up the works if they get to extreme numbers. A computer can only remember so much you know! And so, we should clean out the stars behind us, because they don’t matter to us anymore anyway.

But the stars are part of Myrtle’s ‘turtle track’, and so we can’t just clean out some of them, can we? There are the clean and clearscreen commands but they get rid of everything!

Never fear, tag is here!

tag allows you to wrap one or more turtle track entries so that you can reference them later, to replace, copy or delete them. We’re going to put tags around stars so we can delete them. We do this with begintag and endtag.

We need to give each tag a name, and so we need to give begintag a name to use. endtag doesn’t take a name, since we can only close the last opened tag. If you create a tag within a tag, that tag has to be closed first, before you can close the tag above it.

What we’re going to do is every 2000 stars, we’re going to close off the existing tag (the stars still  in front of the camera turtle), then create a new tag for the next batch of stars, and erase the tag that came before the one that we just closed off (the stars now behind the camera turtle). It’s a real slight-of-hand magic trick!

To set up our tags, first we have to add the following before the forever loop:

  begintag 0
  ;dummy 'first' tag (numbered 0)
  make "tag 1
  ;create tag container
  begintag :tag
  ;create second tag using the tag
  ;container value

Because we’re erasing the tag before the previous tag, we need to create a dummy 0 tag to erase when we get started. Then we create a tag container, which contains the value (number) of the current tag, and then we create a tag with a name of that value (1).

We’re all ready to go! Now, inside of the forever loop, we need to add:

    if divisorp 2000 loopcount [
      ;every 2000 loops:
      erasetag :tag - 1
      ;erase the tag BEFORE the tag we just closed
      inc "tag
      ;increment the tag container
      begintag :tag
      ;start a new tag

if is sort of like dountil, except that it checks if the condition is true first and then executes the list of instructions provided to it, but it only does it once and only if the condition is true.

divisorp is a boolean, it returns only true or false. Because of booleans, unlike in other programming languages if does not require a comparison operator. Comparison operators (=, >, < etc) are themselves boolean operators — they return either true or false to if, until, dountil or whatever other command needs a boolean parameter passed to them.

divisorp returns true if the first value passed to it divides equally into the second value. divisorp  in this case returns true if 2000 divides equally into loopcount, which is the total count of the number of loops executed  — for example, the number of times we’ve been through the forever loop.

So every 2000 times through the forever loop, divisorp returns a true value and that causes if to execute the contents of the list. Which is:

endtag – end the current tag

erasetag :tag – 1 – erase the tag before the tag we just closed

inc “tag – increment the tag counter

begintag :tag – start a new tag

And that’s all there is to it. The janitor will come every so often and sweep out those old stars, keeping things running smoothly!


Improvement Three: View Screen On

I’m kind of torn on which is cooler, having the galaxy wink into existence around us or approaching it as if we’ve arrived from the intergalactic void, but if you want to try the winking-into-existence option, here’s how to do it:

First, before the forever loop, we put in a norender command. This stops turtleSpaces from updating the ‘render’, or representation of items in 3D space.

Second, inside the forever loop, before everything else, we need to put the following:

    if loopcount = 1500 [
      print |Engage!|

which as you may remember from the previous section, after 1500 loops will cause if to execute the render command, which turns rendering back on. This ensures there are stars for us to see before we start to see them.

That’s all there is to it!
Bonus Improvement: Spinning Through Space

As a final bonus improvement, after the cam:forward command, insert the following:

cam:rollright 1/10

This will simulate the gentle roll of the spacecraft as it travels through the stars, causing the stars to spin slightly around the spacecraft.

Congratulations! You’ve graduated from starwarp academy! Good job. Welcome to turtleSpaces.


A Starry turtleSpaces Logo Introduction Part Two: Starwarp

In this second part of our introduction to turtleSpaces Logo, we’re going to take the stars we made in the first part, and create a ‘rolling’ starfield we are going to move through using the camera turtle, to create a Star Trek-style warp effect.

To create this effect, the drawing turtle, Myrtle, is going to create stars deep into the space. The camera turtle, Snappy, will move forward (the camera turtle points into the space, towards Myrtle, by default) following Myrtle as she moves deeper, creating stars.

This tutorial is in three parts: First we’ll create the moving starfield, then we’ll cause old stars to vanish (and explain why we need to do that), and finally we’ll set things up so that the starfield ‘pops’ into view, rather than being shown from the beginning.

To begin, we’ll create a new procedure:

TO starwarp

and then we’ll start with some setup commands:

TO starwarp

  lower 3000

  setfillshade 12
  setpenshade -12


We know all of this already from the first part, except for lower, which causes the turtle to descend, from its point of view.

Then we’ll add the main forever loop, which will repeat, well, forever:

  forever [

Then we’ll populate it with the commands needed to make the rolling starfield, and explain them:

  forever [
    lower 10
    setposition {-1500 + random 3000 -500 + random 1000 zpos}
    setpencolor fillcolor
    spot 0.1 * (1 + random 50)
    cam:forward 10


This is the basic routine, but it can use a lot of work, which we’ll get to in a moment.

lower 10 – lowers the turtle 10 turtle units. The turtle descends from its position and orientation, like an elevator.

setposition {-1500 + random 3000 -500 + random 1000 zpos} – this is a bit complicated. setposition sets the turtle’s position in 3D space. It takes a list of three values, X Y and Z. It does not change the turtle’s orientation.

The center of 3D space is [0 0 0]. From Myrtle’s default position, to her left is negative on the X axis, to her right is positive. To her rear is negative in the Y axis, to her front positive. Below her is negative in the Z axis, while above her is positive.

And so, we’re passing a list to setposition made up of two random calculations (for the X and Y coordinates) and Myrtle’s existing Z co-ordinate, which is expressed by the zpos primitive, which is a function that returns Myrtle’s current Z coordinate.

Why the curly braces? Well, traditional Logo lists such as [pig duck cow] aren’t dynamically generated — if you want to remove, add or change values inside of them, you need to do so using commands that manipulate the list. But this can be a bit tedious and so we created the concept of ‘soft lists’ (as opposed to traditional ‘hard lists’) which are lists whose contents are evaluated (or solidified) at runtime, when the interpreter actually processes and executes the command to which the list is attached.

And so, with soft lists, each item is usually either a function (such as random) or a value (such as 10). If you want to add a string value to a softlist, you need to precede it with a ” eg “duck or surround it with pipes eg |duck|.

So, when the setposition command is evaluated, the parser (the part of Logo that decides what to do next) sees the softlist, and evaluates its contents, turning it into a hard list. So it generates the two random numbers, and gets the zpos, and then creates a hard list of 3 items, passing it back to setposition.

randomfillcolor – sets a random fill color (as in part one)

setpencolor fillcolor – sets the pen color to the fill color (as in part one)

spot 0.1 * (1 + random 50) – creates a spot of a size from 0.1 to 5

cam:forward 10 – moves the camera turtle (cam is a shortcut turtle name for the current view turtle). Prefixing a command with turtle: causes the named turtle to execute that command.

…and that’s it for version one! Run the starwarp procedure and see what happens.

Pretty cool huh? But it has a few shortcomings, which we will address in part three.

TO starwarp

  ;this procedure recreates the classic
  ;'moving starfield' effect
  ;the turtle starts deep into the workspace
  ;(by 'lowering' or decreasing its Z-coordinate)
  ;then creating stars (at least a certain distance
  ;away from the center using the distance function)
  ;and continuing to lower. Meanwhile the camera
  ;moves forward (from its perspective), following
  ;the turtle.
  ;Like in reality, the stars are not moving, the
  ;turtles are!
  ;On to the code:
  ;reset the workspace
  ;hide me!
  ;don't draw
  lower 3000
  ;'lower' into the distance
  setfillshade 12
  setpenshade -12
  ;gradiented stars
  ;gradiented shapes graduate between the
  ;pencolor / penshade and the fillcolor / fillshade
  forever [
    lower 10
    ;move the 'star turtle' deeper into the scene
    setposition {-1500 + random 3000 -500 + random 1000 zpos}
    ;flat-ish galaxy
    ;curly braces denote a 'soft list', a list that
    ;is evaluated and created upon execution
    randomfillcolor setpencolor fillcolor
    ;spots and other shapes use the fill color
    ;which randomfillcolor randomly chooses
    ;we set the pencolor to the fill color
    ;for the gradient, because we're only
    ;gradienting the shade
    spot 0.1 * (1 + random 50)
    ;make a randomly-sized star
    ;between 0.1 and 5 turtle units in size
    cam:forward 10
    ;the camera turtle points towards
    ;what it's looking at, so moving
    ;forward decreases its z position
    ;(in its default orientation)
  ;do this forever and ever


A Starry turtleSpaces Logo Introduction Part One: Starfield

Traditional Logo had new users build a house as an introduction, but due to turtleSpaces’ 3D nature, starfields are much more impressive, so we’ll start there.

Click and drag the window above to see all the stars!

Cool huh? First, we’re going to create this simple starfield that wraps around the camera position.

We’ll start by creating the procedure:

TO stars

Then we’ll add in some setup stuff:

TO stars


  setfillshade 12
  setpenshade -12


reset – resets the workspace to its default configuration
hideturtle – hides the turtle
penup – doesn’t draw lines. The turtle draws lines as it moves by default

setfillshade 12 – sets the fill shade to 12. Shades have a range of -15 (light) to +15 (dark) where 0 is normal
setpenshade -12 – sets the pen shade to -12 (light)
gradient – causes shapes that support gradients to use them. They graduate from the pen color / shade to the fill color / shade

We don’t need to use the gradients, but the starfield looks so much better with them enabled!

So, now we’ll carry on and create our main loop:

  repeat 1000 [

This will create 1000 stars, once we fill in the rest of it. Let’s fill in the loop and then go through each command:

  repeat 1000 [
    forward 500 + random 1000
    up 90
    setpencolor fillcolor
    spot 1 + random 10

…and that’s it! Not a lot, is it? Let’s go through it step-by-step.

randomvectors – sets a random three-dimensional orientation for the turtle. This is the equivalent of going left random 360 up random 360 rollright random 360 but in an easier and faster method. Why vectors? Because vectors describe the turtle’s orientation in 3D space, as well as the orientation of all other objects in it. Why build-in a shortcut? Because we wan’t to be easy to use!

forward 500 + random 1000 – move forward 500 turtle-units PLUS 0-999 turtle-units. random returns a random value between 0 and one less than the given value, because 0 is one of the (in this case) possible 1000 choices. This gives our starfield depth while making sure the stars aren’t too close!

random is a function, it doesn’t do anything on its own. Try typing random 30 at the prompt and see, you’ll get:

I don’t know what to do with 10

for example. That value needs to be ‘passed’ to another function or command — its output needs to become someone else’s input, in this case +.

Then +‘s output is passed to forward, which then moves the desired number of turtle-units. This is a big part of how Logo works, and is why commands can be stacked on the same line — they gobble up all of the inputs, and once they do they are ‘complete’ and we know to move on.

up 90 – spots are created around the turtle on its z-plane and so we need to tilt the turtle up 90 degrees so that the stars are facing back towards our view point near the center of the space.

(Note that if you used the lower primitive instead of forward, you wouldn’t need to tilt the turtle up.)

(Note also that different shapes may be positioned in different orientations relative to the turtle. The best way to build things is to progressively build them through the REPL (the command-line interface), using the backtrack command to undo any mistakes.)

randomfillcolor – picks a random fillcolor (the color shapes are colored), a value between 1 and 15 that is NOT the current fillcolor. The alternate form of this is complex: make “oldfillcolor fillcolor dountil fillcolor != :oldfillcolor [setfillcolor 1 + random 15]randomfillcolor is nicer. But you could do it the hard way if you want!

setpencolor fillcolor – because we’re using gradients we need to make the pencolor the new fillcolor so that the stars don’t gradient to a different color. fillcolor is a function that returns the current fillcolor.

spot 1 + random 10 – create a spot graphical primitive between 1 and 10 turtle-units in diameter around the turtle. Remember, random 10 will return a value from 0 to 9. If you just did spot random 10 without adding the 1 you might get 0., which while not an error won’t create anything of substance (literally).

home – return the turtle to the home position, which by default is [0 0 0], the center of the space.

Finally, all of this ‘filling’ is a list, passed to repeat to execute. Logo uses lists for all sorts of things, as you’ll see as you progress in your journey through Logo!

Congratulations, you’ve reached the end of this first (ahem) turtorial! Next in part two, we’re going to make a moving star ‘warp’ effect.

Here’s the full commented listing:

TO stars
  ;TO declares a procedure, in this case one called
  ;'stars'. Procedures can be simply called by name
  ;to execute them. So, at the prompt, you can
  ;type 'stars' (without quotes) to execute this
  ;this is a very simple starfield generator and
  ;a good first project for turtleSpaces. All
  ;we're doing is randomly orienting the turtle,
  ;moving forward a random amount and creating
  ;a randomly-sized spot 1000 times.
  ;10 commands, one function. Dead simple!
  ;But first a little setup stuff...
  ;reset the workspace
  ;hide the turtle
  ;don't draw lines
  ;we could omit this but it looks much better this way:
  setfillshade 12
  setpenshade -12
  ;gradiented stars
  ;gradiented shapes graduate between the
  ;pencolor / penshade and the fillcolor / fillshade
  ;...and that's it for setup stuff!
  ;Now on to the main event:
  repeat 1000 [
    ;this means 'do this 1000 times'.
    ;things between square brackets are lists.
    ;repeat is a command that takes the number
    ;of times it is supposed to execute the contents
    ;of a list, and that list itself. What follows
    ;is the contents of that list:
    ;give the turtle a random 3D orientation.
    ;you could do this yourself using a bunch
    ;of movement commands but we like making
    ;things easy!
    forward 500 + random 1000
    ;move forward 500 turtle units
    ;plus 0-999 turtle units
    ;(random returns a value between
    ;0 and the number passed to it
    ;excluding that number)
    ;random is a function that does not
    ;'do' anything on its own. Try typing
    ;'random 30' at the prompt to see.
    ;It returns a random value, which is
    ;then passed to another function or
    ;a command. In this case, random's
    ;output is passed to the + function,
    ;which then adds 500 to it and then passes
    ;its output to the forward command
    ;this is a big part of how Logo works
    up 90
    ;spots are created around the turtle
    ;on the z-plane, and so we need to tilt
    ;the turtle up
    ;try creating a spot eg 'spot 100'
    ;after the workspace has been reset
    ;to see how the spot is placed relative
    ;to the turtle
    ;different shapes may be places different
    ;ways relative to the turtle
    ;shapes use the fill color
    ;and randomfillcolor picks a random
    ;fill color. You can pick one arbitrarily
    ;using the setfillcolor command
    setpencolor fillcolor
    ;because we're using a shade gradient, we
    ;need to set the pencolor to the fillcolor
    ;otherwise it would gradient to the default
    ;pencolor as well
    spot 1 + random 10
    ;make a spot between 1
    ;and 10 turtle-units in diameter
    ;(remember, random 10 returns
    ;a value between 0 and 9)
    ;return to the home position
    ;(where the turtle started)
  ;perform the above list 1000 times
  ;as you can see, lists can be spread out
  ;across many lines
  ;So, 14 commands, a fillcolor and a couple of
  ;randoms and that's it.
  ;Click and drag the mouse over the view window
  ;to rotate the camera and see all the stars!

How to create and 3D print a chess pawn in turtleSpaces Logo

First, open the weblogo.

Then, click in the bottom right REPL area

Create the ‘head’ of the pawn using the ico primitive

cs penup ico 20

If you start the line with a cs, you can use the up arrow to go back to the line after adding each command (and seeing the result) to edit what you’ve done and add more! Append all of the following instructions on to the same line, then just keep re-executing it.

We’re going to be making a cone next, and cones are created under the turtle. So we need to tiilt the turtle down, and lower it close to the bottom of the ico, in preparation for creating a ‘cut cone’:

dn 90 lo 17

Next we create a cutcone, lower the turtle and create another cutcone. Type help “cutcone to see the parameters…

cutcone 10 20 5 20

Lower the turtle and create the next cone segment

lo 5 cutcone 20 15 5 20

Lower the turtle again and create a larger cone

lo 5 cutcone 10 20 40 20

… smaller cone, but deeper than is visible so that the 3D printer slices the pawn correctly …

lo 40 cutcone 22 28 20 20

… a torus … (type help “torus to see the parameters)

lo 15 torus 5 24 20 20

… another torus …

lo 5 torus 3 28 20 20

… and a cylinder to finish the base! (type help “cylinder to see the parameters)

cylinder 31 5 20

All done! Now you can download the STL file under the File menu, open it up in your slicing program and print it. But don’t forget to type hideturtle first or you might get a surprise!

The pawn sliced in Ultimaker Cura…

The whole line of code should look something like this:

cs ico 20 dn 90 lo 17 cutcone 10 20 5 20 lo 5 
cutcone 20 15 5 20 lo 5 cutcone 10 20 40 20 lo 40 
cutcone 22 28 20 20 lo 15 torus 5 24 20 20 lo 5 
torus 3 28 20 20 cylinder 31 5 20 hideturtle

Easy peasy! Click and hold the left mouse button over the model and drag to rotate it.

Read through the shape guides available under the Docs menu on this website and think about how you could create other chess pieces!

This is the chess pawn as a procedure:

TO pawn
  cs penup
  dn 90 
  ico 20 
  lo 17 
  cutcone 10 20 5 20 
  lo 5 
  cutcone 20 15 5 20 
  lo 5 
  cutcone 10 20 40 20 
  lo 40 
  cutcone 22 28 20 20 
  lo 15 
  torus 5 24 20 20 
  lo 5 
  torus 3 28 20 20 
  cylinder 31 5 20 

You can turn it into a procedure just by typing to pawn in the REPL, pressing the up arrow until you retrieve the pawn code, press enter, and then type end. Then you can save it!

A Fully Commented turtleSpaces Logo Listing of PONG

When I was a kid I had a home PONG machine, one of those that was sold through a department store (in this case Sears) in the late 1970s, which my Dad bought from a garage sale for $5. It was black and white, and the paddles were controlled by knobs on the front of the unit, and the NES had come out by this point and so it wasn’t very enticing for the other kids in the neighbourhood, but my brother and I spent hours playing it anyway.

I’ve decided to take a different approach with this version of PONG, using a single turtle and a single thread taking a linear path through the code, rather than a multi-turtle approach because many programming languages do not have threads and it’s important to think about how you can accomplish things without them.

So, in this example, while the turtle acts as the ball, it also draws the paddles and the scoreboard as needed, and some tricks are used to smooth this over, the way you would in other single-threaded programming environments. Meanwhile, it also demonstrates the directional capabilities of the turtle, and how it operates in the turtleSpaces environment from its own perspective.

This project is divided into a number of user-defined procedures which could be worked on in groups in a classroom setting. There are a number of problems to be solved: moving the ‘ball’, bouncing it off of the walls and paddles, moving the paddles using the keyboard, updating the score. Pong is a well-known game and so its mechanics require little explanation. The key here is how do we do all of that with a single turtle?

Read on to find out!


SETABOUT |Use A and Z to control left paddle, K and M to control right paddle|

CREATORS [melody]


TO pong

  ;this version of Pong uses a single turtle to re-create
  ;the game, employing a more traditional linear method
  ;rather than a multi-turtle, multi-threaded method.
  ;For a demonstration of the latter, see turtleSpaces
  ;Breakout, under the Examples menu in the web interpreter

  ;this is an extremely thoroughly commented listing, so
  ;please read through. It should be fairly straightforward
  ;to adapt this into a series of lessons for your class.

  ;There are many more comments than lines of code in this
  ;listing! Hopefully this will demonstrate to both you
  ;and your students the simplicity and power of Logo

  ;It might be helpful to first read the introduction to
  ;one of the Logo books available on the turtleSpaces

  ;executes the setup procedure. Here we initialize containers
  ;(variables), draw the playfield, create the ball and paddles

  ;Note: medium-blue keywords indicate user-defined procedures

  forever [
    ;checks for and acts on player keypresses

    ;moves the ball depending on a few factors

    ;checks the ball position and acts if necessary

  ;square brackets indicate lists. In this case, we're
  ;providing the forever primitive (or command) with a list
  ;containing the three procedures we wish to execute
  ;'forever' (and also some comments, which get ignored)

  ;lists can generally be spaced out over multiple lines
  ;for readability. They also discard whitespace between
  ;items in the list. Logo is not a stickler for whitespace
  ;or formatting!

  ;there, that was easy, right? ;)
  ;now for the nitty-gritty...


TO setup

  ;reset the workspace. This returns all the turtles to
  ;their default states and positions

  ;we're going to use the system time to 'speed up' the ball
  ;as gameplay goes on, and so we'll reset it now. The time
  ;is not reset by the reset primitive and so we need to
  ;declare it seperately

  ;no need to draw lines here! But for fun you can
  ;try to comment this line out and see what happens.
  ;It gets kind of messy, particularly because the turtle
  ;'ball' sneaks away and moves the paddles and updates
  ;the scoreboard while you aren't looking...

  ;don't delay while audio is played. For historical reasons,
  ;sound primtives (commands) such as toot and playnotes cause
  ;execution to pause while they are being played, but we can
  ;turn that off to make things proceed more smoothly

  ;create the turtle model (a voxel, or 3D pixel, to keep
  ;in the 1970s mood). This is a user-defined procedure

  ;draw the 'arena' or playfield. This is also a user-
  ;defined procedure

  make "keytimer 0
  ;here we 'make' a 'container' used as a counter
  ;to limit the rate at which keys can be pressed
  ;and prevent 'flooding' of the game with too many

  make "movepaddle false
  ;used to indicate if the paddle has been moved
  ;and increase the speed of ball movement to allow the
  ;game to 'catch up'

  make "leftscore 0
  make "rightscore 0
  ;initialize the score containers and assign them
  ;values of 0

  ;set up and draw the scoreboard. This is a user-defined

  make "leftpaddle -20
  drawpaddle "leftpaddle
  ;set the initial position of the left paddle
  ;and draw it, using the user-defined drawpaddle
  ;procedure, to which we pass the name of the paddle

  make "rightpaddle -20
  drawpaddle "rightpaddle
  ;set up and draw the right paddle

  ;reset the turtle's position to the home position
  ;and its orientation to the default

  ;show the turtle (ball)

  right 10 + (random 70) + ((random 3) * 90)
  ;set starting angle for the ball by turning
  ;the turtle to the right a random amount, ensuring
  ;that it doesn't send the ball straight up or down!
  ;That would get boring very quickly...


  ;Round brackets () ensure the order of operations
  ;is correctly processed. turtleSpaces uses BEDMAS
  ;but processes equal-order operations right to
  ;left (like original Logo), which means that without
  ;brackets, the last 'random' primitive would be passed
  ;270 (3 * 90) which is not what we want!

  ;All right, we're all ready to go!
  ;let's return back to the main pong procedure...


TO makemodel

  setpremodel [setvectors [[0 1 0] [0 0 1] [1 0 0]]]
  ;setpremodel takes a list of commands, in this case
  ;we're providing it with a single command which itself
  ;takes a list of three lists, indicating orientation
  ;vectors (you don't need to worry about vectors for now).
  ;But you can see in this example how lists can get nested
  ;on a single line.

  setmodel [
    setfillcolor yellow
    ;there are 16 default colors, and they each have an
    ;associated keyword, which just returns the index
    ;number of the color, which in the case of yellow is 13

    setfillshade -5
    ;you can set a shade for the color, which can be a
    ;value between -15 and 15. Excluding pure white and black
    ;there are 434 default colors, created using a combination
    ;of shade and color. But you can also define arbitrary
    ;colors using the definecolor primitive

    back 2.5 raise 2.5
    ;raise elevates the turtle in the Z dimension,
    ;and this is the extent of the use of 3D movement
    ;primitives in this source code listing

    slideleft 2.5 voxel 5
    ;because voxels are created to the front,
    ;right and beneath the turtle, we need to
    ;move the turtle before making the voxel if
    ;we want the voxel to be centered on the
    ;turtle's position

  ;The contents of lists can be spaced out across
  ;multiple lines for easier readability

  ;some explanation:

  ;The setvectors command inside the setpremodel command
  ;'fixes' the orientation of the voxel used as the turtle
  ;model regardless of the orientation of the turtle itself
  ;to appear more like a classic Pong pixel, while
  ;the setmodel primitive creates the voxel model itself.

  ;(For technical reasons, you can't assign a fixed
  ;orientation to a model inside a setmodel command)

  ;Comment out the makemodel command in the setup procedure
  ;by preceding it with a semicolon (like these comments are)
  ;to play instead with the actual turtle, and watch it as
  ;it changes direction when it bounces off the walls and

  ;This is because we use the turtle's current 'heading'
  ;or direction to determine how much to turn when we bounce
  ;the ball (or turtle) off of things. In reality, the turtle
  ;is constantly moving forward


TO arena

  ;let's draw the playfield:

  setpencolor lightblue
  setpos [0 -100]
  ;setpos takes a list of two values, X and Y.
  ;Remember, coordinates behind and to the left of
  ;the turtle's default position (at the center of
  ;the screen) are negative.

  ;Another primitive, setposition, takes a list
  ;of three values (X, Y and Z) with Z being positive
  ;above the turtle's default position, and negative
  ;below it. But because we're only working in two
  ;dimensions, we can use setpos here

  repeat 20 [mark 5 forward 5]
  ;draw dotted center line
  ;using 20 'dashes' or marks.
  ;the mark primitive uses the pen color
  ;to create a filled rectangle, like a marker

  setpc mediumblue
  ;setpc is shorthand for setpencolor. setfc is
  ;similarly shorthand for setfillcolor

  ;mediumblue returns 6, the palette index of
  ;the color that is a medium blue. Functions can
  ;be chained in intricate ways, passing their return
  ;values to other functions and finally commands
  ;(primitives that do not return a value). Logo is
  ;very flexible this way!

  setpos [-200 -100]
  right 90
  ;turn the turtle to the right 90 degrees
  mark 400
  ;mark the bottom line

  setpos [-200 100]
  mark 400
  ;mark the top line

  ;That was simple! You could make it more complex
  ;if you like, but the retro aesthetic is groovy!


TO moveball

  if :movepaddle = false [
    repeat 4 [forward 1]

    ;if the paddles haven't been moved since the last
    ;time we moved the ball, let's move it four turtle
    ;units forward

    if and xpos < 145 xpos > -145 [
      make "move time / 1000
      if :move < 20 [repeat int :move [forward 1]]
      else [repeat 20 [forward 1]]
    ;if the ball is presently well inside the area between
    ;the paddles, lets move the ball a bit more based on the
    ;amount of time elapsed since the round has started
    ;(to a maximum of 20 turtle units)

    ;This way, the ball will get faster and faster
    ;until someone misses it!


  else [
    ;if the paddles HAVE been moved...

    if and xpos < 145 xpos > -145 [
      ;and the ball is well inside the area between the paddles...

      repeat 10 [forward 1]
      ;move 10 turtle units instead of 4 so we can 'catch up'
      ;and reduce the 'lag' caused by moving the paddle

      make "move time / 1000
      if :move < 20 [repeat int :move [fd 1]] [repeat 20 [fd 1]] ;also apply additional time-based 'speed' as above... ;fd is a shortcut for forward. Also if you supply a second ;list of instructions to an if statement, it will execute ;the second list if the comparison provided is false, ;similarly to the else primitive (although you can use ;else later in a procedure as it will take note of the ;result of the last comparison.) ] else [ repeat 4 [forward 1] ] ;but if the ball is closer to the paddles then let's move ;it only four, to provide a little more help catching it ;but also to ensure we detect the ball has 'hit' the ;paddle and doesn't accidentally pass through it, which ;will cause the player distress! make "movepaddle false ;finally, reset the movepaddle 'flag' to false ;(since we've dealt with it) ] ;and we have moved the ball! END TO checkball ;in this procedure, we check to see if the ball has ;passed over the boundary at the top and the bottom of ;the playfield, or if it has 'touched' the paddles, ;or if it has gone out of play, and we act accordingly if ypos > 100 [
    ;if the ball has exceeded the top boundary of
    ;the playfield (the center of the playfield
    ;has x and y values of 0, which increase going
    ;upward and to the right, and decrease going downward
    ;and to the left):

    toot 600 10
    ;make a 600 hz tone for 10/60ths of a second

    if heading > 180 [left 2 * (heading - 270)]
    ;the turtle's heading is a degree value increasing
    ;from zero in a clockwise direction (to the right)
    ;and so when the turtle is pointing right, its heading
    ;is 90, when pointing down 180, and when up 0.

    ;Here we check if the turtle is pointing to the left,
    ;(has a heading value greater than 180) and if so, we
    ;turn the turtle left twice the value of the heading
    ;minus 270 degrees, because we know the value of the
    ;heading is going to be greater than 270 degrees since
    ;the turtle is at the top wall.

    ;And so we turn double the angle between the turtle's
    ;current heading and the angle of the wall, thus causing
    ;the turtle to 'bounce' off of the wall

    else [right 2 * (90 - heading)]
    ;otherwise, we can assume the turtle is pointing to
    ;the right, and we do a similar calculation, instead
    ;subtracting the heading from 90 degrees, because
    ;we know the heading is going to be 90 degrees or less,
    ;based on the turtle striking the top boundary, and
    ;the turtle pointing to the right.

    forward 2 * (ypos - 100)
    ;because the ball can move more than one turtle unit
    ;at a time in order to make the gameplay speedy, we
    ;need to bring the ball back 'in bounds' so that we
    ;don't inadvertently read that the ball is out of bounds
    ;again before it has a chance to re-enter the playfield

    ;there is probably a more accurate way to do this, but
    ;simply doubling the distance the ball is out of bounds
    ;seems to be sufficient. But if the ball ever gets
    ;'stuck' out of play, you know what you need to fix!


  if ypos < -100 [ toot 600 10 if heading > 180 [right 2 * (90 + (180 - heading))]
    else [left 2 * (90 - (180 - heading))]
    forward 2 * abs (ypos - -100)

  ;this is similar to the above, except with the bottom
  ;boundary. Note that because the location of the bottom
  ;boundary is negative, we need to get the absolute (positive)
  ;value of the current turtle position minus the boundary
  ;(using the abs primitive) because the result of that
  ;calculation is otherwise negative

  ;now we check the paddles:

  if and xpos > 164 xpos < 171 [ ;if the ball is in the right paddle X 'zone': if and ypos > :rightpaddle ypos < (:rightpaddle + 40) [ ;AND if the ball is in the right paddle Y 'zone' (the ;area currently occupied by the paddle): toot 500 10 ;make a 500hz tone for 10/60ths of a second if heading > 90 [right 2 * (90 - (heading - 90))]
      else [left 2 * heading]
      ;this is similar to the top and bottom boundary
      ;calculations, except instead of changing based on
      ;if the turtle is facing right or left, here we
      ;do different calculations based on if the turtle is
      ;pointing downward or upward

      right -20 + (ypos - :rightpaddle)
      ;apply 'english' to the ball -- depending on the
      ;location on the paddle the ball is striking, turn
      ;the turtle to the left (which it does when a negative
      ;number is provided to the right primitive) or
      ;the right a related number of degrees. This allows
      ;the player to affect the trajectory of the ball
      ;and makes the game more interesting!

      forward 2 * (xpos - 164)
      ;make sure the ball is no longer in the 'strike'
      ;zone for the paddle, because otherwise it could
      ;be detected again and cause some strange behavior.
      ;There's probably a better way to do this, but
      ;this method seems to suffice


  ;Let's do this all again for the left paddle:

  if and xpos < -164 xpos > -171 [
    ;if the ball is in the left paddle X 'zone':

    if and ypos > :leftpaddle ypos < (:leftpaddle + 40) [ ;and the ball is in the vertical area occupied by ;the paddle: toot 500 10 ;toot if heading > 270 [rt 2 * (360 - heading)]
      else [lt 2 * (90 - (270 - heading))]

      left -20 + (ypos - :leftpaddle)
      ;apply 'english'

      forward 2 * (abs (xpos - -164))
      ;get away from the paddle

  ;finally, we check if the ball has sailed past a player:

  if or xpos > 200 xpos < -200 [ ;if the ball's position exceeds either the left ;or right boundaries: toot 100 100 ;make a 100hz tone for 100/60ths of a second ;(1.66 seconds) if xpos > 200 [inc "leftscore]
    else [inc "rightscore]
    ;if the ball is past the right boundary, credit the left
    ;player with a point. Otherwise, credit the right player
    ;with a point. The inc primitive increases the value of
    ;the specified container by one. Note that it takes a
    ;quoted name, not a colon name for the container.

    ;update the score

    ;we reset the time because we're using it to
    ;speed up the ball

    wait 100
    ;wait 100/60ths of a second, for the toot to
    ;finish sounding

    if or :leftscore = 10 :rightscore = 10 [
      ;if either player's score is now 10:

      setpos [-90 -20]
      setfillcolor orange
      foreach "i |GAME OVER| [typeset :i wait 10]
      ;type out GAME OVER, but with a delay between
      ;each character, for dramatic effect

      playnotes "L3B3A3G3F3L6E3
      ;play a little ditty, and wait for it to finish

      ;game over, man, game over!

    drawpaddle "leftpaddle
    drawpaddle "rightpaddle
    ;Because we're using a single turtle for this
    ;game, it is a good idea to 'clean' and redraw
    ;the game elements between rounds, because otherwise
    ;the 'turtle track' will gradually accumulate all of
    ;the ball movements and slow down its rendering over
    ;time. We want a speedy game so let's clean it up

    home showturtle
    right 10 + (random 70) + ((random 3) * 90)
    ;reposition, and randomly orient the ball

  ;we're done for now!


TO checkkeys

  ;in this procedure, we will check to see if a key
  ;has been pressed, and if so, act accordingly
  ;(by moving a paddle, if a paddle movement key
  ;has been pressed)

  inc "keytimer
  ;Because of key repeat, we want to ensure the player
  ;can't 'flood' the game with too many keypresses, and
  ;by using a simple counter, we can ensure this
  ;doesn't happen

  if and keyp :keytimer > 1 [
    ;and so, we check to see if a key has been pressed
    ;(keyp) AND the :keytimer container's value is
    ;at least two. That means at least two ball movement
    ;cycles have to pass between paddle moves, keeping
    ;the game moving!

    make "keytimer 0
    ;reset the keytimer container to 0

    make "key readchar
    ;take a key from the keybuffer and put it into
    ;the key container

    ;clear the keyboard buffer. If we don't, key
    ;repeat (or a player hammering the key) can clag
    ;up the game

    if :key = "a [if :leftpaddle < 60 [ make "leftpaddle :leftpaddle + 20 drawpaddle "leftpaddle ] ] ;if the 'a' key is pressed, and the left paddle isn't ;already as high as it can go, increase its position ;by 20 turtle units and redraw it if :key = "z [if :leftpaddle > -100 [
        make "leftpaddle :leftpaddle - 20
        drawpaddle "leftpaddle
    ;if the 'z' key is pressed, and the left paddle isn't
    ;already as low as it can go, decrease its position by
    ;20 turtle units and redraw it

    if :key = "k [if :rightpaddle < 60 [ make "rightpaddle :rightpaddle + 20 drawpaddle "rightpaddle ] ] ;if the 'k' key is pressed, and the right paddle isn't ;already as high as it can go, increase its position by ;20 turtle units and redraw it if :key = "m [if :rightpaddle > -100 [
        make "rightpaddle :rightpaddle - 20
        drawpaddle "rightpaddle
    ;finally, if the 'm' key is pressed, and the right paddle
    ;isn't already as low as it can go, decrease its position
    ;by 20 turtle units and redraw it

  ;that's all for now!


TO updatescore

  ;this procedure updates the scoreboard

  ;hide the ball

  ;suspend drawing the graphical elements while
  ;we update the scoreboard

  settypesize 20
  ;set the size of the type, the graphical text

  if tagp "score [erasetag "score]
  ;if there is already a score 'tag', erase it.
  ;Tags mark areas of the 'turtle track' so that
  ;they can be copied, removed or used to create
  ;turtle models

  begintag "score
  ;create a score tag

  ;reset the turtle's position and orientation

  setpos [-60 50]
  setfillcolor red
  typeset :leftscore
  ;type the left player's score

  setpos [40 50]
  setfillcolor green
  typeset :rightscore
  ;type the right player's score

  ;close the score tag

  ;resume rendering -- voila, the score is updated!


TO drawpaddle :paddle

  ;drawpaddle takes a parameter, which is 'passed' into
  ;the :paddle container, which exists only inside this
  ;procedure. Once we exit this procedure, it vanishes!

  ;We use the value contained in :paddle (the value we
  ;passed to drawpaddle) to decide which paddle to draw
  ;and reduce the amount of code we need to write (since
  ;we only need one procedure for both paddles)

  ;because we are going to erase the old paddle and
  ;then draw a new paddle, stop rendering the graphics
  ;so that it just seems like the paddle moved.
  ;It's magic!

  make "heading heading
  make "pos pos
  ;save the current position and heading of the turtle
  ;into two containers with similar names

  ;reset the turtle's position and orientation

  if :paddle = "leftpaddle [
    setfillcolor pink
    setpos {-170 :leftpaddle}
  ;if the paddle we're drawing is the left paddle,
  ;set the fill color to pink and move to the left paddle's

  ;Curly-braces indicate a 'soft list', a list that is evaluated
  ;at the time of execution. Soft lists can contain :containers
  ;and functions which then get resolved to their values / results
  ;before being passed to the primitive they are attached to.
  ;This is how you can dynamically pass values to primitives
  ;that ordinarily take 'hard' [] lists

  ;Note that if you pass a string value in a soft list, you
  ;will need to precede it with a " or place it between pipes ||

  else [
    setfc cyan
    setpos {165 :rightpaddle}
  ;otherwise set the fill color to cyan and set the right
  ;paddle's position

  if tagp :paddle [erasetag :paddle]
  ;if there's already a paddle, erase its 'tag' from the
  ;turtle track. This erases the paddle, if it exists

  begintag :paddle
  ;create a new 'tag' with the name of the paddle, eg

  ;tags mark sections of the turtle track for manipulation
  ;later, such as to erase them, or use them to create a
  ;turtle model

  voxeloid 5 40 5
  ;create the paddle voxel

  ;close the tag

  setpos :pos
  setheading :heading
  ;reset the turtle's heading and position

  make "movepaddle true
  ;set the movepaddle container to true. This tells
  ;code in the moveball procedure that the paddle has
  ;moved and to make up for the time we lost doing it

  ;start updating the graphic elements again
  ;abra cadabera! The paddle has moved!

  ;We've reached the end of this listing!

  ;Now, exactly how much Python code would you
  ;have had to have written in order to accomplish
  ;all this? Hint: a lot more!

  ;Thanks for reading! I hope this helps you on your
  ;journeys inside turtleSpaces. Bon Voyage!




An Introduction to Logo Movement with Myrtle the Turtle

This catchy song introduces the turtleSpaces Logo movement primitives.

This animation was made inside turtleSpaces, and demonstrates its ability to create animated content.

You can use screen capture software such as ScreenFlow or the built-in SAVEWEBM primitive to export a recording of the screen, and then sync it to your music.

You can also load music in OGG format into turtleSpaces and then work on synchronizing your animation with it in realtime using the SLEEP and WAIT primitives. This animation was done that way. Keep in mind that the animation may play back at different speeds on different computers unless you use the TIME primitive to keep everything locked to timing points!

It took around three hours to create the animation in this video using the Logo programming language. The captions are done by creating a turtle (I named “caption”) and then directing it to create the captions using the CONTROL primitive, eg control “caption [typeset |And I ORBIT all round…|]


A Guide to 3D Printing Using turtleSpaces

This guide is in development

Valid shapes:

Shapes must be closed, that is they must have no exposed ‘inside’ faces. Closed shapes include:

voxel, voxeloid, sphere, spheroid, icosphere, icospheroid, cappeddome, cappeddomoid, cylinder, torus, etc.

Note: the cylinders used for large pen sizes (rope) are valid shapes and appear to slice correctly.

Warning: open shapes will be rejected by your 3D printer’s ‘slicing’ software!

Making hollow forms:

To create a ‘hollow’ form, an inverted shape must be created within the outer shape. For example:

ico 50 ico -40


voxel 100 fd 90 lo 90 sr 90 voxel -80

cone 50 100 10 rr 180 ra 10 cone 40 -80 10

Tutorial #1: Simple Tree

These procedures draw a simple tree in a classic Logo lined style, and a forest of these trees. First, let’s take a look at the tree procedure as a whole.

TO tree :scale
  slideleft 8.25 * :scale
  forward 20 * :scale
  repeat 4 [
    slideleft (10 * (5 - repcount)) * :scale
    left 30
    slideright (12 * (5 - repcount)) * :scale
    right 30
  left 30
  slideright 5 * :scale
  right 60
  slideright 5 * :scale
  left 30
  repeat 4 [
    right 30
    slideright (12 * repcount) * :scale
    left 30
    slideleft (10 * repcount) * :scale
  back 20 * :scale
  slideleft 8.25 * :scale

Now let’s go through it a line at a time. The procedure starts out with the procedure declaration TO, then the name of the procedure tree, then a single parameter, :scale

TO tree :scale

Remember that the colon indicates that scale is a container – but parameters are only containers inside of their procedures. This is important.

Notice that there is a companion END at the end of the procedure. Every TO must have an END.

slideleft 8.25 * :scale

slideleft is a movement primitive, it tells the selected turtle to shift its position to its left the given number of turtle-units, which correspond to OpenGL units. In this case, the number of turtle units to shift left is 8.25 * the value of :scale, which is given as a parameter when tree is called.

forward 20 * :scale

forward is another movement primitive, it tells the selected turtle to move forward the given number of turtle-units.

repeat 4 [

repeat is a loop primitive. It takes two parameters, the number of times to repeat the instruction list, and the instruction list itself, which is bookended by an open square bracket and a closed square bracket. repeat executes the instruction list the specified number of times. The contents of the instruction list can be spread across multiple lines, as it is here, and which follow:

slideleft (10 * (5 - repcount)) * :scale

slideleft is similar to slideright. The rounded brackets tell turtleSpaces which part of the supplied expression to evaluate first. If there are brackets inside brackets, the innermost ‘nest’ is evaluated first. turtleSpaces resolves mathematical expressions using the PEMDAS (BEDMAS) ordering, but often some clarity is required. In the case of the line above, we want to ensure that repcount (the current iteration of the repeat loop) is subtracted from 5 before it is multiplied by 10, and all of that needs to be done before it is multiplied by :scale

turtleSpaces parses right to left. This means that it evaluates expressions in the right of the instruction first. What this means for the statement above is that without brackets, repcount * :scale would be evaluated first. Then 10 * 5. Then the result of repcount  * :scale would be subtracted from 10 * 5. This is not what we want! So we need brackets.

left 30

left rotates the turtle to its left the specified number of degrees, in this case 30.

slideright (12 * (5 - repcount)) * :scale

The slideright instruction is similar to the slideleft instruction above, except in this case we are going to slide a bit more right than we did left.

right 30

Similarly to left, right turns the turtle to the right the given number of degrees, also 30 in this case.


The closing bracket finishes the instruction list. All of this is collected and given to repeat, which then repeats the instructions the given number of times (4).

left 30 
slideright 5 * :scale 
right 60 
slideright 5 * :scale 
left 30 
repeat 4 [ 
  right 30 
  slideright (12 * repcount) * :scale 
  left 30 slideleft (10 * repcount) * :scale 

The next four instructions draw the ‘peak’ of the tree, and the following repeat loop the opposite side of the tree.

back 20 * :scale 
slideleft 8.25 * :scale

back is similar to forward, except the turtle backs up. slideleft you already know!


end finishes the procedure.

Once the procedure is entered, you can call it by typing tree and then a scale ‘factor’.

tree 1

tree 0.5

tree 2

0.5 makes a tree half as large as one, and 2 twice as large.

TIP: To make the turtle move smoothly, use the fluid primitive.


Let’s make another procedure that draws a whole forest!

TO forest
  repeat 10 [
    slideleft -150 + random 300
    forward -100 + random 150
    tree (2 + random 8) / 10

Again stepping through each instruction:

TO forest

This procedure declaration doesn’t take a parameter – all we need to know is inside it, and all it will take to call it is a single word: forest.


reset causes the graphical workspace to return to its initial state, erasing any contents and moving all of the turtles to their home positions. It doesn’t erase any procedures but it does erase containers! Unlike other Logos (and like virtually every other programming language), in turtleSpaces all containers (variables) need to be declared by a procedure, they cannot exist on their own and are not otherwise saved in project files. While it seems like a good idea if they could be, it’s really not.

repeat 10 [

The following instructions will be repeated ten times. You can change that to whatever number you want. Or you could make it a parameter in the forest procedure definition, and then replace the number 10 with the container you specified there, such as :trees


Turtles (except for Snappy the camera turtle) start with their pen down after a reset or when they are created. This means they draw lines whenever they move. You can stop them from drawing lines with the penup instruction.


home causes the turtle to move to its home position. In Myrtle’s case, her default position is [0 0 0], that is 0 horizontally (x), 0 vertically (y) and 0 in depth (z). All of these are ‘centered’ in their given axis (x y or z). With two-dimensional procedures and programs you don’t need to worry about depth (z). There are other primitives for positioning the turtle we will get into in another tutorial. But for now, we’ll stick with what we’ve learned so far.

slideleft -150 + random 300

random picks a random number between and including 0 and the number that is given to it, but not that number. So, random 300 will return a number between 0 and 299. Why? Because you might want 0 as a possible result. And you might want to pick from a range of however many numbers you specified (in this case 300). 300 numbers including 0 gives you a range from 0 to 299. I know it would be easier if it was a range from 0 to 300, but that would be 301 numbers in total, not 300!

We take the number chosen by random, and add it to -150. If the number is still negative, that means that the turtle will slide to the right rather than the left! 

forward -100 + random 150

Similarly, if the result of -100 + random 150 is negative, the turtle will move backwards, not forward. All of the movement primitives behave the same way. A negative value will cause it to move in the opposite direction indicated by the primitive. For example, a negative angle will cause left to turn right.


This ‘shorthand’ instruction causes the turtle to pick a random pen color, a value between 1 and 15. You could do the same if you wrote:

setpencolor 1 + random 15

but randompencolor gets straight to the point. To see a list of the turtle’s default colors, type showcolors. The colors are based on the default colors in the low resolution mode of the Apple II! They’re historical (and I’m maybe a little hysterical).


Similarly to penup, pendown puts the turtle’s pen down, and causes it to draw again.

tree (2 + random 8) / 10

And now that we’re in a random position, we’re going to draw a tree (the first procedure) providing a random :scale value. random cannot generate parts of numbers, only whole numbers, so to get a fraction we need to divide (/) our random result by 10. So, after calculating, the parameter passed to tree will be a value between 0.2 and 0.9


And that closing square bracket signals the end of the instruction list provided to the repeat above.


That’s the END of the forest procedure and our first (ahem) turtorial (ba-dum).

Let’s give it a go:

Because it’s completely random, it may take a few tries to get a distribution that you like.

Finally, set the title of your creation using the settitle primitive:

settitle "forest

and then save it using the save primitive:


And now you can visit your forest again whenever you like.

You’ve entered your first Logo program. Well done!