processing / p5.js

p5.js is a client-side JS platform that empowers artists, designers, students, and anyone to learn to code and express themselves creatively on the web. It is based on the core principles of Processing. http://twitter.com/p5xjs —
http://p5js.org/
GNU Lesser General Public License v2.1
21.2k stars 3.23k forks source link

[p5.js 2.0 RFC Proposal]: Minimize bundle sizes #6776

Open nickmcintyre opened 6 months ago

nickmcintyre commented 6 months ago

Increasing access

Reducing the size of the built p5.js library would increase access to people without high-speed connections or large data plans. Doing so would also reduce power consumption from data transfer and parsing.

Which types of changes would be made?

Most appropriate sub-area of p5.js?

What's the problem?

Accessibility

p5.js is currently 1MB minified. For comparison, that's about the same size as the minified versions of D3 and three.js combined.

p5.js is currently 4.8MB unminified. For comparison, that's about 2.5x the size of the unminified versions of D3 and three.js combined.

In The "why" of web performance, MDN lays out very plainly how performance impacts accessibility, especially for folks with fewer resources. Their case study focuses on websites with large data transfer requirements. @GregStanton also made the case for considering performance through the lens of accessibility.

Overall, it seems likely that we can trim several hundred KB from the built library.

Sustainability

As a side note, @GregStanton also mentioned global considerations. Big +1. I've suggested elsewhere that the new p5.js website should follow the Web Sustainability Guidelines.

Our creative medium has very direct and measurable environmental impacts (see Sustainable Web Design). Those impacts tend to make underlying social issues worse. So, I believe we should do our best to minimize them. I'd be more than happy to serve as a sustainability steward if people are open to the idea.

What's the solution?

I propose minimizing the data transfer required to code with p5.js and to view sketches. This could include a few related efforts.

Default to a minified build

It’s not clear that anyone studies the unminified p5.js bundle. The original use case may no longer be relevant, or at least it’s extremely niche. It also takes a JavaScript expert to understand the transformed source code in the unminified bundle. Even though the inline reference comments appear in place, a curious beginner or even a seasoned developer would have a hard time getting much use out of them.

If someone wants to study the p5.js source code, they can follow links from the FES/Reference to this repo.

A minified FES build could work in the p5.js Web Editor.

Refactor/rewrite using ES6+

Since we already plan to refactor, I propose making a concerted effort to trim all bundles as we do so. We can make the source code more accessible to developers while making the built library more accessible to users. I believe these efforts are complementary.

Can we thoughtfully choose JavaScript features that are more concise and expressive, and that won't require polyfills?

Change the build target

Modern JavaScript can make the p5.js source code more concise and expressive. It'd be nice if that translated into smaller bundle sizes, which means avoiding polyfills where possible. A few questions about browser and JavaScript versions come to mind:

Consider removing opentype.js

This dependency accounts for >15% of the minified p5.js bundle. Do we need it, strictly speaking? Could some advanced features be implemented as an add-on library?

Pros (updated based on community comments)

TBD

Cons (updated based on community comments)

TBD

Proposal status

Under review

davepagurek commented 6 months ago

I think this is a good goal!

In addition to the efforts you already mentioned, I think the biggest thing we can do in service of this is to make parts of p5 optional to include, which I believe is part of the plan in the current 2.0 proposal. For example, Opentype.js is currently required for rendering fonts in WebGL (not necessary, but it will take some R&D to see if other approaches are truly feasible. There's more discussion in https://github.com/processing/p5.js/issues/6391 if you're curious!) But it's only required for WebGL, and if you need to use textToPoints. I think a good approach might be to default to a more minimal build, and for some functionality like WebGL fonts or textToPoints, one has to also include another p5 file related to that functionality. To me this feels like an approach that lets us grow our feature set without imposing the size cost of all those features upon everyone. How does something like that sound to you?

curran commented 5 months ago

I came across this issue as well. It would be great to get the build size down. I wonder what is causing it...

image

This might be useful in tracking down what made it large:

image

https://bundlephobia.com/package/p5@1.9.0

davepagurek commented 5 months ago

I made a fresh build and sent it through disc to analyze its contents. I've attached the output: it's an html file that you can open in the browser after unzipping: disc.html.zip Here's a brief summary:

