PyScript + p5.js

Hi all, I’m trying to see if I can get PyScript working with p5.js, and wondering if anyone more experienced could offer some perspective.

So far I’m able to add both scripts, and get PyScript to import something called p5 from the global namespace:

<html>
    <head>
        <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
        <script defer src="https://pyscript.net/alpha/pyscript.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
    </head>
    <body>
        <main>
        </main>
        <py-script>
            from js import p5
            
            print(p5) # prints what looks like a function called p5
        </py-script>
    </body>
</html>

My problem trying to move forward is that the way p5 is supposed to be used isn’t just as a collection of functions to call (though that’s a part that I’m missing). The usual way of using it is something they call “global mode” where you just define some JS functions (setup, draw, etc) in the global namespace, inside of which you call other p5 functions that are supposed to be in the global namespace (createCanvas(), fill(), rect(), etc).

I assume that I can’t define the setup/draw functions inside of PyScript in a way that the p5 library will pick up, so I’m thinking I’d have to try the “instance mode” from p5.js (see here: p5.js overview · processing/p5.js Wiki · GitHub)

In that style, one way or the other you need to ultimately call a constructor for a p5 object.

Here’s where I’m stuck:

  1. The p5 that comes from “from js import p5” appears to be a function in Python, and looks like it’s the constructor that “new p5()” is expecting from js, but when I try to call it like a Python constructor, I get an error telling me that I can’t call a class like a function.

  2. Even if I could get that working, I’m not at all sure how to get access to the actual drawing functions (createCanvas, fill, rect, etc) that are supposed to be in the global namespace. All of the following fail with different errors:

from js import * 
# module js doesn't have __all__

from js import createCanvas 
# module js doesn't have createCanvas

p5.createCanvas() 
# p5 doesn't have createCanvas attribute

This seems like, because of the way p5js is expecting to work, it may be more tricky than other JS libraries would be to integrate. Does anyone have any thoughts, or things that I might try? I’d really love to use p5 but with Python syntax!

2 Likes

nice to meet you.

The p5 that comes from “from js import p5” appears to be a function in Python, and looks like it’s the constructor that “new p5()” is expecting from js, but when I try to call it like a Python constructor, I get an error telling me that I can’t call a class like a function.

