p5js with brython


p5js is basically processing but uses javascript and runs in the browser. Brython is basically python but runs in the browser. So if you want to write processing stuff but for some reason you want to do it in python, you can do exactly that.

Why p5js?

It runs on almost ("almost" as in "I really haven't checked all of them") any modern browser (the basic 2d "drawing" part of it at least; the 3d part apparently uses webgl so I'm not so sure). You can easily share with your friends or make into a part of your online portofolio. Yes, processing.py does exist, but it's definitely way better if your works can be accessed without any java/python environment setup clutter. I switched to p5js years ago even though I'll have to admit that java is (in some ways) a much better language than javascript.

(In case you don't know, processing is not a programming language; it's basically some kind of a subset of java but between the "real" java and a DSL.)

First try

Now, if you directly do it like this:

<!-- index.html  -->
<body onload="brython()">
    <main></main>
    <script src="../../p5.min.js"></script>
    <script type="text/python" src="script.bry"></script>
</body>
# script.bry
def setup():
    createCanvas(WIDTH, HEIGHT)

def draw():
    background(0)
    stroke(255)

it will not work, because:

  • it seems that brython() only works if you hook it to <body> 's onload, and if you do that brython will take over the job of loading other scripts, so inside such a <body> the original <script> tag won't work.
  • p5js expects setup and draw to be defined in toplevel, and brython does not export definitions to toplevel by default.
  • brython does not automatically import javascript values in the toplevel.

The basic solution

The seemingly required part

# load the p5js library
load("p5.min.js")

Brython uses ajax to load scripts, which means you'll have to set up an http server to see if your script works as you have intended. If you have installed cpython you're fine, it comes with one: use python -m http.server when under the directory containing the html wrapper file and your script.

Export brython definition to toplevel

You can import browser.window and do it like this:

from browser import window
def my_func():
    ...
window.my_func = my_func

Accessing toplevel objects from brython

You have to access them using browser.window:

# ....
from browser import window
def draw():
    window.background(0)
    window.stroke(255)
# ... and et cetera

I tried fiddling with getattr and locals:

# something like this...
def global_import(name_list):
    for name in name_list:
        locals()[name] = getattr(window, name)

so that I can:

global_import([
    'background',
    'stroke',
])
def draw():
    background(0)
    stroke(255)

but I failed because it doesn't work. getattr is strange stuff.

Cleaning up

You can have this little decorator:

def export(f):
    window[f.__name__] = f
    return f

so you can add @export before any function instead of adding window.blah = blah:

# ...
@export
def setup():
    window.createCanvas(WIDTH, HEIGHT)

@export
def draw():
    window.background(127)
    window.stroke(255)
# ...

Conclusion

This is really kinda stupid. When I first started writing this I thought it'll be a (relatively) seamless experience, turns out it really isn't. It's better to just use javascript, using python like this is too much clutter.


Back

© Sebastian Higgins 2020 All Rights Reserved.
Content on this page is distributed under the CC BY-NC-SA 4.0 license unless further specified.
Last update: 2020.11.23