To attach an image to a Python Turtle, register the image as a shape with screen.register_shape("rocket.gif") and then call t.shape("rocket.gif"). Up to Python 3.13 that shortcut only accepts GIF files; PNG works too, but through tkinter.PhotoImage (shown below), and Python 3.14 adds PNG to register_shape() directly. This guide also covers background images, resizing, making images turn with the turtle, and the errors you’re likely to hit.
Every program was run with Python 3.12.5 (Tk 8.6.13) and Pillow 12.3.0 on Windows 11, and the screenshots are the real windows. The sample images (a rocket and a night sky) were made with Pillow; use your own files in the same folder as your script.
Use a GIF image as the turtle
import turtle
screen = turtle.Screen()
screen.setup(500, 400)
screen.register_shape("rocket.gif") # 1. register the image file
t = turtle.Turtle()
t.shape("rocket.gif") # 2. use it as the turtle's shape
t.penup()
t.goto(0, -60)
t.pendown()
t.pensize(3)
t.goto(0, 60) # it moves and draws like any turtle
turtle.done()
- The file name you register is also the shape name.
- The image file must be in the folder Python runs from, or use a full path.
- The image moves, draws and stamps like any turtle shape.
PNG images (Python 3.13 and earlier)
In Python 3.12 and 3.13, passing a PNG file name to register_shape() fails, because it only accepts names ending in .gif:
import turtle
screen = turtle.Screen()
try:
screen.register_shape("rocket.png")
except turtle.TurtleGraphicsError as err:
print("TurtleGraphicsError:", err)
screen.bye()
Output:
TurtleGraphicsError: Bad arguments for register_shape.
Use help(register_shape)
But Tk 8.6, which Turtle draws with, can read PNG files, including transparent ones. Load the image yourself with tkinter.PhotoImage and register it as an image Shape:
import tkinter as tk
import turtle
screen = turtle.Screen()
screen.setup(620, 470)
screen.bgpic("sky.png") # PNG background works
png = tk.PhotoImage(file="rocket.png") # Tk 8.6 reads PNG, including transparency
screen.register_shape("rocket", turtle.Shape("image", png))
t = turtle.Turtle()
t.shape("rocket")
t.penup()
t.goto(-120, -80)
turtle.done()
PNG in Python 3.14 and later
Python 3.14 added PNG (and PGM/PPM) support to register_shape() and bgpic(), and a second form that takes a shape name plus a file name. Per the official documentation (not run here, since this page was tested on 3.12):
screen.register_shape("rocket.png") # Python 3.14+
screen.register_shape("rocket", "rocket.png") # Python 3.14+: your own shape name
t.shape("rocket")
If your code must run on older versions too, use the PhotoImage method above; it works on all of them.
Add a background image with bgpic()
screen.bgpic() puts an image behind everything. It’s centred and not scaled, so make the window about the size of the image. bgpic("nopic") removes it again:
import turtle
screen = turtle.Screen()
screen.setup(620, 470) # the image is 600 x 450
screen.title("Background image")
screen.bgpic("sky.png") # shown centred, not stretched
writer = turtle.Turtle(visible=False)
writer.penup()
writer.color("white")
writer.goto(0, -20)
writer.write("Good night!", align="center", font=("Arial", 32, "bold"))
turtle.done()
More on backgrounds in Python Turtle background.
Resize an image
PhotoImage can shrink with subsample(n) and grow with zoom(n) by whole numbers. stamp() leaves a copy of the current shape on the screen:
import tkinter as tk
import turtle
screen = turtle.Screen()
screen.setup(560, 340)
original = tk.PhotoImage(file="rocket.png")
screen.register_shape("small", turtle.Shape("image", original.subsample(2))) # half size
screen.register_shape("normal", turtle.Shape("image", original))
screen.register_shape("big", turtle.Shape("image", original.zoom(2))) # double size
t = turtle.Turtle()
t.penup()
for x, name in [(-170, "small"), (-40, "normal"), (140, "big")]:
t.goto(x, 0)
t.shape(name)
t.stamp() # leave a copy of the image behind
t.hideturtle()
turtle.done()
For any size (for example 64 × 64), resize with Pillow first; see the conversion example below.
Make the image turn with the turtle
Image shapes don’t rotate: the turtle’s heading changes but the picture stays the same. Turtle’s own documentation says so, and the shape type confirms it:
import turtle
screen = turtle.Screen()
screen.register_shape("rocket.gif")
t = turtle.Turtle(shape="rocket.gif")
t.left(90)
print("heading:", t.heading(), "| shape:", t.shape()) # the heading changes...
print("shape polygon:", t.get_shapepoly()) # ...but an image has no polygon to rotate
screen.bye()
Output:
heading: 90.0 | shape: rocket.gif
shape polygon: None
The fix is to prepare one rotated copy of the picture per direction with Pillow (pip install pillow) and switch shapes as the heading changes:
import turtle
from PIL import Image, ImageTk
screen = turtle.Screen()
screen.setup(620, 620)
screen.bgcolor("#0f2030")
base = Image.open("rocket.png") # the picture points up (heading 90)
for heading in range(0, 360, 45):
# Pillow rotates counterclockwise, like Turtle headings
frame = ImageTk.PhotoImage(base.rotate(heading - 90, expand=True))
screen.register_shape(f"rocket{heading}", turtle.Shape("image", frame))
t = turtle.Turtle()
t.penup()
for heading in range(0, 360, 45):
t.goto(0, 0)
t.setheading(heading)
t.forward(200)
t.shape(f"rocket{heading}") # pick the picture that matches the heading
t.stamp()
t.hideturtle()
turtle.done()
Move an image with the arrow keys
Put the pieces together for a tiny game: a background, an image shape and key bindings (more events in the Turtle cheat sheet):
import turtle
screen = turtle.Screen()
screen.setup(620, 470)
screen.bgpic("sky.png")
screen.register_shape("rocket.gif")
rocket = turtle.Turtle(shape="rocket.gif")
rocket.penup()
rocket.speed(0)
def move(dx, dy):
x, y = rocket.position()
rocket.goto(max(-280, min(280, x + dx)), max(-200, min(200, y + dy))) # stay on screen
screen.onkey(lambda: move(0, 20), "Up")
screen.onkey(lambda: move(0, -20), "Down")
screen.onkey(lambda: move(-20, 0), "Left")
screen.onkey(lambda: move(20, 0), "Right")
screen.listen()
turtle.done()
Convert PNG or JPG to GIF (and resize) with Pillow
For Python 3.13 or older, or to make an image smaller, convert it once with Pillow. GIF only supports on/off transparency, so pixels are either kept or made fully transparent:
from PIL import Image
img = Image.open("rocket.png").convert("RGBA")
img = img.resize((64, 64)) # resize while you're at it
# GIF has 1-bit transparency: keep pixels that are mostly opaque
alpha = img.getchannel("A").point(lambda a: 255 if a > 128 else 0)
gif = img.convert("RGB").quantize(colors=255)
gif.paste(255, mask=Image.eval(alpha, lambda a: 255 - a)) # paint the background with palette colour 255
gif.save("rocket_small.gif", transparency=255) # ...and mark that colour as transparent
check = Image.open("rocket_small.gif").convert("RGBA")
print(check.size)
print("corner:", check.getpixel((0, 0))) # alpha 0 = transparent
print("centre:", check.getpixel((32, 32))) # alpha 255 = the rocket
Output:
(64, 64)
corner: (0, 0, 0, 0)
centre: (236, 239, 241, 255)
Common errors
import tkinter as tk
import turtle
screen = turtle.Screen()
for name in ["missing.gif", "photo.jpg"]:
try:
tk.PhotoImage(file=name)
except tk.TclError as err:
print(f"{name}: TclError: {err}")
screen.bye()
Output:
missing.gif: TclError: couldn't open "missing.gif": no such file or directory
photo.jpg: TclError: couldn't recognize data in image file "photo.jpg"
couldn't open ... no such file or directory: the path is wrong. Put the image next to your script and run the script from that folder, or use an absolute path.couldn't recognize data in image file: Tk can’t read that format (JPG, WebP, BMP). Convert it to PNG or GIF with Pillow.Bad arguments for register_shape: a PNG name in Python 3.13 or older. Use thePhotoImagemethod or upgrade to 3.14.- The image doesn’t turn: expected. Use pre-rotated shapes as shown above.
- The image disappears when you create
PhotoImageobjects in your own code: keep a reference to them (registering them as a shape does that for you).
Summary
| Task | Code |
|---|---|
| GIF as the turtle | screen.register_shape("x.gif"), t.shape("x.gif") |
| PNG as the turtle (any version) | register_shape("x", turtle.Shape("image", tk.PhotoImage(file="x.png"))) |
| PNG as the turtle (3.14+) | screen.register_shape("x.png") |
| Background image | screen.bgpic("sky.png") |
| Smaller / bigger | photo.subsample(2) / photo.zoom(2) |
| Rotate with heading | Pre-rotated shapes with Pillow |
| Leave a copy | t.stamp() |
Frequently asked questions
How do I add an image to a Python Turtle?
Register it as a shape with screen.register_shape("image.gif"), then call t.shape("image.gif"). The image then moves wherever the turtle goes.
Can Python Turtle use PNG images?
Yes. Python 3.14+ accepts PNG in register_shape() directly. In older versions, load it with tkinter.PhotoImage(file="x.png") and register turtle.Shape("image", photo).
How do I set a background image in Turtle?
Call turtle.Screen().bgpic("background.png"). The image is centred and not stretched, so size the window to match it.
Why doesn’t my turtle image rotate?
Image shapes never rotate in Turtle. Create rotated copies with Pillow and switch shapes when the heading changes.
Can I use a JPG image in Turtle?
Not directly: Tk can’t read JPG and raises couldn't recognize data in image file. Convert it to PNG or GIF with Pillow first.
More Turtle image tutorials:
Bijay Kumar is a 13-time Microsoft MVP with more than 18 years in software development, and the founder of Python Guides and TSinfo Technologies. He started out building .NET and SharePoint solutions at HP, TCS and KPIT before moving into Python, machine learning and AI, and he also builds web apps with TypeScript and React. He writes the tutorials here himself, and every example is run before publishing so you see the real output. More about Bijay · Microsoft MVP profile · LinkedIn