P5.js Hello
Table of Contents
What Is This?
This is a post to remind me how to make p5.js posts. It's based on my somewhat idiosyncratic way of working (using Emacs' org-mode for the text-markup and nikola to render it as HTML) and is meant to help me get restarted if I stop using p5.js for a little while and need to remember how to do it. The short version:
- Include the
p5.js
JavaScript library in the post. - Make a "tangle" to export the JavaScript file.
- Make an HTML
div
tag to locate the p5.js sketch in the post. - Make a sketch that defines the necessary
p5.js
methods (mostlysetup
anddraw
). - Create a P5 object, passing in the sketch and the div ID to the constructor.
The Setup
The next three sections in this post won't normally be visible in other posts, they're specific to my use of nikola and a noweb-like style with org-mode and will be in the source but won't be in the exported HTML.
The Meta-Header
To get p5.js
into this post I'm telling nikola to use a template I made in the meta-data block at the beginning of the file for this post.
.. template: p5.tmpl
The p5.tmpl
template adds an HTML tag to include the p5 JavaScript code in the HEAD section of the HTML document.
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.min.js"></script>
The Tangle
Since the intent is to define the JavaScript in the org-mode blocks and then export it I'll typically create a tangle
code block to control the export. The org-mode header uses a path relative to the org-mode file that makes up this post. This file is in a sub-folder (posts/
) so the path has to go up one directory first then back down into the files/posts/
folder and then into another sub-folder whose name matches the slug for this post (p5js-hello
). The folder named after the slug doesn't exist by default and won't be created by Emacs (it will just fail with a message in the Emacs mini-buffer when you export the file) so you need to create it before doing the export.
The tangle is made up of references to blocks that I'll be defining in later sections, referring to them using their name. For example, <<hello-div>>
in this tangle will match an org-mode block named hello-div
defined in a later section of this post.
#+begin_src js :tangle ../files/posts/p5js-hello/hello.js :exports none
<<hello-div>>
<<hello-class>>
<<hello-sketch>>
<<p5-instance>>
#+end_src
The DIV Shortcode
In order to specify where in this post the p5.js
sketch will be rendered I need to create an HTML source
tag which points to the JavaScript file I created in the tangle section above (in this case the file is hello.js
). I also want to create a div
tag with an ID that I define so that I can tell p5.js
to use it as the location for the sketch (in this case the ID will be "hello-08b095a8"
).
This is the jinja-based shortcode I use to put those two tags into the post.
{{% p5div source="hello.js" divid="hello-08b095a8" %}}
The hello.js
file will sit next to the HTML file for this post once we export it so you only want the file name, not the path to the folder it sits in. I'm mentioning it here because I sometimes get mixed up between the three cases I've run into for files:
- files that need a path from this post (like the tangle)
- files that need paths from the top of the repository (usually created using commands run outside of org-mode)
- files that just need the file-name
When nikola
builds the site it will convert that shortcode to a couple of tags that look something like:
<script source="hello.js">
<div class="p5js" id="hello-08b095a8"></div>
In the future I'll probably get rid of or change this shortcode, since the only thing it does that's specific to p5 is set the class
attribute, but, for now, this is how it's getting done.
The JavaScript
The remaining sections define the JavaScript that gets exported into the tangle file that I defined in the first section of this post.
The Div ID
Since I need to pass the ID given to the div
tag in the previous section to P5 I'll store it in a variable for later.
const HELLO_DIV = "hello-08b095a8";
As noted above, the tangle refers to the JavaScript blocks I'm defining in the rest of the document by name (which I give to them in the block-headers using :noweb-ref
). The header where I stick the name doesn't get exported into the HTML document so just for this block I'll show what it looks like with the org-mode header along with the JavaScript that goes into the block.
#+begin_src js :noweb-ref hello-div
const HELLO_DIV = "hello-08b095a8";
#+end_src
Here the :noweb-ref hello-div
tells org-mode to stick the JavaScript into the tangle block above where the <<hello-div>>
placeholder text is.
The Hello Class
Here's a toy class that keeps the parameters for the circle and which draws the circle when its draw
method is called.
class Hello {
constructor(p5, width, height, diameter, step) {
this.p5 = p5;
this.step = step;
this.radius = 10;
this.width = width;
this.height = height;
this.y = height/2;
this.diameter = diameter;
this.x = this.diameter + 1;
}; //constructor
draw() {
this.p5.circle(this.x, this.y, this.diameter);
if (this.x <= this.diameter || this.x >= this.width - this.diameter) {
this.step *= -1;
};
this.x = (this.x + this.step) % this.width;
}; // draw
}; // Hello
Hello Sketch
Here's the sketch that gets passed to the P5 constructor.
function hello_sketch(p5){
const WIDTH = 800;
const HEIGHT = WIDTH/4;
const BACKGROUND = 255;
const ALPHA = 50;
const POINT_COLOR = "RoyalBlue";
let HELLO;
p5.setup = function() {
p5.createCanvas(WIDTH, HEIGHT);
p5.background(BACKGROUND);
p5.stroke(POINT_COLOR);
p5.fill(BACKGROUND);
HELLO = new Hello(p5, WIDTH, HEIGHT, 50, 5);
}; // setup
p5.draw = function() {
p5.background(BACKGROUND, ALPHA);
HELLO.draw();
}; //draw
}; // hello_sketch
P5 Object
Finally, I'll create a p5
object, giving it the sketch and div
ID so p5.js
will render the code.
new p5(hello_sketch, HELLO_DIV);
If we go back to the top we should see the sketch running (a circle moving back and forth) .
The End
And that's it for the basic post. The parts once again:
- Use the p5 nikola template in the nikola-header.
- Tangle the JavaScript to a file in
../files/posts/<slug>
. - Setup the
script
anddiv
tags to include the sketch in the exported HTML (using the p5div shortcode). - Write the sketch to pass to p5.
- Create a p5 object using the sketch and
div
ID.