Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

EktuPy API Reference

Complete reference for all EktuPy classes, methods, and functions.

Table of Contents


Stage Class

The Stage is the canvas where all sprites live and move. The stage has a fixed size of 960x720 pixels.

Constructor

Stage(background_color="#ffffff")
ParameterTypeDefaultDescription
background_colorstr“#ffffff”Background color (CSS color)
# Create a stage with default white background
stage = Stage()

# Create a stage with a custom background color
stage = Stage("#1a1a2e")

Properties

PropertyTypeDescription
widthintStage width (always 960)
heightintStage height (always 720)

Coordinate System

EktuPy uses a Scratch-like coordinate system with the origin at the center:

                    (0, 360) Top
                        |
                        |
(-480, 0) Left -----(0, 0)-----> (480, 0) Right
                        |
                        |
                   (0, -360) Bottom
AxisRangeDescription
X-480 to 480Horizontal position (negative = left, positive = right)
Y-360 to 360Vertical position (negative = down, positive = up)

Direction angles:

  • 0 = Up
  • 90 = Right
  • 180 (or -180) = Down
  • -90 = Left

Methods

set_background(color)

Set the stage background color.

stage.set_background("#87CEEB")  # Sky blue
stage.set_background("red")      # Named color

set_background_image(name_or_path)

Set a background image by asset name or path.

# Using an asset name
stage.set_background_image("sky-and-clouds")

# Using a path
stage.set_background_image("/static/assets/backdrops/jungle.svg")

Available backdrops:

  • jungle - Dense jungle forest scene
  • mountain-sunrise - Mountains with rising sun
  • paddy-field - Sun over rice paddy fields
  • race-track - Racing track scene
  • sky-and-clouds - Blue sky with fluffy clouds

add_sprite(sprite)

Add a sprite to the stage.

cat = Sprite("cat")
stage.add_sprite(cat)

start()

Start the game loop. Usually called automatically.

stop()

Stop the game loop.


Sprite Class

Sprites are the visual objects that move and interact on the stage.

Constructor

Sprite(image_path=None)
ParameterTypeDefaultDescription
image_pathstrNoneAsset name or path to sprite image (SVG, PNG, etc.)

You can create sprites using either an asset name (simpler) or a full path:

# Using asset name (recommended)
snake = Sprite("snake")    # Looks up "snake" in the asset map
paddle = Sprite("paddle")  # Looks up "paddle" in the asset map

# Using full path (also works)
snake = Sprite("/static/assets/sprites/snake.svg")

Asset names are easier to use and will show a warning in the editor if the name doesn’t exist. See Asset Map for more details.

Sprite Properties

All properties can be read and written:

PropertyTypeDescription
xfloatX position (0 = center)
yfloatY position (0 = center)
directionfloatDirection in degrees (0=up, 90=right)
sizefloatSize as percentage (100 = normal)
costume_numberintCurrent costume number (read-only)
costume_namestrCurrent costume name (read-only)
is_cloneboolTrue if this sprite is a clone (read-only)
sprite.x = 100
sprite.y = -50
sprite.direction = 90  # Face right
sprite.size = 150      # 150% size
print(sprite.x)        # Read position

Motion Methods

move(steps)

Move in the current direction.

sprite.move(10)  # Move 10 steps forward

turn_right(degrees) / turn_left(degrees)

Rotate the sprite.

sprite.turn_right(90)  # Turn 90 degrees clockwise
sprite.turn_left(45)   # Turn 45 degrees counter-clockwise

go_to(x, y)

Instantly move to a position.

sprite.go_to(0, 0)      # Go to center
sprite.go_to(-100, 50)  # Go to specific position

set_x(x) / set_y(y)

Set just the x or y position.

sprite.set_x(100)
sprite.set_y(-50)

change_x(amount) / change_y(amount)

Change position by an amount.

sprite.change_x(10)   # Move 10 pixels right
sprite.change_y(-5)   # Move 5 pixels down

go_to_random_position()

Move to a random position on the stage.

sprite.go_to_random_position()

go_to_mouse()

Move to the mouse pointer position.

sprite.go_to_mouse()

go_to_sprite(other)

Move to another sprite’s position.

