Category Logo Programming Examples

Logo Fireworks

This Logo program uses hatchlings to create simple 2D fireworks. The turtle’s model is changed into an icosphere to simulate a launching firework, and then more hatchlings are used to create the starburst. The trails are merged back into the main turtle ‘track’ and the hatchlings are terminated, leaving the rendered drawing on-screen at the conclusion of the program.

Read through the code (and pay particular attention to the ;comments) to see how the program works. It is made up of two procedures: fireworks, and burst.

TO fireworks
  ;simple 2D fireworks
  repeat 5 + random 10 [
    ;launch at least 5 but not more
    ;than 14 fireworks
    ;pull up the turtle's pen
    ;return to [0 0 0]
    raise repcount
    ;raise the turtle so its lines
    ;don't overlap
    sety -100
    ;set the turtle's y position to
    hatch [left -60 + random 121 burst]
    ;hatch a new turtle, turn that
    ;turtle left some random value
    ;between -60 (same as right 60)
    ;and 60 degrees, then execute the
    ;burst procedure
    wait 30 + random 31
    ;wait between one half and one
    ;second before launching the next
  ;repeat until all fireworks launched

TO burst
  ;a single firework 'burst'
  ;originally called 'firwork' but
  ;that was a little confusing!
  penup hideturtle
  newmodel "firework [icosphere 1]
  setmodel "firework
  ;create and use a simple model
  ;made from a single icosphere
  ;set a random fill color
  ;models inherit the fill color
  ;if not otherwise set inside
  ;the model
  repeat 100 + random 601 [
    ;travel between 200 and 700
    ;turtle steps
    forward 1 - 0.002 * repcount
    ;move forward 1 step minus
    ;a fraction based on the current
    ;loop iteration. This has the
    ;effect of slowing us down
    ;over time (reduce velocity)
    sety ypos - 0.0005 * repcount
    ;change the y position of the
    ;turtle a fraction based on the
    ;current loop iteration, simulating
    ;very simple gravity
    sleep 2
    ;take a little nap
  ;repeat until finished travelling
  setpencolor fillcolor
  ;set the pen color to the fill color
  repeat 20 + random 21 [
    ;create between 20 and 40 fragments
    raise 0.01
    ;ensures seperation between
    ;overlapping lines
    hatch [
      ;draw lines
      right random 360
      ;turn right between 0 and 359
      make "steps 30 + random 71
      repeat :steps [
        ;travel between 30 and 100
        ;turtle steps
        ;setpenshade -15 + repcount / 5
        setpenshade -15 + 30 * (repcount / :steps)
        ;set the pen shade based on the
        ;loop iterations
        fd 1 - 0.002 * repcount
        ;move forward minus "drag"
        sety ypos - 0.01 * repcount
        ;apply fake gravity
      ;merge the hatchling's track
      ;(output) into the track of its
      ;permanent ancestor, in this case
    ;done with hatchling
  ;repeat until finished

turtleArt Thumbtack String Corkboard

This commented Logo source code creates a thumbtack, a cork board, and then draws random string art using them.

Read the ;comments to learn more about what the primitives do and what their shortcuts are.

Try some of the primitives out in turtleSpaces interactive mode (the myrtle prompt) and see what they do!

You can drag-select the source code and then paste it into the turtleSpaces editor (which you enter by typing ed and Enter or pressing control-shift-E) using control-shift-V (paste)

Then exit back to interactive mode using control-shift-O (save) and then type stringart and Enter to execute it.

Change elements of the code and see what happens!

TO tack
  ;create a thumbtack, or pushpin
  setfillcolor pick without 5 without 10 range [1 15]
  ;pick a number between 1 and 15 except 5 and 10
  cylinder 4 2 20
  ;cylinder takes width depth sides
  lower 2
  ;lowers the turtle eg moves beneath it
  cutcone 2 3 7 20
  ;cutcone takes topwidth bottomwidth depth sides
  lo 7
  ;lo is shortcut for lower
  cylinder 4 2 20
  lo 2
  setfc pick [5 10]
  ;setfc is shortcut for setfillcolor, the color
  ;used for painting shapes
  cylinder 1 5 8
  lo 5
  cone 1 1 8
  raise 16
  ;raises the turtle, eg moves above it
  ;all done!

TO corkboard
  ;create a corkboard to push our pins into
  setpos [-205 -116]
  setfillcolor 8 setfillshade -4
  ;setfillshade takes a range of -15 to 15
  forward 5 lo 3 slideright 5
  voxeloid 400 222 4
  ;voxeloid takes width height depth
  ;this creates the surface of our corkboard
  slideleft 5 ra 3 back 5
  setfs 6
  ; setfs is shortcut for setfillshade
  voxeloid 10 232 10
  ;this and subsequent voxeloids
  ;create the frame
  right 90
  sl 10
  ;sl is shortcut for slideleft
  voxeloid 10 410 10
  fd 400
  ;fd is shortcut for forward
  left 90
  bk 10
  ;bk is shortcut for back
  voxeloid 10 232 10
  fd 222 lt 90 bk 10
  ;lt is shortcut for left
  voxeloid 10 410 10
  ;that's all folks!

TO stringart
  ;resets the workspace
  snappy:run pick [
    [pullout 10]
    [pullout 30 orbitdown 70]
    [orbitleft 20 orbitdown 45 rollright 10 pullout 60 rr 10 sl 25 lo 40]
    ;rr is shortcut for rollright
    [orbitright 20 orbitdown 45 rollleft 10 pullout 60 rl 10 sr 25 lo 40]
    ;rl is shortcut for rollleft
  ;pick a camera sequence and execute it as
  ;snappy, the camera turtle
  ;if we need snappy to do more sophisticated
  ;things, we can store procedures inside him
  ;logo is all about having the freedom to do
  ;things in multiple ways!
  setpenwidth 1 + random 5
  ;sets the 'width' of the pen, from 1 to 5
  ;randps and randfs (aka randompenshade and randomfillshade)
  ;set a random shade between -12 and 12
  ;execute corkboard procedure
  repeat 20 + random 20  [
    ;do the following 20 + 0-19 times:
    ;shortcut for randompencolor
    setpos {-190 + random 380 -100 + random 200}
    ;move to a random position
    ;pu is shortcut for penup
    ;save the turtle state on the 'stack'
    ;you can have multiple states on the stack
    raise 10 + random 4 up -10 + random 20 rr -10 + random 20
    ;move into a semi-random position
    ;to give our tacks a more natural-looking
    ;moving in negative values moves the opposite
    ;direction, eg left -90 turns right 90 degrees
    ;execute tack procedure
    ;(create a tack)
    ;load the turtle state from the 'stack'
    ;this puts it back where and how it was when
    ;we pushed it
    ;pd is shortcut for pendown
    ;this will cause us to create a line to the
    ;next random position
  ;repeat the above however many times
  ;specified by 20 + random 20
  ;ta da!
  ;for extra credit, get some tacks, a corkboard, and some
  ;colored string and make some string art of your own!
  ;consider the angles the turtle would need to turn to travel
  ;along the line of string created between all the pins
  ;were the turtle to travel the string like driving
  ;down a road


Solar System Simulation

This Solar System simulation features the ability to add orbiting moons to the planets, a few of which are already defined. It is not entirely to scale (the planets are larger than they are in reality).

Use the scrollwheel or finger-scroll to zoom in and out, and drag the mouse to rotate around the sun!

TO solarsystem
  cam:pullin 100
  ;pull in the camera turtle
  ;make a starfield in the distance:
  setfillcolor 5
  repeat 200 [
    home randomvectors
    forward 2500 + random 100
    up 90
    spot 1 + random 5
  ;define planetary parameter lists:
  make "distance [0 39 72 100 152 520 953 1918 3006 3953]
  ;the distance of the planets from the sun in 100ths of an AU
  ;The first value is the sun itself, which is at the home position
  make "size [100 3 7.5 7.9 4.2 88.7 74.6 32.6 30.2 1.41]
  ;the planets are in scale relative to each other but not the sun,
  ;which is much larger than the value we have given it here
  make "color [13 8 9 7 1 14 11 7 2 10]
  ;each planet's color (and the sun)
  make "tilt [0 7 3.3 0 1.85 1.31 2.49 0.77 1.77 17.14]
  ;ecliptical tilt
  make "speed [0 4.1 1.62 1 0.53 0.084 0.034 0.011 0.006 0.004]
  ;speed of orbit
  ;define moons:
  make "moon [0 0 0 1 2 0 0 0 0 0]
  ;how many moons?
  make "mooncolor [[][][][5][8 9][][][][][]]
  make "moondistance [[][][][3][2 3][][][][][]]
  make "moonsize [[][][][2.1][1.12 0.62][][][][][]]
  make "moontilt [[][][][0][1 2][][][][][]]
  make "moonspeed [[][][][30][40 30][][][][][]]
  ;moon parameters, similar to planets
  ;only earth and mars are populated here,
  ;add the others as you like!
  ;create the sun and planets:
  repeat 10 [
    make "planet repcount
    ;store the current planet for use inside hatch
    newmodel repcount {"setfc item :planet :color "ico 0.1 * item :planet :size}
    ;create a new turtle model using the data from the :color and :size lists
    ;we use a 'soft list' {} to create 'hard data' for use by newmodel
    ;as the turtle model is rendered each frame
    hatch [
      ;create a new hatchling
      setmodel :planet
      ;set the turtle model
      ;set the orbit (anchor) point to the current position
      pullout 0.5 * item :planet :distance
      ;pull away from the anchor the distance specified in the :distance list
      orbitright random 360
      ;orbit around a random amount
      orbitup item :planet :tilt
      ;orbit up (tilt) the amount specified in the :tilt list
      ;hatchlings are not visible by default
      if 0 < item :planet :moon [ ;are there any moons? foreach "moon item :planet :mooncolor [ ;for each moon, let's create it the same way we did planets: make "moonnumber loopcount make "moonmodel word :planet loopcount newmodel :moonmodel {"setfc :moon "ico 0.1 * item :moonnumber item :planet :moonsize} make "parent turtle ;define the 'parent' turtle for use by the moon hatchling hatch [ ;hatch the moon setmodel :moonmodel dropanchor right random 360 pullout item :moonnumber item :planet :moondistance orbitup item :moonnumber item :planet :moontilt showturtle forever [ fixate query :parent [position] ;keep 'fixated' (look at and set the anchor point to) ;the parent turtle's position. if orbitdistance > item :moonnumber item :planet :moondistance [
                pullin orbitdistance - (item :moonnumber item :planet :moondistance)
              ;if it's getting away from us, try and catch up!
              orbitright 0.1 * item :moonnumber item :planet :moonspeed
              ;orbit the planet based on the :moonspeed
              sleep 6.25
              ;take a little nap
      forever [
        orbitright 0.05 * item :planet :speed
        sleep 25
      ;orbit the sun (we're back to the planet now) based on the value
      ;from the :speed list. Then take a little nap (wait 1)
  ;hide Myrtle, although she would be sitting in the sun

One-a-Day: Space Tube Ride using TUBE and TUBEARC

Myrtle creates a 3D space tube ride for her to fly through…

Sometimes Myrtle needs to take a break and have a little fun! One of the things she can do is make a space tube and fly through it, using the TUBE and TUBEARC primitives:

TUBE makes a straight tube, and takes the parameters <radius> <depth> and <sides>. So, tube 20 10 10 makes a tube with a radius of 20 turtle units, a depth of 10 turtle units and 10 sides.

TUBEARC creates a curved tube, and takes the parameters <thickness> (the radius of the tube itself) <radius> (the radius of the arc itself) <sides> <totalsegments> and <arcsegments> – the last two are the total segments that would make up a complete torus, or donut, and the number of segments we actually want to create. So values of 20 and 10 will create a half-donut: tubearc 10 20 20 20 10

There are four procedures to our space tube program: createcourse, drawcourse, followcourse and spacetube.

createcourse generates the data that represents the tube’s course. It creates a course made up of 100 segments, represented by a word made up of a series of numbers between 0 and 4, where 0 represents a straight section of tube, and 1 to 4 represent four directions of turn. We use dountil to ensure we don’t repeat the same direction of turn more than once and end up looping back on ourself. Although, createcourse is not perfect and it is still possible for the course to cross over itself – but it’s a good thing tube walls aren’t solid!

drawcourse creates the graphical representation of the contents of the :course container we generated using createcourse. We use the twosided primitive to generate reflective “normals” on both sides of the shapes we generate from that point forward, and we use randfc to set a random ‘fill color’ or shape color for each segment as we create them.

We use foreach to step through each segment listed in the :course container, creating either a tube or a tubearc where appropriate.

While tubes are created directly under Myrtle, tubearcs are created around her, and so this means we need to do a bit of calisthenics to position the tubearcs where they need to go. Follow them through one command at a time to see how they work! We wait 2 / 60ths of a second between each segment creation so we can follow it.

followcourse causes Myrtle to fly through the created course, once again iterating through the :course container using foreach. fluid causes Myrtle to move forward smoothly, and to turn through the arcs we use a repeat. sleep is another form of wait, which takes milliseconds as a parameter instead of wait‘s 60ths of a second.

setpremodel allows us to ‘prepend’ commands to the turtle’s model, allowing us to make Myrtle look like she’s turning the appropriate way as she moves through the course.

Finally, spacetube puts it all together and is the procedure we execute to create the maze and fly through it. spacetube performs a reset, turns off the text layer, tells Snappy (the camera turtle) to pull away from Myrtle 1000 turtle units, tells Snappy to ‘follow’ Myrtle (turn to face her when she disappears from his view), then executes the createcourse procedure, the drawcourse procedure, sets the view (camera) turtle to Myrtle, turns Myrtle’s ‘light’ on (so we can see properly inside the tubes), turns Snappy’s light off, sets the camera ‘view point’ of Myrtle a bit up and behind her, and then executes followcourse.

TO spacetube
  snappy:pullout 1000
  snappy:follow "myrtle
  setview "myrtle
  setlight 1
  snappy:setlight 0
  setviewpoint [0 10 -30]

TO createcourse
  make "course empty
  make "last 5
  repeat 100 [
    dountil :last != :segment [make "segment random 5]
    make "course word :course :segment
    if :segment != 0 [make "last :segment]

TO drawcourse
  pu twosided
  foreach "segment :course [
    if :segment = 0 [tube 50 200 20 lo 200]
    else [rt :segment * 90
      dn 90 rr 90 sl 100 tubearc 50 100 20 20 5 fd 100 rr 90 lt 180 + :segment * 90]
    wait 2

TO followcourse
  dn 90
  foreach "segment :course [
    if :segment = 0 [fd 200]
    if :segment = 1 [setpremodel [rt 22.5] repeat 90 [fd 1.75 rt 1 sleep 1] setpremodel []]
    if :segment = 2 [setpremodel [dn 22.5] repeat 90 [fd 1.75 dn 1 sleep 1] setpremodel []]
    if :segment = 3 [setpremodel [lt 22.5] repeat 90 [fd 1.75 lt 1 sleep 1] setpremodel []]
    if :segment = 4 [setpremodel [up 22.5] repeat 90 [fd 1.75 up 1 sleep 1] setpremodel []]

One-a-Day: Amiga Ball Bounce

The Commodore Amiga computer had a famous ‘bouncing ball’ demonstration program, a checkered bouncing ball. It was a bit different than this, but this is certainly reminiscent of it.

We love the Amiga demo so much that we created a special checkered ball primitive, AMIGABALL, and a second ‘oid’ primitive, AMIGABALLOID, which is used to create the ‘compressed’ ball.

Rather than turning the turtle into a ball (one method), this method ‘cleans’ the ‘turtle track’ after rendering each frame of the animation (which we wait for using the NEXTFRAME primitive).

The background voxel is actually an ‘inverted’ voxel, a voxel that is created using a negative size value. Due to the nature of OpenGL, only the insides of negative voxels are visible, which is why we can see inside it, and not see the face directly in front of us. Because we clean the ball each frame, we need to use a HATCHLING to create and hold the voxel in place. The hatchling just sleeps while the program runs.

The CAM: prefix directs the current view turtle (usually ‘Snappy’) to do things. You could also use SNAPPY: in this case but CAM: is shorter.

DOUNTIL PENCOLOR != FILLCOLOR [RANDFC] ensures that the ball has two different colors. The Amigaball(oid) uses both the pen and the fill colors in its construction, the only 3D primitive to do so.

To stop the ball from bouncing, press the Escape key.

TO bounce
  reset fs
  penup lower 120 slideright 120 fd 120
  hatch [randomfillcolor voxel -240 forever [wait 10]]
  ;create the cube, using a hatchling so it persists
  cam:run [oup 10 ra 5]
  setspheroidaxis 1
  ;stretch the spheroid on the vertical, default is horizontal
  dn 90 rr 10
  randpc dountil pencolor != fillcolor [randfc]
  ;set random pen and fill colors
  forever [
    repeat 14 [
      amigaball 50 10 20
      setx xpos - 1 lo 4 lt 2
      nextframe clean
    ;move down
    repeat 11 [
      amigaballoid 50 10 20 1 - repcount / 20
      lo 2 lt 2 nextframe clean
    ;compress ball
    randpc dountil pencolor != fillcolor [randfc]
    ;change colors
    repeat 11 [
      amigaballoid 50 10 20 0.375 + repcount / 20
      ra 2 lt 2 nextframe clean
    ;expand ball
    repeat 28 [
      amigaball 50 10 20
      setx xpos + 1 ra 4 lt 2
      nextframe clean
    ;move up
    repeat 11 [
      amigaballoid 50 10 20 1 - repcount / 20
      ra 2 lt 2 nextframe clean
    ;compress ball
    randpc dountil pencolor != fillcolor [randfc]
    ;change colors
    repeat 11 [
      amigaballoid 50 10 20 0.375 + repcount / 20
      lo 2 lt 2 nextframe clean
    ;expand ball
    repeat 14 [
      amigaball 50 10 20 setx xpos - 1
      lo 4 lt 2 nextframe clean
    ;move back to center



One-a-Day: FRAG

The FRAG primitive creates a filled shape out of the current turtle position and her last two positions. For example:

FD 100 RT 90 FD 100 FRAG

will create a triangle.

While turtleSpaces has a variety of shape primitives, sometimes you need to create an arbitrary shape, and FRAG aids you in this.

Take this example, which draws a star:

TO star
  repeat 4 [
    forward 100
    right 170
    forward 86
    left 70
    forward 86
    right 170
    forward 100
    right 180

We can change the number of sides the star has by changing the number of repeats and fiddling with the values a bit:

TO star2
  repeat 5 [
    fd 100 rt 170
    fd 86 frag
    lt 88 fd 86
    rt 170 fd 100
    frag rt 180

First, let’s change our star procedures so they can take a :size parameter, like so:

TO star1 :size
  repeat 4 [
    fd 100 * :size
    rt 170
    fd 86 * :size
    lt 70
    fd 86 * :size
    rt 170
    fd 100 * :size
    rt 180

In this case, :size is a ratio, that is, 0.5 will make a star half the size, and 2 will make a star twice the size of the default.

You can change the color of the star using the SETFILLCOLOR primitive, or set a random fill color with RANDFC.

The following procedures create a sky full of stars:

TO star1 :size
  repeat 4 [
    fd 100 * :size
    rt 170
    fd 86 * :size
    lt 70
    fd 86 * :size
    rt 170
    fd 100 * :size
    rt 180

TO star2 :size
  repeat 5 [fd 100 * :size rt 170 fd 86 * :size frag lt 88 fd 86 * :size rt 170 fd 100 * :size frag rt 180]

TO star3 :size
  repeat 6 [fd 100 * :size rt 170 fd 86 * :size frag lt 100 fd 86 * :size rt 170 fd 100 * :size frag rt 180]

TO star4 :size
  repeat 7 [fd 100 * :size rt 170 fd 86 * :size frag lt 108.625 fd 86 * :size rt 170 fd 100 * :size frag rt 180]

TO star5 :size
  repeat 8 [fd 100 * :size rt 170 fd 86 * :size frag lt 115 fd 86 * :size rt 170 fd 100 * :size frag rt 180]

TO stars
  reset cam:setposition [0 0 0]
  cam:fixate [0 0 0]
  cam:setviewpoint [0 0 0]
  cam:newworker [forever [up 0.1 lt 0.1 rr 0.1 wait 1]]
  repeat 200 [
    pu home randori fd 400 + random 1000 up 90
    lt random 60 pd randpc randfc randfs randps
    make "size (10 + random 90) / 100
    run {pick [star1 star2 star3 star4 star5] :size}

Type STARS and press Enter to see the stars!

FRAG’s sister, SHARD creates a three-dimensional FRAG with depth beneath it. This depth is supplied as a parameter, in turtle-units, eg. SHARD 5. Try replacing FRAG with SHARD 5 in one of your star procedures and see what happens! (You’ll need to drag the camera around to see the sides of the star)

Quick Play: Warped Logo Spirals

One of the best things about turtleSpaces is its capacity for discovery through play.

Today I was playing with spirals:

I started off by creating a simple spiral using the RARC primitive and a simple REPEAT loop. This loop uses REPCOUNT (the current repeat loop iteration) to define the radius of the arc, resulting in an ever-growing spiral.

I clicked and dragged on the window to rotate the camera around the spiral. But let’s make the spiral itself three-dimensional:

I’ve ‘warped’ the spiral by placing three rarcs inside of the repeat loop, along with a ROLLLEFT (rl) and a ROLLRIGHT (rr). With the twisting and turning, the turtle ends up roughly halfway around a somewhat distorted circle, which you can see if you type:

cs rarc 90 20 rr 90 rarc 90 20 rl 90 rarc 90 20

cs repeat 2 [rarc 90 20 rr 90 rarc 90 20 rl 90 rarc 90 20]

If we orbit the camera (by clicking and dragging) around our spiral, we can see just how warped it is!

What else can we do with it?

Whoa! What happened there? I added a dn (down) 10 to the end of the loop. Let’s take a look from another vantage point:

That’s pretty distorted. Let’s try a smaller value, say 2:

And what about 5?

What about down (dn) instead of up?

Okay, pretty neat. How about if at the end of each loop we also roll left (rl) a little?

Another angle:

Let’s play around with the values of down (dn) and rollleft (rl) a bit:

What if we do a right turn (rt) instead of a roll right (rr)?

And what if we add the roll back in?

Let’s play around with the values a bit:

All right, that’s enough from me! But you can play around with the different values and see what happens. That’s all part of the fun of turtleSpaces!


Make it Rain!

TO rain


  penup hideturtle
  setbackgroundcolor blue setbackgroundshade 12
  make "count 0

  forever [

    inc "count
    setposition {-300 + random 600 300 -300 + random 600}
    make "size 0.25 + ((1 + random 20) / 20)
    begintag "teardrop
    setfillcolor pick [2 3 6 7 14]
    setfillshade -10 + random 20
    up 90
    cutsphere 10 * :size 20 20 1 13
    lower 4.5 * :size
    cone 8.9 * :size 18 * :size 20
    down 90
    newmodel :count "teardrop
    hatch [
      setmodel :count
      make "speed (5 + random 10) / 10
      while ypos > -300 [
        back :speed rollright 0.25
    wait 5 + random 25


‘Snake’ written in turtleSpaces Logo

TO snake
  ;empties (erases) all containers (variables),
  ;returns the turtles to their default positions,
  ;restores their states to defaults
  ;sets all the turtles, except for the default ones
  ;(myrtle, snappy, libby) 'free' - erasing them
  ;don't draw
  setbackgroundcolor lightblue
  ;don't show the executing turtle (typically myrtle)
  ;don't show up in output from the near primitive
  ;don't delay when playing sounds (the default)
  print |Use keys J and K to turn snake, eat apples!|
  ;print instructions to the screen
  ;create the 'ground'
  setfillcolor brown
  setpos [-185 -115]
  lower 5
  quad 185 * 2 115 * 2
  make "score 0
  make "length 8
  ;the default snake length, in segments
  make "queue empty
  ;this snake implementation uses a push / pull queue
  ;to keep track of the snake's turns
  repeat :length [push "fd "queue]
  ;all segments start moving forward
  newmodel "ball [setfillcolor 12 icosphere 5]
  newmodel "head [setfillcolor 4 icosphere 6]
  newmodel "apple [setfillcolor red icosphere 4]
  ;create turtle models, where ball is a non-head segment
  ;create the turtles that make up the starting snake:
  repeat :length [
    nameturtle repcount
    ;create a new turtle
    ;each segment has the number of its segment as its name
    control repcount {"wrap "penup "back repcount "* 10 "showturtle}
    ;initialize the new turtle and move it into position
    ;{} denotes a 'softlist', a list whose contents are evaluated
    ;at execution
    control repcount [
      setbounds [-185 -115 185 115]
    ;sets the bounds used for wrapping. [] denotes a 'hardlist', a
    ;list whose contents are fixed and not evaluated
    if repcount = 1 [control 1 [setmodel "head]]
    ;if this is the first segment, give it the 'head' model
    else [control repcount [setmodel "ball]]
    ;otherwise, give it the 'ball' (segment) model
  ;set up the 'apple' turtle:
  nameturtle "apple
  control "apple [
    penup setmodel "apple showturtle
    dountil (count near position 5) = 1 [
      setpos {-180 + (10 * random 36) -100 + (10 * random 20)}
  ;set a random position until such times as the apple is not near
  ;any snake segments (turtles)
  ;leading parameters used in comparison operators (=, >, < etc)
  ;must be enclosed in {}'s if more than a single value or primitive
  ;because the parser evaluates right to left
  ;the main loop:
  forever [
    ;don't render while we shuffle the snake forward
    for {"i 1 :length} [
      make "move item :i :queue
      ;set the move variable to the :i item in the :queue
      switch "move
      case "fd [ask :i [forward 10]]
      case "rt [ask :i [right 90 forward 10]]
      case "lt [ask :i [left 90 forward 10]]
      ;switch / case statements execute instructions based on the
      ;contents of the container (variable) provided to switch
      ;ask creates a new worker that executes the commands using
      ;the specified turtle (the turtle named by :i)
      ;so, in this for loop we are iterating through the :queue,
      ;moving the turtle segments based on the item present in
      ;each space in the queue (fd, rt or lt)
    while workers != [] [sleep 1]
    ;sleep execution until the ask workers finish moving their
    ;turtles (the snake). This way, when we enable the render,
    ;we won't catch the snake mid-move.
    ;enable rendering again
    wait 40 - :score
    ;wait units are 60ths of a second. The higher the score, the
    ;shorter the wait will be
    ;user control:
    if keyp [
      ;did the user press a key? If so, do this stuff:
      make "input readchar
      ;take the output from the readchar primitive and place it
      ;into the input container. keyp indicates if there is a character
      ;already in the keyboard buffer. If readchar is called when there
      ;is no character in the buffer, it waits.
      switch "input
      case "j [push "lt "queue]
      ;if the user pressed the j key, push lt to the front of the queue
      case "k [push "rt "queue]
      ;if the user pressed the k key, push rt to the front of the queue
      otherwise [push "fd "queue]
      ;if no case has been satisfied since switch (the user pressed a key
      ;other than j or k), push fd to the front of the queue
    else [push "fd "queue]
    ;similarly, if the user hasn't pressed any key, push fd to the front
    ;of the queue
    ignore dequeue "queue
    ;ignore (drop) the last value off the end of the :queue
    ;dequeue would ordinarily return this value, ignore sends it into
    ;the ether
    ;is there something occupying the same space as the head of the snake?
    if (count (near 1:position 5)) > 1 [
      ;near returns the names of the turtles within the specified distance (5)
      ;count returns the count of the number of turtles returned by near
      ;if there is more than one (the head itself), we will execute the following:
      if memberp "apple near 1:position 5 [
        ;is it an apple?
        playsound "chomp
        inc "score
        ;increment the value in the score container by one
        (print |Yum! Score:| :score)
        ;to pass more than the default number of parameters to a primitive, wrap
        ;it in parentheses (round brackets)
        dountil (count near apple:position 5) = 1 [
          control "apple [setpos {-180 + (10 * random 36) -100 + (10 * random 20)}]
        ;place a new apple
        repeat 2 + int (:score / 10) [
          queue "pa "queue
          ;a pa or 'pass' will cause these new segments not to move on the next
          ;iteration, until movement commands are 'shuffled' on to their places
          ;in the queue
          inc "length
          ;increment the value of the length container by one
          nameturtle :length
          ;create a new turtle with the name contained in :length (the new length)
          (control :length {"hideturtle "setmodel ""ball "wrap "penup "setpos
          "query :length - 1 leftbracket "position rightbracket "setvectors
          "query :length - 1 leftbracket "vectors rightbracket})
          ;set up the new turtle and make sure it's positioned next to the
          ;previously last turtle, and is facing in the same direction
          control :length [setbounds [-185 -115 185 115] showturtle]
          ;set the bounds of the new turtle and show it
          wait 10
        ;add new segment to the snake
      ;and if it's not an apple?
      else [
        print "crash! playsound "crash finish
      ;game over, man! game over!

Example: Spiral Tunnels

TO spiraltunnelanimation
  reset fullscreen
  setbackgroundcolor 0
  dropanchor penup
  raise 190 pullout 50
  repeat 2100 [
    setfillshade -15 + int (repcount / 65)
    setfillcolor (item 1 + remainder int (repcount / 2.35) 8
    {red orange yellow green lightgreen cyan blue magenta})
    voxeloid 7 12 20
    orbitleft 10.1 lower 0.6
  home lower 3000
  setmodelscale 2.5
  down 90 right 180
  make "dir 0
  snappy:setanchor [0 0 -1050]
  forever [
    forward 2 rollright 0.1
    if zpos > 1500 [setz -3000]
    snappy:rollleft 0.05 snappy:pullin 0.1
    if and :dir = 0 snappy:orbitdistance = 0 [
      make "dir 1
      snappy:right 180
      setbackgroundcolor 15
      snappy:setanchor [0 0 180]
    if and :dir = 1 snappy:orbitdistance = 0 [
      make "dir 0
      snappy:right 180
      setbackgroundcolor 0
      snappy:setanchor [0 0 -1050]