p5 for Processing users

p5 API borrows many core ideas from the Processing so most of the API looks similar. This document lists the major differences between Processing and the p5 API.

In addition to skimming through this document, you should also check the API reference for more details and take a look at complete working examples on the examples repository.

Naming conventions

  • Most function names are now in lowercase_separated_by_underscores as opposed to the lowerCamelCase in Processing. So, if a method was called bezierPoint in Processing it will be called bezier_point in p5.
  • Mathematical constants like \(\pi\) are still in UPPPERCASE_SEPARATED_BY_UNDERSCORES. Note that width, height, mouse_x, etc are not treated as constants.
  • The “P” prefix has been dropped from the class names. So, PVector becomes Vector, PImage becomes Image, etc.

We’ve also renamed a couple of things:

  • Processing’s map() method is now called remap() to avoid a namespace conflict with Python’s inbuilt map() function.

  • All get*() and set*() methods for objects have been removed and attributes can be set/read by directly accessing the objects.

    For instance, if we have a vector vec in Processing, we would use

    /* read the magnitude of the vector */
    float m = vec.mag()
    
    /* set the magnitude of the vector */
    vec.setMag(newMagnitude)
    

    In p5, we can just use:

    # read the magnitude of the vector
    m = vec.magnitude
    
    # set the magnitude of the vector
    vec.magnitude = new_magnitude
    
  • Processing’s random() method is now called random_uniform() to prevent confusion (and nasty errors!) while using Python’s random module.

Running Sketches

  • p5 doesn’t come with an IDE and p5 scripts are run as any other Python scripts/programs. You are free to use any text editor or Python IDE to run your programs.

  • Sketches must call the run() function to actually show the sketches. Sketches without a call to run() will not work. So, a Processing sketch:

    void setup() {
        /* things to do in setup */
    }
    
    void draw() {
        /* things to do in the draw loop */
    }
    
    void mousePressed() {
        /* things to do when the mouse is pressed */
    }
    

    would look like this in p5:

    from p5 import *
    
    def setup():
        # Things to do in setup
    
    def draw():
        # Things to do in the draw loop
    
    def mouse_pressed():
        # Things to do when the mouse is pressed.
    
    run() # This is essential!
    
  • Drawing commands only work inside functions.

  • If you want to control the frame rate of the you need to pass in frame_rate asnan optional argument when you run your sketch.

    from p5 import *
    
    def setup():
        # setup code
    
    def draw():
        # draw code
    
    # run the sketch at 15 frames per second.
    run(frame_rate=15)
    
  • Processing’s frameRate() method is called set_frame_rate() in p5. To get the current frame rate in the sketch, use the frame_rate global variable.

Shapes, etc

  • One of the major differences between the Processing and the p5 API is the way coördinate points are handled. With the exception of the point() functions, all drawing functions that allow the user to pass in coordinates use tuples.

    Hence, to draw a line from \((100, 100)\) to \((180, 180)\), we would use:

    start_point = (100, 100)
    end_point = (180, 180)
    
    line(start_point, end_point)
    

    To draw a rectangle at \((90, 90)\) with width \(100\) and height \(45\), once would use:

    location = (90, 90)
    rect(location, 100, 45)
    

    Technically, any object that supports indexing (lists, p5 Vectors) could be used as the coordinates to the draw calls. Hence, the following code snippet is perfectly valid:

    start_point = Vector(306, 72)
    control_point_1 = Vector(36, 36)
    control_point_2 = Vector(324, 324)
    end_point = Vector(54, 288)
    
    bezier(start_point, control_point_1, control_point_2, end_point)
    
  • Functions like bezier_point, bezier_tangent, curve_point, curve_tangent, etc also need the coordinates as iterables. Further, they also return special objects that have \(x, y, z\) coordinates.

    start = Vector(306, 72)
    control_1 = Vector(36, 36)
    control_2 = Vector(324, 324)
    end = Vector(54, 288)
    
    bp = bezier_point(start, control_1, control, end, 0.5)
    
    # The x coordinate of the bezier point:
    print(bp.x)
    
    # The y coordinate of the bezier point:
    print(bp.y)
    
  • Unlike Processing, p5 doesn’t have special global constants for “modes”. Functions like ellipse_mode() and rect_mode() take strings (in all caps) as inputs. The following are valid function calls:

    center = (width / 2, height / 2)
    
    rect_mode('RADIUS')
    square(center, 50)
    
    ellipse_mode('CENTER')
    circle(center, 100)
    
  • Processing’s pushMatrix() and popMatrix() have been replaced by a single push_matrix() context manager that cleans up after itself. So, the following Procecssing code:

    pushMatrix()
    
    translate(width/2, height/2)
    point(0, 0)
    
    popMatrix()
    

    Becomes:

    with push_matrix():
        translate(width / 2, height / 2)
        point(0, 0)
    
  • Like push_matrix(), push_style() is a context manager and can be used with the with statement.

Event System

  • Processing’s mousePressed global boolean has been renamed to mouse_is_pressed to avoid namespace conflicts with the user defined mouse_pressed function.

  • To check which mouse button was pressed, compare the mouse_button global variable to one of the strings 'LEFT', 'RIGHT', 'CENTER', 'MIDDLE'

  • The keyCode variable has been removed. And Processing’s special “coded” keys can be compared just like other alpha numeric keys.

    def key_pressed(event):
        if event.key == 'A':
            # code to run when the <A> key is presesed.
    
        elif event.key == 'UP':
            # code to run when the <UP> key is presesed.
    
        elif event.key == 'SPACE':
            # code to run when the <SPACE> key is presesed.
    
        # ...etc
    

Math

  • Vector addition, subtraction, and equality testing are done using the usual mathematical operators and scalar multiplication is done using the usual * operator. The following are valid vector operations:

    # add two vectors `position` and `velocity`
    position = position + velocity
    
    # subtract the vector `offset` from `position`
    actual_location = position - offset
    
    # scale a vector by a factor of two
    scaled_vector = 2 * vec_1
    
    # check if two vectors `player_location`
    # and `mouse_location` are equal
    if (player_location == mouse_location):
        end_game()
    
    # ...etc.
    
  • The mean and standard deviation value can be specified while calling random_gaussian()

  • The distance function takes in two tuples as inputs. So, the following Processing call:

    d = dist(x1, y1, z1, x2, y2, z2)
    

    would become:

    point_1 = (x1, y1, z1)
    point_2 = (x2, y2, z2)
    
    d = dist(point_1, point_2)
    
  • The remap() also takes tuples for ranges. The Processing call:

    n = map(mouseX, 0, width, 0, 10)
    

    becomes:

    source = (0, width)
    target = (0, 10)
    
    n = remap(mouse_x, source, target)
    

New Features

  • The title() method can be used to set the title for the sketch window.

  • circle() and square() functions draw circles and squares.

  • Colors can be converted to their proper grayscale values.

    # if we have some color value...
    our_color = Color(255, 127, 0)
    
    # ...we can get its gray scale value
    # using its `gray` attribute
    gray_value = our_color.gray