cat.go_to_sprite(dog)

glide_to(x, y, seconds)

Smoothly glide to a position over time.

sprite.glide_to(100, 50, 2)  # Glide over 2 seconds

glide_to_random_position(seconds)

Glide to a random position.

sprite.glide_to_random_position(1.5)

glide_to_mouse(seconds)

Glide to the mouse pointer.

sprite.glide_to_mouse(1)

glide_to_sprite(other, seconds)

Glide to another sprite.

cat.glide_to_sprite(dog, 2)

point_towards(x, y)

Point toward a position.

sprite.point_towards(100, 100)

point_towards_mouse()

Point toward the mouse pointer.

sprite.point_towards_mouse()

point_towards_sprite(other)

Point toward another sprite.

cat.point_towards_sprite(dog)

set_rotation_style(style)

Set how the sprite rotates visually.

StyleDescription
"all around"Rotate freely in all directions
"left-right"Only flip horizontally (default)
"don't rotate"Never rotate visually
sprite.set_rotation_style("all around")

bounce_if_on_edge()

If touching edge, bounce (reverse direction).

@on_forever
def move():
    sprite.move(5)
    sprite.bounce_if_on_edge()

Looks Methods

say(text, seconds=None)

Show a speech bubble.

sprite.say("Hello!")           # Show until changed
sprite.say("Hi!", 2)           # Show for 2 seconds
sprite.say("")                 # Clear bubble

think(text, seconds=None)

Show a thought bubble (cloud shape).

sprite.think("Hmm...")
sprite.think("I wonder...", 3)

show() / hide()

Show or hide the sprite.

sprite.hide()
wait(1)
sprite.show()

change_size(amount)

Change size by amount.

sprite.change_size(10)   # Grow by 10%
sprite.change_size(-20)  # Shrink by 20%

set_ghost_effect(value)

Set transparency (0=visible, 100=invisible).

sprite.set_ghost_effect(50)  # 50% transparent

change_ghost_effect(amount)

Change transparency by amount.

sprite.change_ghost_effect(10)  # More transparent

clear_graphic_effects()

Reset all visual effects.

sprite.clear_graphic_effects()

switch_costume(name_or_index)

Switch to a different costume.

sprite.switch_costume(0)       # By index
sprite.switch_costume("walk1") # By name

next_costume()

Switch to the next costume (wraps around).

sprite.next_costume()

add_costume(image_path, name)

Add a new costume from an image.

sprite.add_costume("/static/assets/walk2.png", "walk2")

Layer Methods

sprite.go_to_front_layer()    # Move to front
sprite.go_to_back_layer()     # Move to back
sprite.go_forward_layers(2)   # Move forward 2 layers
sprite.go_backward_layers(1)  # Move backward 1 layer

Pen Methods

Draw lines on the stage as the sprite moves.

pen_down() / pen_up()

Start or stop drawing.

sprite.pen_down()
sprite.move(100)   # Draws a line
sprite.pen_up()
sprite.move(50)    # No line drawn

set_pen_color(color)

Set the pen color.

sprite.set_pen_color("red")
sprite.set_pen_color("#FF5500")
sprite.set_pen_color("rgb(255, 100, 0)")

set_pen_size(size)

Set the pen width in pixels.

sprite.set_pen_size(5)

stamp()

Stamp the sprite’s image onto the stage.

sprite.stamp()

clear()

Erase all pen drawings.

sprite.clear()

Sensing Methods

touching(other)

Check if touching another sprite.

if cat.touching(dog):
    cat.say("Ouch!")

touching_edge()

Check if touching the stage edge.

if sprite.touching_edge():
    sprite.bounce_if_on_edge()

touching_mouse()

Check if touching the mouse pointer.

if sprite.touching_mouse():
    sprite.say("You're on me!")

distance_to(other)

Get distance to another sprite.

dist = cat.distance_to(dog)
if dist < 50:
    cat.say("Too close!")

distance_to_mouse()

Get distance to the mouse pointer.

dist = sprite.distance_to_mouse()
if dist < 100:
    sprite.say("Getting close!")

Clone Methods

Create copies of sprites at runtime.

clone()

Create a clone of this sprite.

new_clone = sprite.clone()

