Help extending p5 and dependencies in web dev

I’m trying a big project, at least for my skills.
I’ll reduce the question in a moment, but let me give a little context first.
I have a server, in node, getting data from api and serving my client.
Client side I’m building some basic html. A header, a simple html navbar, and a footer. between those two, a canvas.

I’m not sure how to handle dependencies, it’s being a headache.
But i got everything working (until now) by this setup:

P5js is imported from CDN via a script tag in index.html
I got a entry.js, linked from index.html via ascript tag that has a type="module" property. This is the only way I found to make import CoolClass from ./uorModules/‘CoolClass.mjs’` work.

It does work.

Cool.

So now how to access p5, globalMode? Well I got code for talking to the server, and data to be passed to my sketch. A little trial and error, i figured out that I should use instance mode. I don’t remember anymore, but in Global mode I got some error (but then again, I might have chosen poorly, and my erro could be some where else.).
Anyway that how I was working.

// som e talking to the server.
// await answer
//runP5()

import Deputado from './ourModules/Deputado.mjs';
import CsDeputado from './ourModules/CsDeputado.mjs';
import { colors } from './ourModules/colors.mjs'
import { p5Singleton } from './ourModules/p5Singleton.mjs';
import Grid from './ourModules/Grid.mjs'

let deputados = [];
let lastUpdate = '';
let initialData = []

//The singleton instance
let grid;

// == == == == == 
function getInitialData() {
    axios.get('/api/start')
        .then(response => {
            // Handle success
            console.log('Data received from server:');
            initialData = response.data.deputados
            console.log("Hereby", initialData);
            const lastUpdate = new Date(response.data.lastUpdate);
            const formattedDate = lastUpdate.toLocaleDateString(undefined, {
                day: '2-digit',
                month: '2-digit',
                year: '2-digit'
            });
            const formattedTime = lastUpdate.toLocaleTimeString(undefined, {
                hour: '2-digit',
                minute: '2-digit',
                hour12: false
            });
            const formattedDateTime = `${formattedDate} as ${formattedTime}`;
            document.getElementById('ultima').textContent = `dados atualizados em: ${formattedDateTime}`
            runP5();
        })
        .catch(error => {
            // Handle error
            console.error('There was a problem with the fetch operation:', error);
        });

}
//call it
getInitialData();

function runP5() {
    const s = (p) => {
        p.preload = function() {
        }

        p.setup = function() {}; // === === === --- -> eof setup



        //draw
        p.draw = function() {
        }; // === === === --- ->  eof draw



        p.windowResized = function() {
            resetCnv();
        };


        //   EOF P5 default functions
        // === === == === === == === === ==


        // === === == === === == === === ==
        // other functions and objects using p5 
        //     vvvv ====== ==== vvvv ==== ====== vvvv

        function calcCnvHeight() {
            
        }
        function resetCnv() {
           
        }
        function displaySorted(field) {
           
        }

    }; //  === === === === === === ===    ---- -> end of 's' (instance of p5)

    let myp5 = new p5(s);
}

        // === === == === === == === === ==
        //some classes  using p5 
        //     vvvv ====== ==== vvvv ==== ====== vvvv


But them things grown and I start needing a lot of classes to have things organized, and making all this code inside this function in just one file is… well… not ideal at least.
Passing ref to p5 in every object or a static reference, was my next choice, but then I’m writing code like SomeCoolClass.p5.createVector(x, y).

I tried a Singleton.

// p5Singleton.mjs
const p5Singleton = {
    p5Instance: null,
    setInstance: function(p) {
        this.p5Instance = p;
    },
    getInstance: function() {
        return this.p5Instance;
    }
};

export { p5Singleton };

than in entry.mjs

import { p5Singleton } from './ourModules/p5Singleton.mjs';
unction runP5() {
    console.log("Im in!")
    const s = (p) => {
        //asap set the p5 Singleton
        p5Singleton.setInstance(p);
// more code... blah

and in classes, now in their own file .mjs

export default class CsomeClass {
            constructor(obj) {
                // the Singleton instance
                this.p = p5Singleton.getInstance();
                this.id = obj.id;
                this.nome = obj.nomeEleitoral;
        //blah...
       // and code in the class using p5 goes like
       this.p.rect() /// not really better anyway

and then … well I got some code I’ve wrote before, using a different setup that do extends p5.Vector.
In thatproject I got 4 files for my classes all imported as script tags in index.html without 'module', my sketch as well was a tag and in Global mode. and that worked also. Nothing imports or exports nothing. The class that extends p5 was like this:

class Gpoint extends p5.Vector {
        constructor(x, y) {
                //lets base it in p5Vector
                super(x, y);
                //several.. this is the original fixed point, not used yet					
                this.fixed_point = createVector(x, y)
                    //the adjusting point coordinates	
                this.gpoint = createVector(x, y);
                // anchor point of the window at creation time - to calc proportions			
                this.origin = createVector(0, 0);
                //the size of window. passin 1 as I'm going to divide by it: https://github.com/processing/p5.js/issues/5821
                // WINDOW SIZING !!			
                this.size = createVector(windowWidth, windowHeight, 1);
                //the ratio of the point to window size
                this.ratio = Gpoint.sub(this.gpoint, this.origin).div(this.size);
                //those so we can use it just as p5Vector, but maybe they just should replace gpoint
                this.x = this.gpoint.x;
                this.y = this.gpoint.y;
                //work arround fo rthe same bug https://github.com/processing/p5.js/issues/5821
                this.z = 1;

                return this;
            } // constructor

        // make from p5Vector
        static make_from_vector(v) {
            return new Gpoint(v.x, v.y);
        }

well I managed to use imports and exports so in my new setup entry.js sees evertyhing. (there are more classes involved. But when I run everything GPoint complains **createVector is not defined**. If I `console.log(p5) from the line above the one throwing the error, i get the p5 obj…

