How to access p5js functions when using ES6 modules

I am trying to write an app using ES6 modules and the voronoi library, and in this environment all of the processing instance functions (background, stroke, random, etc) are not in the module’s namespace.

I have got around this for the modules I’m writing by passing the processing object into each one and storing it in the module, i.e.

class SomeModule {
  constructor(processing) {
    this.processing = processing;
  }

  draw() {
    this.processing.background(0);
    ...

However, this does not work when I import files which assume that the processing functions will be in their namespace. Specifically in my case, I am loading p5.voronoi.js from my HTML, but when I try to use it I get an error:

p5.voronoi.js:280 Uncaught ReferenceError: round is not defined
    at setRandoms (p5.voronoi.js:280)
    at p5.voronoi (p5.voronoi.js:227)
   ...

How can I get processing’s round function, and all the other functions I rely on, into a namespace accessible from p5.voronoi.js?

That “p5.voronoi” library is meant for sketches using the “global mode” approach only:

It means at the very least your main “sketch.js” file gotta use the “global mode” approach as well.

If p5.js is loaded via <script>, it dumps itself on the global namespace & thus it can be accessed even by ES6 module files, as long as it finds setup() and/or draw() on the global namespace as well, like this: window.setup = function () {}

Here’s a p5.js global mode sketch example using ES6 module files:

Here’s a p5.js global mode sketch example using ES6 module files:

Brilliant! That looks to the solution. I hadn’t realised you could use the processing globals inside a module (the examples I found didn’t do it that way, but rather fetched the processing instance via a constructor, as above).

That’s an awful lot of this.processing. refernces that I’ve just had to remove from my code! Much neater codebase as a result.

Only problem now is that it doesn’t work, because every time I call loadPixels or image it complains that the canvas element has 0 width or height. I presume this is because creating the canvas is done asynchronously, and the way I did things before left time for them to complete before they were needed, but now things are happening quicker.

Going to go off an see how I can run those creation methods asynchronously (but if you can point me in the right direction, it might save me some digging :slight_smile:)

Classes in other files should still require a p5 instance, so they can be used regardless the main sketch is in global or instance mode.

AFAIK, <canvas> are created synchronously.
Loading assets, on the other hand, is indeed asynchronously.

Most I can do is pointing you to a more complex sketch I have, which also uses ES6 modules, classes, a 3rd-party library (Matter.js), and is available in both global & instance modes:

Thanks for your replies @GoToLoop and sorry for taking so long to get back on this… life intervened!

Your tips sent me in the correct direction, which put very simply was to set a setup and draw method on the global window object. I’m not sure quite where I got my previous approach from, but I copied it from somewhere, where instead the setup and draw methods were instantiated in something like the following way:

const sketch = processing => {
  processing.setup = () => {
    ...
  }

  processing.draw = () => {
    ...
  }
}

const myP5 = new p5(sketch);

I then had to iron out a few race conditions which I had been experiencing mainly as a result of my initial approach, but once I’d done that then it worked even better than before, plus I managed to clean the code of hundreds of ugly instances of (for example) this.processing.circle(... and replace them with a far more straightforward circle(...

Coincidentally, changing my approach to the one you recommended also solved the other mystery problem I’d been seeing, detailed in this post: Setup method runs twice - #3 by dansumption

BTW if you’re interested to see what I ended up making with all this new knowledge (and I’m very proud of it), here’s the music video I made entirely using p5js: https://youtu.be/rdbgckSmlAo

Thanks again!

1 Like

Some place like this 1? :wink:

p5.js sketches can use 2 approach coding styles: “Global” & “Instance” modes.

On “global mode” the whole p5 instance is dumped in the window global context.

While on “instance mode” we create a function containing our sketch code and pass it as the 1st argument for the p5 constructor.

“Global mode” is the easiest to code with. However, we can’t have more than 1 “Global mode” sketch running on the same frame context!

If we need to have more than 1 sketch, we’re gonna have to code the others on “instance mode” style.

Unless we place the extra “global mode” sketches on their own separate <iframe> tag:
Developer.Mozilla.org/en-US/docs/Web/HTML/Element/iframe

On the link below we’ve got 4 sketch instances on the same page:
Matter-JS-Bouncing-Colorful-Balls.Glitch.me/all.html

The top 2 are coded w/ “global mode” style, while the 2 on the bottom are using “instance mode”:
Matter-JS-Bouncing-Colorful-Balls.Glitch.me/sketches/global.mjs
Matter-JS-Bouncing-Colorful-Balls.Glitch.me/sketches/instance.mjs

If you hit CTRL+U on the 4 running sketches’ page you’re gonna come up w/ the following “all.html” file:

<script defer src=https://cdn.JsDelivr.net/npm/p5></script>
<script defer src=https://cdn.JsDelivr.net/npm/matter-js></script>

<iframe src=global.html></iframe>

<script type=module src=sketches/global.mjs></script>

<script type=module>
  import sketch from '/sketches/instance.mjs';
  for (let i = 0; i++ < 2; new p5(sketch));
</script>

Notice I had to place the extra “global mode” sketch in an <iframe>, b/c we can’t have more than 1 “global mode” sharing the same page frame:
<iframe src=global.html></iframe>

It loads another HTML file, which in turn loads & runs “global.mjs” along w/ p5.js & matter-js libraries, separated from its parent HTML’s context:
Matter-JS-Bouncing-Colorful-Balls.Glitch.me/global.html

<script defer src=https://cdn.JsDelivr.net/npm/p5></script>
<script defer src=https://cdn.JsDelivr.net/npm/matter-js></script>

<script type=module src=sketches/global.mjs></script>

In short we can stick w/ the easier “global mode” style as long as we take care to place them in separate <iframe> tags if we’re using more than 1 global sketch on the same page. :sunglasses:

Ahhhhh, that (and the rest of your post) makes sense! Thanks for going to the trouble of explaining.

I only anticipate using p5js to generate a single sketch (which anyway I am mainly just using as something I can capture in OBS so as to turn into an MP4 file), so don’t think I will ever need instance mode. Annoying that it was the only way I discovered initially of allowing modules access to the processing object. Still, it’s all learning…

1 Like