🎨 Worksheet 2: Keyboard Sketcher

Let’s make a simple drawing program! You’ll control a “pen” with the arrow keys to draw anything you want. This worksheet will teach you how to handle continuous key presses for smooth movement, a key skill for making better games.

Game Screenshot

📝 IMPORTANT: Type, Don’t Copy!

Remember, typing the code helps your fingers learn the keyboard shortcuts and syntax. Run your code after each part to see what you’ve built! The goal is to understand why the code works.


Part 1: Make a Dot

First, let’s create a “pen” (a small circle) that will eventually be our drawing tool.

# Pen's starting position
pen_x = 400
pen_y = 300
pen_speed = 4

def setup():
    size(800, 600)

def draw():
    background(50, 50, 50) # Dark grey background

    # Draw the pen
    fill(255, 255, 0) # Yellow
    no_stroke() # Removes the black outline from shapes
    circle(pen_x, pen_y, 10)

run_sketch()

Run it! You should see a dark window with a small yellow dot in the middle. It doesn’t move yet, but we’ll fix that soon.

🔬 Experiment Time!

Before moving on, let’s play with the code you just wrote. Try to solve these small challenges.

🤔 Challenge: Can you make the window a perfect square?
💡 Hint 1

The size() function in setup() controls the window’s width and height.

💡 Hint 2

Change size(800, 600) to size(400, 400). Remember to change it back!

🤔 Challenge: Can you change the background to pure black?
💡 Hint 1

The background() function uses (Red, Green, Blue) values from 0-255. Black is the absence of all color. You can find any color you want with an online tool like rgbcolorpicker.com.

💡 Hint 2

Use background(0, 0, 0). What numbers would you use for pure white?

🤔 Challenge: Can you make the pen start in the top-left corner?
💡 Hint 1

The top-left corner is at coordinate (0, 0).

💡 Hint 2

You need to change the starting values for the pen_x and pen_y variables at the top of your code.


Part 2: Handling Key Presses

For smooth movement, we need to know when a key is being held down. We’ll use boolean variables to do this. A boolean is the simplest type of variable: it can only ever be True or False. We’ll use them like light switches to remember if a key is currently down.

Add these new variables and functions to your code.

# --- At the top of your file ---
# Pen's starting position
pen_x = 400
pen_y = 300
pen_speed = 4

# NEW: Boolean variables to track which keys are held down
up_pressed = False
down_pressed = False
left_pressed = False
right_pressed = False

# --- At the bottom of your file, before run_sketch() ---
def key_pressed():
    global up_pressed, down_pressed, left_pressed, right_pressed
    if key_code == UP:
        up_pressed = True
    if key_code == DOWN:
        down_pressed = True
    if key_code == LEFT:
        left_pressed = True
    if key_code == RIGHT:
        right_pressed = True

def key_released():
    global up_pressed, down_pressed, left_pressed, right_pressed
    if key_code == UP:
        up_pressed = False
    if key_code == DOWN:
        down_pressed = False
    if key_code == LEFT:
        left_pressed = False
    if key_code == RIGHT:
        right_pressed = False

🔬 Experiment Time!

This code doesn’t change the visuals yet, but a lot is happening! Let’s investigate what these new functions do.


Part 3: Make the Dot Move!

Now, let’s use our boolean variables inside the draw() loop to finally move the pen. Because draw() runs over and over, it will check these True/False states 60 times a second, creating smooth movement.

Update your draw() function like this:

def draw():
    global pen_x, pen_y
    background(50, 50, 50)

    # --- ADD THIS MOVEMENT LOGIC ---
    # Check the booleans 60 times per second
    # In Python, "if up_pressed:" is the same as "if up_pressed == True:"
    if up_pressed:
        pen_y = pen_y - pen_speed
    if down_pressed:
        pen_y = pen_y + pen_speed
    if left_pressed:
        pen_x = pen_x - pen_speed
    if right_pressed:
        pen_x = pen_x + pen_speed
    # ---------------------------------

    # Draw the pen
    fill(255, 255, 0)
    no_stroke()
    circle(pen_x, pen_y, 10)

Run it! Hold down the arrow keys. The yellow dot should now move smoothly around the screen!

🔬 Experiment Time!

🤔 Challenge: The pen feels a bit slow. How can you make it faster?
💡 Hint 1

Look at the variables at the very top of your code. Is there one that sounds like it controls speed?

💡 Hint 2

Try changing the value of the pen_speed variable to 10. What about 1?

🤔 Challenge: Stop the pen from flying off the screen!
Hint 1: Where to check?

You’ll need if statements inside your draw() loop to constantly check the pen’s position after it has moved.

Hint 2: How to check the left edge?

To stop it from going past the left edge (where x is 0), you can check if pen_x < 0:. Inside that if statement, you need to set pen_x back to 0.

Hint 3: How to check the other edges?

You can set its position back to the edge, like pen_x = 0. The screen’s width and height are stored in special variables called width and height. You’ll need to check if pen_x > width, pen_y < 0, and pen_y > height.


Part 4: Leaving a Trail

A moving dot is cool, but a drawing program needs to draw! How can we remember every position the pen has visited? With a list! A list is a special type of variable that can hold many values in order.