It’s easy to do this with something like Jython (https://www.jython.org/), but when it comes to Python, the basic instance calls are C-based, so everything It’s like a function. For more information, check out the official Python documentation.
URL: 3. Data model — Python 3.12.0 documentation

Even if I could get that working, I’m not at all sure how to get access to the actual drawing functions (createCanvas, fill, rect, etc) that are supposed to be in the global namespace. All of the following fail with different errors:

There is a way to execute a PyScript from the JavaScript side. However, the mechanism for executing JavaScript from the PyScript side has not yet been developed.
However, it may be solved by using Ajax. The question is, where does the Ajax mechanism itself use Python installed? Must be decided.
In the case of standalone, I think it is possible.

This seems like, because of the way p5js is expecting to work, it may be more tricky than other JS libraries would be to integrate. Does anyone have any thoughts, or things that I might try? I’d really love to use p5 but with Python syntax!

Since PyScript itself is still under development, I think that various experiments will be conducted and solutions will be born.
In the near future, I think we will be able to seamlessly switch between JavaScript and PyScript.

Regards you.
ktsh.tanaka.2020

I may have misunderstood what you mean here. Are you sure you’re correct when you say this?

I’ve seen examples of calling JavaScript from Python, as outlined here: PyScript: JavaScript and Python Interoperability - John Hanley

2 Likes

to neil

I agree.
Experiments are being conducted on calling Javascript from Python. However, the opposite case has only been done via Ajax yet.

I am accessing from Japan. This is a Japanese site, but please refer to it.

1 Like

Hi @ktsh.tanaka.2020 - thanks for getting back. Sorry but i don’t see how that link is relevant. It includes discussions from 2012 and clearly pre-dates PyScript.

I think there’s a risk you’re accidentally going off on a tangent that doesn’t seem relevant to Andrew’s query and doesn’t support your earlier statement that the “mechanism for executing JavaScript from the PyScript side has not yet been developed”.

Is there some connection or relevance that i may be missing?

1 Like

hi, neil , thanks for replay.

I agree. I might have misunderstood it as another argument. I apologize for that.

I think there’s a risk you’re accidentally going off on a tangent that doesn’t seem relevant to Andrew’s query and doesn’t support your earlier statement that the “mechanism for executing JavaScript from the PyScript side has not yet been developed”.

Regarding p5.js, I think it was pointed out that it is difficult to call from the python side.
I need a python library to solve this problem, but it doesn’t seem to exist yet.

In Japan, on December 21, 2020, there is an article about calling python from p5.js in the article “21st day of ISer Advent Calendar 2020”.

However, upon further investigation, “GIthub” has his pyp5js developed by berinhard.

Maybe if you can install it on pyscript (and of course you need to add it), your wishes will come true.

1 Like

Hi all, thanks for the replies.

@ktsh.tanaka.2020 I appreciate the links. I was aware of pyp5js, and in fact, the latest version is built on top of Pyodide (as is PyScript). You’re right that if all I want is to be able to run p5 sketches with Python code, that would do it for me! I should have been more specific in why I’m trying to use PyScript.

Right now, pyp5js is built as a Pyodide/Python wrapper with a manually-maintained set of Python functions that wrap the relevant JS functions (see here: pyp5js/target_sketch.js.template at develop · berinhard/pyp5js · GitHub)

That’s a clever solution, but it’s limited because any changes in P5 need to be manually updated in pyp5js, and it also means that it doesn’t have easy access to P5’s extended libraries (like p5.sound, etc).

My hope with PyScript was that it would be able to plug into the existing P5 ecosystem without needing to be actively and separately maintained.

It seems like PyScript is able to do part of this right now (call JS functions from PyScript), but I think what’s missing (or what I’m not understanding) is how to use JS classes/objects from PyScript. Maybe this is not possible (yet?).

2 Likes

I’m totally shooting in the dark here, and this is untested with p5.js, but

babylonjs = js.window.BABYLON

works wonderfully for Babylon.js (which I discovered completely accidentally :smiley: ).

Perhaps there’s an equivalent way to “import” the top-level p5 JS object…

1 Like

This is a really cool idea and I :heart: P5JS.
I’ll be working on a small demo for the team next week and will try to leverage P5js for this demo.
I can update my progress here.
Cheers!

3 Likes

So, poking at a super-simple p5js sketch using the Chromium DevTools, what I find is that, with e.g. <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.js"></script> inside the head tag, typing e.g. window.el into the Console brings up the completion list with ellipse at the top! Cool.

TLDR; Loading p5js in that way apparently attaches all of the p5js objects to the window object (I’m pretty sure my JavaScript nomenclature is incorrect :sweat_smile:)

Maybe it would be possible to do import js.window as p5

1 Like

@marimeireles Sweet!! Yes, please share if you make any progress! :slight_smile:

@Ben_Alkov Unfortunately, I believe that in p5’s “global mode” (which is what you’re seeing there) the drawing functions are only added to the global namespace after the sketch has already been started - at which point, it’s too late to modify the contents of setup()/draw()/etc to affect the sketch!

1 Like

@andrew.hannum; You’re absolutely correct.

Using a hint from “Global and instance mode” on the p5.js wiki,

I kept beating my head against it 'til I got a minimal pyscript/p5.js demo.

TBH, I don’t know how useful it is - e.g. three and Babylon both have built-in render loops which take a callback; I don’t know off-hand how to set up a similar loop for p5.js - but at least my curiosity is satisfied :laughing:.

The trick is to have a “stub” function for p5.js to trigger it to initialize itself, e.g.

      <script>
        function setup() {}
      </script>

(IDK what would happen if draw were used here - probably nothing good) , and then, in Python, do p5js = js.window.p5.new() and Bob’s your uncle - as it turns out, that’s unnecessary. Just use p5js = js.window for convenience.

1 Like

Seems like the bot ate my previous post, but no matter - this one’s better: Here’s a fully working p5.js example (Bouncy Bubbles at my GitHub pages) ported from the “official” collection (examples | p5.js)

2 Likes

Whooo baby, @Ben_Alkov I believe you’ve done it! :grinning: That’s fantastic, very clever workarounds (the “stub” and also the manual calling of a recursive draw loop!)

And in fact, indirectly you’ve answered one of my original big questions - how to call the constructor for a JS object from PyScript. I guess the Python-ified JS object gets a .new() method that you call – so "Whatever.new() , instead of “new Whatever()”.

And with that, I’ve gone ahead and added sound effects to your bubble demo using the p5.sound library!!! See here: Musical Bubbles

This is an unfair amount of fun. :cowboy_hat_face: Thanks for spending the time to hack this together!

I’m gonna be honest, I think this is a game-changer… (Python game development that runs in-browser maybe?)

Next steps for me are to figure out how to hook up normal callbacks for mouse/keyboard (mousePressed(), keyPressed(), etc)

2 Likes

Yes! Exactly, this is how one works with e.g. three.js.

Sweet! I love sonification projects.

Right?! I’m so psyched to be able to do “real” web dev using essentially only Python…

Check out https://github.com/tildebyte/file-hosting/blob/3addfbbaa11149dc010c4e9c33fb18ccf317d4ca/pages/orbitingcubes-pyscript-threejs/orbitingcubes.py#L196 for an example of handling mouse events - the same callback concept applies for any window event (tldr; use js.window.addEventListener('click', create_proxy(_handle_click)) where_handle_click is a Python function.

You’re welcome to use the code in my “pages” repo if you wish - there’s no license there yet, but I will definitely choose one I picked Apache 2, which requires attribution, so keep that in mind :+1:

EDIT: But don’t overdo it - you don’t need to provide license info/attribution if you just want to copy a few lines out of a script :smiley:

3 Likes

Wow!
That’s so awesome :grinning:

Thanks for the effort @Ben_Alkov!!

I’d love to have your example on the community examples repo we’re building: GitHub - pyscript/pyscript-collective: Placeholder repository for community contributions to pyscript

If you want to open a PR. I’d also help contributing some docs to it :slight_smile:

1 Like

@ marimeireles

I opened several PRs in pyscript/pyscript-collective :+1:

I had a question about CONTRIBUTING.MD, section “Contributing a new resource”: it says " The contributions in this topic are more similar to what an awesome-page would look like" - I’d like to put a link to my Github pages repo which has some demos made with pyscript. Should I open a PR where I add an “awesome-projects.md”, or will it be a GH wiki page, or something else entirely?

Wow, that’s awesome :smiley:
Thank you!

That’s a good question, what do you think it’s best?
We’re still very much in the process of building that repo. I feel like in general an MD file has more visibility, but if you have any other ideas or models, I’m curious to hear thoughts.

1 Like

We’re probably getting a bit OT here; I moved this to an issue in the pyscript-collective repo :saluting_face: (#17; insanely enough, Github links are completely blocked from posting here)

Hi @Ben_Alkov - I’m not seeing anything obvious blocking my posting of GH links here. From the preview mode, i see #17 below absolutely fine. I’ll post this now (just in case it only breaks on posting??!)