on_clone_start(callback)

Register a function to run when this sprite is cloned.

def on_cloned():
    # 'sprite' here refers to the new clone
    sprite.go_to_random_position()

sprite.on_clone_start(on_cloned)

delete_clone()

Delete this clone (only works on clones).

if sprite.is_clone:
    sprite.delete_clone()

Per-Sprite Event Decorators

Each sprite can have its own event handlers, just like Scratch.

@sprite.on_start

Runs when the program starts (for this sprite only).

@cat.on_start
def cat_setup():
    cat.go_to(-100, 0)
    cat.say("I'm the cat!")

@sprite.on_forever

Runs every frame (for this sprite only).

@cat.on_forever
def cat_loop():
    cat.turn_right(1)

@sprite.on_key_press(key)

Runs when a key is pressed (for this sprite only).

@cat.on_key_press("space")
def cat_jump():
    cat.change_y(50)

@sprite.on_message(message)

Runs when a broadcast is received (for this sprite only).

@cat.on_message("game_over")
def cat_dies():
    cat.say("Oh no!")
    cat.hide()

sprite.on_clicked(callback)

Runs when this sprite is clicked.

def on_cat_click():
    cat.say("You clicked me!")

cat.on_clicked(on_cat_click)

Global Event Decorators

These decorators register handlers that aren’t tied to a specific sprite.

@on_start

Runs once when the program starts.

@on_start
def setup():
    print("Game started!")

@on_forever

Runs every frame (approximately 60 times per second).

@on_forever
def game_loop():
    # Update game state
    pass

@on_key_press(key)

Runs when a specific key is pressed.

Key names: "a" to "z", "0" to "9", "space", "up arrow", "down arrow", "left arrow", "right arrow", "any" (any key)

@on_key_press("space")
def jump():
    player.change_y(50)

@on_key_press("any")
def any_key(key):
    print(f"Pressed: {key}")

@on_mouse_click

Runs when the mouse is clicked on the stage.

@on_mouse_click
def click_handler(x, y):
    print(f"Clicked at ({x}, {y})")
    sprite.go_to(x, y)

@on_message(message)

Runs when a broadcast message is received.

@on_message("level_complete")
def next_level():
    print("Loading next level...")

Control Functions

wait(seconds)

Pause execution without freezing the program.

@on_start
def setup():
    sprite.say("Hello!")
    wait(2)
    sprite.say("Goodbye!")

wait_until(condition)

Wait until a condition becomes true.

@on_start
def setup():
    sprite.say("Waiting for you to press space...")
    wait_until(lambda: key_pressed("space"))
    sprite.say("Thanks!")

stop() / stop_all()

Stop the game/program.

if game_over:
    stop()

Input Functions

key_pressed(key)

Check if a key is currently held down.

if key_pressed("right arrow"):
    sprite.change_x(5)

mouse_x() / mouse_y()

Get the mouse position on the stage.

x = mouse_x()
y = mouse_y()
sprite.go_to(x, y)

mouse_down()

Check if the mouse button is pressed.

if mouse_down():
    sprite.stamp()

Sensing Functions

Global functions for sensing time, date, and environment.

Timer

timer()

Get the time in seconds since the program started (or since the last reset).

t = timer()
if t > 30:
    sprite.say("30 seconds passed!")

reset_timer()

Reset the timer back to 0.

reset_timer()
# timer() now returns 0

Date and Time

current_year()

Get the current year.

year = current_year()  # e.g., 2025

current_month()

Get the current month (1-12).

month = current_month()  # 1=January, 12=December

current_date()

Get the current day of the month (1-31).

day = current_date()

current_day_of_week()

Get the current day of the week (1-7, where 1=Sunday).

day_of_week = current_day_of_week()
if day_of_week == 1:
    sprite.say("It's Sunday!")

current_hour()

Get the current hour (0-23).

hour = current_hour()
if hour < 12:
    sprite.say("Good morning!")

current_minute()

Get the current minute (0-59).

minute = current_minute()

current_second()

Get the current second (0-59).

second = current_second()

days_since_2000()

Get the number of days since January 1, 2000 (including fractional days).

days = days_since_2000()
sprite.say(f"{int(days)} days since Y2K!")