First, add empty lists at the top of your code:

# At the top of your file, with your other variables
trail_x = []
trail_y = []

Now, update your draw() function to save the pen’s position in every frame and then draw the entire trail.

def draw():
    global pen_x, pen_y
    background(50, 50, 50)

    # Move the pen (same as before)
    if up_pressed: pen_y = pen_y - pen_speed
    if down_pressed: pen_y = pen_y + pen_speed
    if left_pressed: pen_x = pen_x - pen_speed
    if right_pressed: pen_x = pen_x + pen_speed

    # --- ADD THIS NEW CODE ---
    # Add the current pen position to our trail lists
    trail_x.append(pen_x)
    trail_y.append(pen_y)

    # Draw the entire trail using a loop
    i = 0
    while i < len(trail_x):
        fill(100, 200, 255) # Light blue trail
        no_stroke()
        circle(trail_x[i], trail_y[i], 5)
        i = i + 1
    # --------------------------

    # Draw the pen on top so it's visible
    fill(255, 255, 0)
    no_stroke()
    circle(pen_x, pen_y, 10)

Run it! Move the dot around. It should now leave a beautiful light blue trail wherever it goes!

What’s happening?

🔬 Experiment Time!

🤔 Challenge: What determines the spacing of the dots in your trail?

Set the trail circle size to 2 and the pen_speed to 1. Draw a line. Now, set pen_speed to 20. Draw another line. Why are the dots spaced out more when the speed is higher?

💡 Hint

The draw() loop runs at the same rate no matter what (60 times per second). In each frame, we add one dot. If the pen moves a larger distance between frames (higher speed), what does that do to the distance between the dots?


🎉 Complete Program!

You’ve made an interactive drawing program! This is a huge step. You learned how to handle smooth keyboard input and how to use lists to remember and draw hundreds of objects on the screen.

🚀 Make It Your Own! (Challenges)

🎮 Challenge 1: Clear the Screen

Add a feature so that when you press the ‘c’ key, the drawing clears. You can clear a list by setting it back to an empty one, like this: trail_x = [].

💡 Need help?

You need to modify the key_pressed() function. For letter keys, we check key instead of key_code. Inside an if key == 'c': block, you’ll need to reset both the trail_x and trail_y lists.

✅ Solution

Add this to your key_pressed() function:

# In key_pressed()
# ... your other key code for arrows ...
    global trail_x, trail_y
    if key == 'c':
        trail_x = []  # Make the list empty again
        trail_y = []  # Make this one empty too!
🎮 Challenge 2: Change Colors On The Fly

Make it so pressing ‘r’ changes the trail to red, ‘g’ to green, and ‘b’ to blue.

  • Red is (255, 0, 0)
  • Green is (0, 255, 0)
  • Blue is (0, 0, 255)
💡 Need help?
  1. Create three new variables at the top for the trail color: trail_r = 100, trail_g = 200, trail_b = 255.
  2. In key_pressed(), add if statements for ‘r’, ‘g’, and ‘b’ that change the values of those variables. Remember to make them global!
  3. In your while loop in draw(), change the fill() command to use these variables: fill(trail_r, trail_g, trail_b).
✅ Solution
# At top of file
trail_r, trail_g, trail_b = 100, 200, 255

# In key_pressed()
def key_pressed():
    #...
    global trail_r, trail_g, trail_b
    if key == 'r':
        trail_r, trail_g, trail_b = 255, 0, 0
    if key == 'g':
        trail_r, trail_g, trail_b = 0, 255, 0
    if key == 'b':
        trail_r, trail_g, trail_b = 0, 0, 255

# In the while loop in draw()
fill(trail_r, trail_g, trail_b)
🎮 Challenge 3: Eraser and Lift-Pen Mode

Make it so holding down the mouse button makes the pen “erase” the trail. When you release the mouse, it should go back to drawing mode. py5 has a built-in boolean variable is_mouse_pressed that is True when the mouse is held down.

💡 Need help?

The “eraser” is just drawing with the same color as the background! Inside your while loop, you’ll need an if/else statement that checks is_mouse_pressed. If it’s true, fill() with the background color. If it’s false, fill() with the trail color.

✅ Solution

Inside your draw() function, just before you add points to the trail, check if the mouse is pressed.

# Inside draw()
# ... after movement code ...

# Check for eraser mode
is_erasing = False
if is_mouse_pressed:
    is_erasing = True

# When adding to trail, you could also add the mode
# (This is an advanced idea, an easier way is in the next hint)

# Easiest way: just change the color when drawing the trail
# Inside your while loop:
if is_mouse_pressed:
    fill(50, 50, 50)  # The background color
else:
    fill(trail_r, trail_g, trail_b) # The normal trail color

circle(trail_x[i], trail_y[i], 20) # Use a bigger circle for erasing
🚀 Bonus Idea

Can you use this same idea to stop drawing when the SHIFT key is held down? This would let you move the pen to a new spot and draw disconnected lines! (Hint: py5 has a variable is_key_pressed that is True if any key is down, and key tells you which one it is. You could check if is_key_pressed and key == SHIFT:).