Python and Canvas2D with Brython

Posted on May 24, 2014

If you like Python and making games, graphics, or demos you might have reached for pygame, pyglet, pyOpenGL, or kivy in the past. The browser has been a far-off land for Python. This is a shame, I think, because graphics programming in the browser is a superior experience for experimentation: zero-installation and no drivers to mess with.

I’ve been thinking about how we could make game development in Python more accessible for everyone. The browser is a nice target because it handles the drivers and setup. The only question was how we could run Python code there.

There are options but one I had heard about and decided to spend an afternoon with recently was Brython. Brython is a Python interpreter written in Javascript. Instead of compiling a subset of the Python language directly into Javascript it implements a full Python interpreter and runs your Python code.

I was curious if this would affect game development and runtime. Python is much easier, in my opinion, to work in and teach than Javascript. If it’s not going to destroy the game play experience for the user then perhaps trading off a little run-time speed and startup for a faster, easier development process is worth it for small projects. Prototypes and ideas are definitely small enough to warrant such trade offs.

The Project

Our goal is a simple demonstration you can see below.

Box demo

We want to animate a box moving on a sinusoidal wave along the y-axis and trailing the mouse on the x-axis while the mouse is within the canvas bounds.

The Setup

The first step is to download and unzip the Brython archive in your project directory. You don’t even need Python installed on your system. From here on out, brython.js is your Python.

Add a test.html file to the Brython distribution and add the following to it:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
    <html>
      <head>
        <meta charset="UTF-8">
        <script src="brython.js"></script>
      </head>
      <body onload="brython(1)">
        <script type="text/python" src="test.py"></script>
        <div id="game" style="width: 800; height: 500; margin: 0 auto;">
          <canvas id="stage" width="800" height="500" style="width: 100%"></canvas>
        </div>
      </body>
    </html>

That’s all there is to it. The important part to note is that we call the brython() function in the document body’s onload attribute. Once you call this function from your page, Brython can run Python scripts… as we do on the next line.

So go ahead and create test.py next to your test.html and move on to the next step.

The Code

Brython, fortunately, includes an API for interacting with the browser and interoperating with Javascript. For our purposes in this tutorial we’re just going to focus on the browser API.

Imports

Here are the imports you should put at the top of test.py:

1
2
3
4
5
    from browser import doc
    from browser.timer import request_animation_frame as raf

    import math
    import time

There are a some things here to notice. First being the browser package. It includes modules for interacting with most (or perhaps all, I haven’t checked thoroughly) of the browser APIs. The doc module gives us access to the DOM. We use requestAnimationFrame from the timer module to schedule our animation loop in the browser. And at the end we have a couple of… Python standard library imports.

A note on the standard library in Brython: it’s there but the issue is thorny. Brython ships with a good deal of the standard library. You can import all of the usual suspects. However the import machinery on the web uses ajax callbacks since the Javascript runtime doesn’t have direct access to the filesystem. This can get really slow if you try to import a module that imports other modules such as random.

Brython provides a bundled single-file standard lib that you can include in your HTML file directly but it requires asking the user to download around 2MB of code first.

It might be possible to compile your own bundled standard library to slim down that 2MB of code but that is out of the scope of the discussion here. Read the Brython documentation on the import mechanism for more information.

Setting up the Context

This tutorial is going to be using the 2D-context of the canvas element. With Brython this is a simple matter of adding a couple lines of code:

1
2
    canvas = doc['stage']
    ctx = canvas.getContext('2d')

We now have a JSObject instance that wraps the Javascript CanvasRenderingContext2d object. This interopability API allows us to call methods on Javascript objects as well as read and write their attributes.

The Demo

This code is purely for demonstration purposes and is in no way meant to be illustrative of best practices or serve as a guide. Consider yourself warned. However it suits our purpose for exploring the Brython Javascript interop API and getting our box on the screen. Add the following code to test.py:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
    ticks = 0
    x = 0.0

    def mouse_moved(ev):
        global x
        new_x = ev.clientX - (canvas.width / 2) + 25
        if new_x > 50 and new_x < canvas.width - 100:
            x = new_x


    canvas.bind("mousemove", mouse_moved)


    def clear():
        ctx.save()

        ctx.setTransform(1, 0, 0, 1, 0, 0)
        ctx.clearRect(0, 0, canvas.width, canvas.height)

        ctx.restore()


    def draw():
        global ticks
        ticks += 1

        clear()

        y = (150 * math.sin(ticks / 50)) + 150
        ctx.fillStyle = "blue"
        ctx.fillRect(x, y, 100.0, 100.0)
        ctx.fill()


    def animate(i):
        global id
        id = raf(animate)
        draw()


    animate(0)

We can call all of the canvas commands you’d normally find in a Javascript tutorial. Except that it is in Python. And you didn’t have to install any interpreters, developer tools, libraries, or compile any extensions.

Conclusion

I’m not going to disparage Javascript but I do prefer Python. Current generation browsers provide a rich set of APIs for graphics, networking, media playback and documents. The added bonus is being able to share your code with others. It’s nice that we have the tools today to leverage those abilities from almost any language we prefer.

I will continue experimenting with Brython and Skulpt in the hopes that I can get a full-fledged game running on them. I agree with other people in the Python community that this language should be better at games and one of the more important platforms for games today is the web. I suspect that the approach Brython and others take might be the avenue by which we can bring Python to the client-side of the web.

Download Brython or Skulpt today and see for yourself how much fun it is.