Ask and Answer

Get user input through an on-screen text box, just like Scratch’s “ask [] and wait” block.

ask(prompt)

Show a text input box at the bottom of the stage with a prompt, wait for the user to type and submit an answer, then return the answer as a string.

@on_start
def setup():
    name = ask("What is your name?")
    sprite.say(f"Hello, {name}!")

The input box appears at the bottom of the stage with:

  • The prompt text displayed above the input field
  • A text input where the user can type
  • A submit button (or press Enter to submit)

The program pauses until the user submits their answer.

answer()

Get the most recent answer from ask(). This is useful when you need to access the last answer later in your code.

@on_start
def setup():
    ask("What is your name?")
    name = answer()  # Get the answer that was just entered

    ask("How old are you?")
    age = answer()   # Get this new answer

    sprite.say(f"Hi {name}, you are {age} years old!")

Example: Multiple questions

@on_start
def quiz():
    score = 0

    ask("What is 5 + 3?")
    if answer() == "8":
        score += 1
        sprite.say("Correct!")
    else:
        sprite.say("Wrong! It's 8")
    wait(1)

    ask("What color is the sky?")
    if answer().lower() == "blue":
        score += 1
        sprite.say("Correct!")
    else:
        sprite.say("Wrong!")
    wait(1)

    sprite.say(f"You scored {score}/2!")

Messaging

broadcast(message, data=None)

Send a message to all listeners.

broadcast("game_over")
broadcast("score_changed", {"score": 100})

Receive with @on_message or @sprite.on_message:

@on_message("score_changed")
def update_score(data):
    print(f"New score: {data['score']}")

broadcast_and_wait(message, data=None)

Send a message and wait for all handlers to complete before continuing.

# Send message and wait for all handlers to finish
broadcast_and_wait("prepare_level")
# This line runs after ALL @on_message("prepare_level") handlers complete
print("Level ready!")

Useful for synchronization:

@cat.on_key_press("space")
def start_animation():
    cat.say("Ready...")
    broadcast_and_wait("all_sprites_ready")  # Wait for everyone
    cat.say("Go!")  # Only runs after all handlers finish

@dog.on_message("all_sprites_ready")
def dog_prepare():
    dog.go_to(0, 0)
    wait(1)  # This delay is waited for by broadcast_and_wait

Console Output

Use standard Python print() for debugging:

print("Hello!")           # Appears in white
print(f"Position: {x}")   # String formatting works

import sys
sys.stderr.write("Error!\n")  # Appears in red

Sound Functions

Play sounds and music in your projects, just like Scratch’s Sound blocks.

Loading and Playing Sounds

load_sound(name, src=None)

Load a sound file and give it a name. Can be called two ways:

Using asset name (recommended):

# Single argument - name is looked up in the asset map
load_sound("sfx_sound_neutral1")  # Loads from asset map

Using explicit path:

# Two arguments - provide alias and path
load_sound("hit", "/static/assets/sounds/hit.mp3")
load_sound("pop", "/static/assets/sounds/pop.wav")
ParameterTypeDescription
namestrAsset name (if single arg) or alias to reference this sound
srcstrOptional. Path or asset name for the sound file (MP3, WAV, OGG)

Asset names are easier to use and will show a warning in the editor if the name doesn’t exist. See Asset Map for more details.

start_sound(name)

Start playing a sound (like Scratch’s “play sound” block). The sound plays in the background while your code continues running.

start_sound("meow")  # Plays and continues immediately
sprite.say("Meow!")  # Runs right away

play_sound_until_done(name)

Play a sound and wait until it finishes (like Scratch’s “play sound until done” block).

@on_start
def setup():
    sprite.say("Listen...")
    play_sound_until_done("meow")  # Waits for sound to finish
    sprite.say("Done!")            # Only runs after sound ends

stop_all_sounds()

Stop all currently playing sounds.

stop_all_sounds()

Volume Control

Control the global volume of all sounds (0-100, like Scratch).

set_volume(value)

Set the volume (0-100).

set_volume(50)   # 50% volume
set_volume(100)  # Full volume
set_volume(0)    # Muted

change_volume(amount)

Change the volume by an amount.

