This section contains some basic concepts of botafar library.

Most likely you should skim through this before going through Arduino or Raspberry Pi tutorials.

Project status#

Botafar is still under development. The current aim is to make creating remote controllable bots a simple, enjoyable and bug free experience. The communication about project happens on GitHub discussions.

If you got stuck or need help with botafar library or website, please write to Q&A.

If you experience bugs on botafar library or website, please write about it to Bug Tracking, even if you are not sure if its a bug or not. Alternatively you can raise an issue on purely library related matters (or even create a pull request if you think you have the bug figured out).

If you want to tell about your creations, get involved or anything else, write about it to General. There is also a separate category for new Ideas.



All botafar bots require a one call after the callbacks have been registered.


If you have prints you want to show to the user

print("You won!")

You can print it directly to the livestream by using botafar.print

botafar.print("You won!")

If you want to show all prints from a file to a user, you can override the default print

import botafar
from botafar import print

print("You won!")


botafar.time() gives the current time in seconds, starting when remote contorlling starts. If no one is remote controlling, botafar.time() returns -1.


botafar.stop() ends the current player’s remote controlling programmatically. Convenient if your bot has some win condition.


botafar.exit() ends programmatically. Convenient if your bot has for example some unrecoverable hardware failure that can be detected programmatically.


Controls bind one or more keyboard keys as parameters. When those keys are pressed on a keyboard or touch screen, they change the contorl’s state which will trigger callbacks.

Available keyboard keys are A-Z, UP, LEFT, DOWN, RIGHT and SPACE


The simplest control is Button binds one key. It has on_press and on_release states.

b = botafar.Button("SPACE")

def press():

def press():


Slider bind two keys and it has two variations, horizontal and vertical. These variations are automatically detected based on the callback functions registered.

Horizontal slider has three states: on_left, on_right and on_center.

Vertical slider states are: on_up, on_down and on_center.

s = botafar.Slider("J","L")

def center():

# A horizontal Slider:
def left():

def right():

# If you want a vertical Slider, use @s.on_up/@s.on_down instead


Joystick bind four keys and it has two variations, without diagonals and with diagonals. In keyboard it means having callbacks for certain combinations of keys, such as pressing W and A at the same time. On mobile touch screen this means the area between W and A keys.

These variations are automatically detected based on the callback functions registered.

Joystick without diagonals has 5 callbacks: on_up, on_left, on_down, on_right and on_center.

Joystick with diagonals has 4 additional callbacks: on_up_left, on_down_left, on_down_right, on_up_right.

j = botafar.Joystick("W","A","S","D")

def center():

def up():

def left():

def down():

def right():

# Diagonals:

def up_left():

def down_left():

def down_right():

def up_right():

The example above shows that having this many control states results in quite many rows callback functions. Later we see how this can be simplified in many cases with on_any or by stacking callbacks.

Common control features#


Event represents a single state change for controls. It allows you write more complex bot logic as well as more readable code in many cases (examples come later)

It can be accessed by adding a parameter called event as the first parameter to the callback methods

b = botafar.Button("SPACE")

def press(event):

This prints:

Event(name='on_press', is_active=True, sender='owner', time=2.47)

(on class method callbacks, the event parameter comes after “self”)


In addition to the regular state callbacks, all controls have a special callback on_any that triggers on all state changes, and requires an event parameter. In many cases this allows making more readable code.

If you do not register other callbacks, you might need to specify orientation=, “vertical” or “horizontal”, parameter for Slider or diagonals=, True or False, parameter for Joystick to choose the type of control.

    "on_center":     ( 0.0, 0.0),
    "on_up":         ( 1.0, 1.0),
    "on_up_left":    ( 0.5, 1.0),
    "on_left":       ( 0.0, 1.0),
    "on_down_left":  (-0.5,-1.0),
    "on_down":       (-1.0,-1.0),
    "on_down_right": (-1.0,-0.5),
    "on_right":      ( 1.0, 0.0),
    "on_up_right":   ( 1.0, 0.5),

j = botafar.Joystick("W","A","S","D", diagonals=True)

def any(event):
    left_value, right_value = MOTOR_VALUES[]
    # Do something with the values


If you want to have multiple keys triggering the same callbacks, you can use alt parameter.

All controls have a alt parameter. For Button, the type is string, for Slider and Joystick, it is a list of strings.

b = botafar.Button("SPACE", alt="C")
s = botafar.Slider("J","L", alt=["U","O"])
j = botafar.Joystick("W","A","S","D", alt=["UP","LEFT","DOWN","RIGHT"])

Callback features#

So far we’ve seen only individual callbacks registered to functions.

import botafar

b = botafar.Button("A")

def press():

But they can be used in many more situations.

Class method callbacks#

You can use class methods in addition to functions as callbacks! This can make your code more readable. But remember to create at least one class instance.

import botafar

b = botafar.Button("A")

class Bot:
    def __init__(self, name): = name

    def press(self):
        botafar.print( + " pressed")

bot = Bot("Bob") # Important

Async callbacks#

botafar supports Python’s asyncio, if you need it.

async def press():

Multiple callbacks#

You can have multiple callbacks tied to the same state change.

def press():
    botafar.print("press 1")

def press():
    botafar.print("press 2")

Stacked callbacks#

You can “stack” callbacks from multiple controls to the same function.

b = botafar.Button("A")
s = botafar.Slider("I","K")

def press_or_up_or_down():
    botafar.print("press or up or down")

Callbacks without decorator#

Instead of creating redundant functions for wrapping other functions

import botafar
from some_package import jump # Example

b = botafar.Button("A")

def press():

you can also register them directly

import botafar
from some_package import jump # Example

b = botafar.Button("A")


Bot lifecycle#

Supported callbacks that are related to bot state


on_prepare allows running a callback before each remote control. This callback is waited to finish before a new player gets controls.

def lifecycle_prepare():


on_start runs when remote controlling starts.

def lifecycle_start():


on_stop runs when remote controlling stops.

def lifecycle_stop():


There are also on_init and on_exit callbacks available, the first runs once at the beginning and the latter run once before Python program exits. Unless you need some specific callback features such as async callbacks, you should probably do things before and after

# Initialize bot here before run()
# Do bot cleanup here after run()

Bot timings#

Supported callbacks that are related to time


on_time allows registering events on specific times. Time counting starts when remote controlling is started, and the callbacks are skipped if the controlling stops before they trigger.

def after_3_seconds():
    botafar.print("3 seconds passed")

You can also pass multiple times to the same on_time function, and get the currently triggered time from an optional ‘time’ parameter that gets filled automatically

@botafar.on_time(5, 8)
def after_5_and_8_seconds(time):
    botafar.print(f"{time} seconds passed")


on_repeat allows running callback continuously during remote controlling. This can be good for example for polling a sensor value.

def once_second():