Screenshot 2024-01-30 at 8 57 19 PM

so, some observations:

nickmcintyre commented 5 months ago

I think a good approach might be to default to a more minimal build, and for some functionality like WebGL fonts or textToPoints, one has to also include another p5 file related to that functionality.

Agreed this seems like an easy win. Most intro classes I'm aware of stick to 2D the entire time. When someone's ready for WebGL, they shouldn't have much trouble following a quick guide to include another file or two.

What do you think about having createCanvas(400, 400, WEBGL) raise a friendly error if p5.webgl.js (or whatever) hasn't been included? The same could go for text() in WebGL mode.

davepagurek commented 5 months ago

I think that would be good! Like maybe in general we can keep around stubs of all the core methods, but where they only throw a friendly error telling you to add a script tag for that other feature.

Also, in case anyone's curious, here's that same visualization as above, but on p5.min.js. It's basically the same, but the docs folder is gone. disc-min.html.zip

curran commented 5 months ago

The pattern is already there for the sound module. I think it would be excellent if all the WebGL-related things were chunked out into a similar external file.

image

lee2sman commented 5 months ago

Thanks for introducing this idea. It makes sense to explore minimizing bandwidth, for exactly the reasons you specified. I'm also thinking about the great paper Permacomputing Aesthetics: Potential and Limits of Constraints in Computational Art, Design, Culture by Aymeric Mansoux, Brendan Howell, Dušan Barok, and Ville-Matias Heikkilä, which seems relevant here.

Questions:

mvicky2592 commented 3 months ago

@lee2sman There is a project like this already. "q5" that's just a reimplementation of p5.js' 2D api and doesn't have any other dependencies, though unfortunately it lacks friendly error support.

https://github.com/quinton-ashley/q5.js/

limzykenneth commented 1 month ago

I've added Rollup bundle analyzer to the dev-2.0 branch and the output result of the main minified bundle is available here: stats.zip and a screenshot below:

Screenshot 2024-06-16 at 20-32-27 Rollup Visualizer

As suspected, the largest components currently taking up space are the FES parameter validation data and opentype.js. I would say while WebGL is significant at about 25% of the remaining file size, we'll gain better file size reduction with the two mentioned, which we already have proposals to refactor already.

mvicky2592 commented 1 month ago

@limzykenneth thanks for this visual!

Open type is only necessary for webgl right? So webgl+opentype take up like 40%. Indeed, FES seems to be the next largest segment.

It'd be great if for p5.js v2.0 webgl and fes could be split into separate modules, then a smaller ~500kb bundle could be made.

Where is the FES parameter validation data located in the repo?

dhowe commented 1 month ago

Opentype is only necessary for webgl right?

This is not strictly true. All the functionality provided by p5.Font currently relies on opentype, including loading of custom fonts, calculation of bounding-boxes, and textToPoints(). This is not to say that much of this can't be replaced with newer browser-native features, but at least webgl text, textToPoints, and tight bounds will require font path data, for which there appears to be no great substitute at moment. Additionally it will require significant code and extensive testing to safely eliminate this dependency (this is not to say that it shouldn't be done).

dhowe commented 1 month ago

To finish, as I've said elsewhere, I think textToPoints could happily exist as an external library, expecially as it doesn't exist in Processing at moment (though I did implement it in Processing 3) which would potentially allow webgl+opentype (or some subset of it) to be refactored into a module outside the core... But again, removing opentype from the core is not a small job

davepagurek commented 1 month ago

I think there's a world where we make a "text metrics" module that can optionally be bundled into p5, or not loaded if it isn't used, similar to what we've been trying to do with math. Still nontrivial work, but maybe a more reasonable goal.

Maybe a question for the typography refactor issue instead of this one, but I'm curious how close native 2D canvas's text measuring APIs are to meeting the bounding box needs? They definitely don't do points on paths though.

dhowe commented 1 month ago

Maybe a question for the typography refactor issue instead of this one, but I'm curious how close native 2D canvas's text measuring APIs are to meeting the bounding box needs? They definitely don't do points on paths though.

I believe we can get quite close, we just can't do tight bounds, which depends on paths/points. This is not a big deal except for things like precise collision detection, but again this could be part of a library or module