change_volume(10)   # Volume up by 10
change_volume(-20)  # Volume down by 20

get_volume()

Get the current volume level (0-100).

vol = get_volume()
sprite.say(f"Volume: {vol}%")

Sound Effects

Apply effects to sounds like pitch and stereo panning.

set_pitch_effect(value)

Set the pitch effect (like Scratch). Range is approximately -360 to 360.

  • 0 = normal pitch
  • Positive values = higher pitch (faster playback)
  • Negative values = lower pitch (slower playback)
set_pitch_effect(60)   # Higher pitch
set_pitch_effect(-60)  # Lower pitch
set_pitch_effect(0)    # Normal

change_pitch_effect(amount)

Change the pitch effect by an amount.

change_pitch_effect(10)   # Increase pitch
change_pitch_effect(-20)  # Decrease pitch

set_pan_effect(value)

Set the stereo pan (like Scratch’s “set pan left/right” effect).

  • -100 = full left speaker
  • 0 = center (both speakers)
  • 100 = full right speaker
set_pan_effect(-100)  # Full left
set_pan_effect(0)     # Center
set_pan_effect(100)   # Full right

change_pan_effect(amount)

Change the pan effect by an amount.

change_pan_effect(50)  # Pan more to the right

clear_sound_effects()

Reset all sound effects (pitch and pan) to normal.

clear_sound_effects()  # Reset to normal pitch and center pan

Example: Playing Sounds with Effects

stage = Stage()
cat = Sprite("cat")
stage.add_sprite(cat)

# Load sounds using asset names
load_sound("sfx_coin_single1")
load_sound("sfx_sounds_fanfare1")

@on_start
def setup():
    set_volume(70)  # 70% volume
    start_sound("sfx_sounds_fanfare1")  # Play a fanfare

@on_key_press("space")
def coin():
    cat.say("Coin!")
    start_sound("sfx_coin_single1")

@on_key_press("up arrow")
def volume_up():
    change_volume(10)
    cat.say(f"Volume: {get_volume()}%")

@on_key_press("down arrow")
def volume_down():
    change_volume(-10)
    cat.say(f"Volume: {get_volume()}%")

@on_key_press("s")
def stop_sounds():
    stop_all_sounds()
    cat.say("Stopped!")

Asset Map

EktuPy includes an asset map that makes it easy to use sprites and sounds by name instead of remembering full paths.

How It Works

The asset map is a JSON file (asset_map.json) that maps asset names to their full paths. This is generated from the static/assets/sprites and static/assets/sounds directories.

When you use an asset name (without slashes), EktuPy automatically looks it up in the asset map and uses the full path.

Using Asset Names

Sprites:

# Instead of using the full path:
snake = Sprite("/static/assets/sprites/snake.svg")

# You can use the asset name:
snake = Sprite("snake")

Sounds:

# Instead of:
load_sound("hit", "/static/assets/sounds/essential_retro/General_Sounds/Neutral_Sounds/sfx_sound_neutral1.wav")

# You can use:
load_sound("sfx_sound_neutral1")  # Single argument - looked up by name
# or
load_sound("hit", "sfx_sound_neutral1")  # Named alias with asset name

Benefits

  1. Shorter code: No need to type long paths
  2. Editor validation: The editor shows a warning if you use a name that doesn’t exist
  3. Easier to remember: Use descriptive names instead of file paths

Helper Functions

get_available_sprites()

Get a list of all available sprite asset names.

sprites = get_available_sprites()
print(sprites)  # ['snake', 'paddle', 'ball-solid', ...]

get_available_sounds()

Get a list of all available sound asset names.

sounds = get_available_sounds()
print(sounds)  # ['sfx_sound_neutral1', 'sfx_sound_neutral2', ...]

get_available_backdrops()

Get a list of all available backdrop asset names.

backdrops = get_available_backdrops()
print(backdrops)  # ['jungle', 'mountain-sunrise', 'paddy-field', 'race-track', 'sky-and-clouds']

Naming Conflicts

If two files have the same basename but different extensions (e.g., ball.svg and ball.png), they are differentiated by adding the extension to the name:

  • ball-svg for ball.svg
  • ball-png for ball.png