So …
GRRRRRRR
sorry…

Than I said. ok I/m going global mode! But for that i must take thetype="module" from script tag and then all my imports stop working again. omg should i go for having all classes linked from a specific script` tag in html and use Global.

well I’m lost.
any insight will help me

ps: perhaps a singleton instance Gpoint?

Updates, but still the question.

I managed to get everything working again via the singleton.
thank chatgpt to refactoring help. great use for it.

The craziest thing is I call some static p5.Vector.sub(). And I need to do this call just like that. So i have a global object anyway? Why all this trouble any way rsrsrs.
gonna keep testing.

update2:

Just tested. changing all calls from p.strokeWeight() to p5.strokeWeight()
did not worked.

Grid.mjs:282 Uncaught TypeError: p5.strokeWeight is not a function
but using the singleton and the direct call for the static version works…

Here’s a simple template that makes p5 globally available, but still controls when p5 is instantiated:


index.html:

<script defer src=//cdn.JsDelivr.net/npm/p5></script>
<script type=module src=index.mjs></script>

index.mjs:

import runSketch from './sketch.mjs';
runSketch(); // Invoke this after all the rest is ready!

sketch.mjs:

export default function runSketch() {
  // Make p5.js callbacks globally visible:
  globalThis.preload = preload;
  globalThis.setup = setup;
  globalThis.draw = draw;
  globalThis.windowResized = windowResized;

  globalThis.mocha = 'Hack to block p5.js auto global instantiation.';
  p5.instance || new p5; // Globally instantiate p5.js if it hasn't already.
  globalThis._setupDone = void 0; // Suppress duplicate imported warning.
}

function preload() {
}

function setup() {
}

function draw() {
}

function windowResized() {
}

3 Likes

Hey @GoToLoop, thanks. I was hoping for your help :slight_smile:

And how would I use it in others files? The classes?
Also, would I be able to use import statments in thi ssketch file?
like htis:
import Gpoint from './Gpoint.js'

probably in index.mjs would be fine, right?

Both are “.mjs” extension files, which have support for keywords import & export.

The idea is to only invoke runSketch() after the rest of your other libraries/modules have been properly initialized.

Just 1 “.mjs” file needs to be present in “index.html”; which in my template is called “index.mjs”.

That entry file “index.mjs” should be responsible to import everything else.

2 Likes

this is my directory:

├── lastUpdateDate.json
├── lastestData.json
├── package-lock.json
├── package.json
├── public
│   ├── android-chrome-192x192.png
│   ├── android-chrome-512x512.png
│   ├── apple-touch-icon.png
│   ├── entrada.mjs
│   ├── favicon-16x16.png
│   ├── favicon-32x32.png
│   ├── favicon.ico
│   ├── images
│   │   ├── fotoPlaceHolder.jpg
│   │   └── photoPlaceHolder.jpg
│   ├── index.html
│   ├── ourModules
│   │   ├── CsDeputado.mjs
│   │   ├── Deputado.mjs
│   │   ├── Detalhes.mjs
│   │   ├── Gpoint.js
│   │   ├── Grid.mjs
│   │   ├── Panel-class.js
│   │   ├── Panel.js
│   │   ├── arrayIO.js
│   │   ├── colors.mjs
│   │   └── p5Singleton.mjs
│   ├── site.webmanifest
│   └── style.css
├── server.mjs
├── servidor_app.sublime-project
└── servidor_app.sublime-workspace

I see, will try this approach tomorrow having Global mode would be cool…
thanks man.

Will test and ask again later.

thanks

@GoToLoop Help me understand this, will you?
The sketch.mjs. seems to work fine in global mode as per your suggestion.
How would I use the global instance in my classes.
I thought I wouldn’t need the singleton passing around anymore.
but…
ps: my files actually are named:
index.mjs => entry.mjs
sketch.mjs => globalP5.mjs

// in CsDeputados.mjs
export default class CsDeputado {
            constructor(obj) {
                console.log(p5); // here i got p5 as in the attached image
                this.id = obj.id;
                this.nome = obj.nomeEleitoral;
                this.siglaPartido = obj.siglaPartido;
                this.siglaUf = obj.siglaUf;
                this.imageB64 = obj.imageB64;
                this.image = null; // Initialize as null
                // Load the image
                this.makeP5Image();
                this.dataNascimento = obj.dataNascimento;
                this.badgeWidth = 357;
            }

            makeP5Image() {
                // Using a callback to handle asynchronous loading
                p5.loadImage(this.imageB64, (img) => {// here the error in the image
                    this.image = img; // Set the image when it's loaded
                });
            }

// there's more code below

As the name implies, the “global instance” is globally available after runSketch() is invoked!

p5 has multiple roles: constructor, class & namespace.
However, it’s not an instance object!
Instead, the global instance is stored in this static property: p5.instance

But b/c all p5.js API is globally available after runSketch(), we can directly use them w/o the dot notation: loadImage(this.imageB64, img => {

Just loadImage() is enough to invoke it! :wink:

2 Likes

Man!!! I love you !! :smiley:
Thanks a lot. this is like a dream, being able to just use p5.
no words…
thanks thanks thanks.

just merged to main :slight_smile:

In time, what is this mocha hack? How this works?

This is from an old code I did:

When I had to lookup how p5js decides to go “Global Mode” and then hack it:

As you can notice, if we assign anything “truthy” to mocha, p5.js library will never auto-instantiate p5!

Well, unless we do it ourselves later. :wink:

Same for _setupDone, but for suppressing the annoying warn() message:

2 Likes

Cool. thanks for posting.