We are about to switch to a new forum software. Until then we have removed the registration on this forum.
After a rather lengthy hiatus from Processing I now find myself experimenting with p5js... Work has led me into the realms of JS development (programming skills learned largely with Processing!) and I wanted to dabble with HTML canvas stuff; so this was a natural library to play with :)
I'm wondering whether anyone has yet figured out a sensible approach to splitting projects into separate files? I really liked that feature in the Processing IDE. I just managed to get something fairly crude working with requireJS; but it seems a little convoluted and I'm having to pass references to p5 all over the place. It just doesn't feel right. The other approach I'm considering is using Grunt to combine partials into a single script file whenever I save; but that comes with setup work I'm perhaps too lazy to do frequently...
Anyone got anything up their sleeve?
Answers
Only thing PDE does is collect all ".pde" files and merge them as 1 ".java" before compilation! :-\"
This process also happens in "Java Script" & "CoffeScript" modes.
And I believe in some other "modes" as well.
Maybe 1 day we end up w/ "p5*js Mode" too! O:-)
Fair point... But I prefer not to pollute the global namespace so am wrapping my p5 code in a function as suggested in the docs. That makes it a little more awkward to split files in a 'programmatic' way: you'd just have to arbitrarily split source code and then later concatenate into a single file. In my experience that can then cause issues with IDEs: code folding going awry etc...
So I was hoping for something more elegant; which is what led me to consider requirejs; but that doesn't feel like a natural fit. I was hoping someone might have already come up with an elegant solution; but I guess it's still early days for p5js...
Still a JS rookie but AFAIK, only Processing's callbacks like preload(), setup(), draw(), mousePressed(), etc. needs to be inside the sketch's function passed to p5's constructor.
Other functions or classes used by the main sketch can live inside other ".js" files I guess.
To get access to p5 methods and properties you need a reference to hook into though... That led me to include my class inside the 'sketch' function. Admittedly I've only had a short time with it, so a better approach may present itself with a little more reflection...
You can pass the main sketch's reference to the "remote" class' constructor in another ".js" file.
It's the same technique used by Java Mode's libraries in order to interact w/ sketch's canvas.
It's also true when we got ".java" files that gotta deal w/ ".pde" files.
Instead of RequireJS, you can use Browserify. It needs a bit less ceremony than RequireJS, but it still allows to have clean separated modules.
Otherwise, if you don't care about modularization and want to use the technique described by GoToLoop, you have have several JS files and use
cat file1.js file2.js > index.js
or similar...<script>
tags and it's done!this
to them.Thanks both for your input!
I'd like to use modules and sensible development practices; but I also want to avoid unnecessary complexity for the sake of tidiness. So I'm looking for a sensible balance :)
From my experiments thus far passing "the main sketch's reference" to the "remote" class isn't necessarily as simple as it first sounds. What about 'global' constants you want to pass from the main sketch to your 'classes'? I had to create an object to store them in my requirejs experiment. It looks like modularisation forces 'sensible' (but sometimes painful) practices (e.g. I had to create getters/setters for some object properties - which I'm not used to doing in JS)... But it's all another step away from the simplicity that the Processing/ps5 approach allows...
So for the time being I'm going to take the Grunt approach: that will allow me to automate the
cat
process PhiLho suggests (hello again BTW: it's been a while!) as well as run a test server that will update my browser whenever I save my source code. This is all an exercise to improve my general development processes and I'm just getting my head round Vagrant, npm, Grunt and so on; so it's a useful exercise. If I manage a decent base setup I might even explore creating a Yeoman generator :)P.S. @GoToLoop I'd advise a certain amount of caution when recommending the use of the
this
keyword in JS: it's a common gotcha for JS newbies... Since the main sketch should already have an explicit reference to p5 it's far safer to pass this directly thanthis
which can lead to unexpected results.this
:https://github.com/processing/p5.js/wiki/Instantiation-Cases
this
== passed argument reference! ~X(be it
this
or something else! 8-Xthis
inside its scope and belong to some instance) can be dangerously tricky! >-)this
) in JS is as safe as it is in Java.this
inside, it's completely safe.this
inside represents the reference that just called the function.this
= window! :-&https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors#Method_binding
I've taken the challenge today. I'm also trying to learn JS yet! X_X
I'm using Firefox Developer Edition 39.0a2's WebIDE for it btW.
http://cdnjs.CloudFlare.com/ajax/libs/p5.js/0.4.4/p5.min.js
"sketch.js":
"face.js":
"index.html":
new Face(p, w>>2, h>>2, w/3, h/2.5)
this
, sincep = this
after all.this.p.fill(Face.EYES);
&this.p.random(this.h>>4)
.P.S.: Here's the original Processing sketch running online via Pjs framework: :D
http://studio.ProcessingTogether.com/sp/pad/export/ro.9G5yFfYFFDXi1/latest
Thanks for taking the time to build a code example :)
Don't get me wrong: I know the module approach can be done; and I know 'this' can sometimes be exactly what you expect; but in my experience with JS it's best avoided when there's an alternative already available ;)
In terms of 'Class' construction I've been using Addy Osmani's Javascript Patterns as a reference at work and find the revealing module pattern a close enough approximation of a Java Class for my purposes (at least as far as I got with building Java Classes in Processing). Obviously this is something of a matter of opinion/taste but I find the resulting code neater (and notice the lack of 'this'). This code should work as a drop-in replacement to face.js:
Must admit Object.freeze is a new one on me: but I've only worked on relatively small front-end JS projects. I think it's probably overkill to try and make a Class immutable in a small project like this; and there's little need for making a 'constant' actually constant when it can only be accessed from within the 'class'.
private
&public
access level keywords! :)>-var
w/const
. No lazy excuses! :Ppublic
in "prototype pattern" after all!const
ends up being a so-so protection layer.const
in the right places:However, since all instance properties are
final
after being initialized inside the "constructor", everything should beconst
anyways (if it's not a passed parameter): \m/I've spotted a bug in your revealing implementation:
You've forgotten to reveal WEIGHT & OUTLINE besides display()! :-&
In the "sketch.js", inside setup(), there's this following statement:
p.stroke(Face.OUTLINE), p.strokeWeight(Face.WEIGHT);
So they gotta be
public
as well:Although I'm not too sure whether we can statically access a revealing class member w/o instantiating 1 1st! :-S
Much probably we're gonna need to move those constants outside Face's revealing constructor! :(|)
As promised, some cons related to "revealing pattern" and why I've finally accepted the most common "prototype pattern": :-\"
static
fields are instantiated when creating an object from some class!prototype
, while fields bound tothis
goes to the newly-born object.Hmmm... bugs :\">
Good spot on my missing making OUTLINE and WEIGHT public; though I might argue whether these should be 'constants' under Face; and that they certainly shouldn't be applied globally in setup(): that makes it somewhat inflexible if you ever want to extend the sketch or mix in other objects...
One strange thing I spotted though: I'm pretty sure I copy pasted your original source for sketch.js and didn't make any edits and I have:
I notice you edited your post :-B
Anyway... it looks like we're coming at this from very different perspectives. The sad reality of JS is that in a production environment you can't often rely on all these new-fangled technologies that might theoretically make your life easier and your code more 'robust': if you've got a few hundred thousand visitors to your site each day an unacceptable proportion are going to get broken content. So I'm stuck a little too firmly in the mindset of providing the best possible browser support.
You're clearly interested in the new features in EcmaScript 6; maximum performance and probably not too concerned whether your code is going to run nicely for everyone. I'll certainly want to squeeze maximum performance out of p5 where appropriate; but I'll happily continue using the RMP when the simplicity/maintainability of the code outweighs any of the negatives you list.
But we're now digressing wildly from the topic. I'm sure I'll have time to engage in some more theoretical discussions later on; but for now I have an answer to my original question. Thanks both!
Just some couple last considerations below... :P
I've amended the "revealing module pattern" so those constants are defined outside and can be statically accessed from anywhere now: :)>-
"face-revealing.js":
const
is indeed ECMAScript 6.https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze
const
is ECMAScript 6, it's been recognized, at least as an alias forvar
,since Firefox 13, Chrome 20 or less, but only now in IE11:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const
for...of
loop we gotta delay its usage a little more.https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of
this
! ~X(static
constants & variables outta it.new
in order to instantiate RMP classes! \m/If you want to use ES6 features in all browsers, you can use transpilers like https://babeljs.io/ or Google's Traceur.
const
are already available even in very old browsers like Opera 12 w/ Presto engine!On the back of this discussion I went away and looked at the .prototype approach to adding methods to a class: and I'm glad I did. In practice I've yet to work on a project that required the instantiation of many objects; so the RMP has worked well for me: I'm not building web apps; just 'nice-to-have' features that many front-end devs churn out using jQuery. I have clean code to work with; it works well in the majority of browsers (even IE8) and it doesn't rely on any extraneous libraries.
I'm still early in my investigations; but one limitation of .prototype methods appears to be that the parent 'Class' must be defined in the global namespace for it to work; and I've yet to figure out a way to avoid this (I don't doubt there is one; but I haven't found any reference yet). I'm deeply suspicious of anything that pollutes the global namespace: I don't have absolute control of what scripts get loaded on our site and some bright spark may decide to pull in (or even write) a poor quality script that will cause conflicts.
Obviously I'm not using p5 in the same context; but I prefer to follow the same best practice (it would be cool if p5 encouraged best practice in a general web context too!). So requireJS is certainly one possible solution - but it's heavy going. I'll have a proper look at Browserify: I vaguely recall looking at that a while back but at the time got the impression requireJS was the way to go. With the huge popularity of node it looks like there's been a change in direction...
As for 'transpilers', shims etc: I prefer to avoid them. They usually involve adding more processing overheads on browsers/hardware already ill-equipped to deal with the original source. Most people writing production code - that needs to support a wide range of browsers, including mobile - won't be switching to Ecmascript 6 for a few years IMHO. On the other hand if you're running it on the server-side then you don't have the same issues... and again that's in a different context to experimenting with p5!
function
which calls itself.function
, there gotta exist some intermediary global variable in order for the "isolates" to access each other!Sure, but it's trivial to hook RMP off a single global variable, and that's a tactic used in other languages. Well written libraries do the same or take measures to avoid namespace pollution. So I'm assuming it's possible with .prototype based 'classes', but my attempts so far have failed... Hopefully I'm missing something simple: I have too many distractions here for me to concentrate properly on this :/
Never used any "global modular loader script" before, but outta curiosity I went to http://requirejs.org/.
As far as I got it, rather than loading all script files we need in the ".html" file like this:
According to http://requirejs.org/docs/start.html, we'd do something like this instead:
"As for 'transpilers', shims etc: I prefer to avoid them. They usually involve adding more processing overheads on browsers/hardware already ill-equipped to deal with the original source."
Two wrong conceptions there:
GoToLoop said:
"on their way" doesn't mean full support of ES6 (yet).
And if you don't care supporting all browsers, fine for you, but some of your users might be frustrated... Of course, if you code only for yourself, that's fine.
Looks like it was just this; or some other issue with the code I was testing on. I went back to your example and had another attempt. I did have to move the position of your 'constants'; but otherwise this does indeed work:
sketch.js
face.js
I've made some other changes that reflect my preferences in terms of coding style. One advantage of having that global hook is that you can use that to pass around references to p5; rather than passing it around with parameters in your methods. I initially set var p = this.p; in Face.display() (before I'd added p5 to foo) as that made it far clearer that p referenced something outside the 'class'. I swapped it over to reference foo.p afterwards; as I still like the resulting tidiness.
The only disadvantage I can see to this general approach is you're adding an extra level to traverse to get to the object; which might come with a minor performance hit. IMO that's a price worth paying for the extra robustness; though by all accounts the best strategy nowadays is to move to something properly modular like requireJS or Browserify...
Note: obviously you might choose something a bit more unique for your global object ;)
@PhiLho said:
True. I remember a friend having major issues because they tested an app in IE8 set to IE7 mode but didn't test in IE7 itself. The JS engine in IE7 sucked and their app ground to a halt in the live environment. Hopefully those days are long gone!
However I'm also targeting old browsers running on old mobile handsets. I don't add file-weight and extra processing in this context just to add curved borders on boxes or CSS3 animation. The user can live without these fancy features in favour of faster performance.
My mistake and the clue is of course in the name.
Indeed, the bottleneck is now less the old browsers (although my public library still has IE6 on their public computers!), but browsers on mobile phones: still need to have lightweight libraries for them.