Displaying P5.js Sketches on Cargo Sites

Philip Meyer
9 min readJan 3, 2021

--

In 2020, I made a multimedia art project for the web called Seven Spaces. I built the site using Cargo, and some of the pieces in the collection where P5.js sketches.This is a guide on how to get P5 sketches working in Cargo.

This guide assumes you already know the basics of P5.js, JavaScript, and Cargo.

Overview

Here are the steps we’ll follow:

  • Upload your P5 files to Cargo, so they’re hosted along with the rest of your site
  • Create iframes on your pages in Cargo that will display your P5 sketch
  • Add some custom code that will allow the sketch to be displayed correctly when the user resizes the browser window

Step 0— Prepping your sketch

To make this work, you’ll probably want to set the dimensions of your canvas to the width and height of the window. This makes it easy for the sketch to adapt to the size of the iframe that we will use to display the sketch on the Cargo site (more on this below).

So, within your setup() function in p5, set the canvas width and height to windowWidth and windowHeight :

function setup() {createCanvas(windowWidth, windowHeight);}

Note: In some cases, you’ll need to make a few more modifications to sketch.js and (perhaps) other files. These cases are described below.

Step 1 — Upload files

First, we’ll upload all of the files used to make a P5 sketch: index.html , sketch.js, and any other JavaScript files you might be using.

Note: If your sketch includes images, audio, or any other files that are not JavaScript or HTML, you’ll want to modify your sketch slightly to make things work smoothly. See below.

To upload your sketch files, go to the Content tab, then to any page within your cargo site (it doesn’t matter which). Expand the Images section, and click the document icon next to Library:

click this icon to add your sketch files

This will open a dialog. Click Upload a File and select all of your sketch files.

If using images, audio, or something else…

If your sketch includes some files that are not either JavaScript or HTML, you’ll want to upload those first, get the URL that Cargo associates with each one them, then tell your sketch to reference that URL.

First, upload the image/audio/whatever to Cargo.

Second, once it’s uploaded, it will appear below. Click it.

Third, copy the link from the dialog box that appears.

Fourth, go to your P5 sketch. Wherever you’re referencing that asset in the P5 sketch, replace that reference with the URL you just copied. Like so:

let image;
let sound;
function preload() {
image = loadImage("https://files.cargocollective.com/c111111/myimage.jpg");
sound = createAudio("https://files.cargocollective.com/c111111/mysong.mp3")
}

Repeat this step for any other assets that are in your sketch.

Finally, save your sketch files and upload them to Cargo using the process described above.

If you have multiple sketches on the same page…

In Seven Spaces, I had made two P5 sketches that I wanted to show on the same site. You can’t have upload two files with the same name, so you’ll need to change the names of index.html , sketch.js , and any other files that share the same name between the two sketches. One way to do this is to use a prefix. For example, you might change the name ofsketch.js to b.sketch.js:

Note: If you need to change the names of things, don’t forget to go into index.html and change the names of the file references there:

Another note: In the screen grab above, you can see that the HTML references multiple Javascript files (b.interactions.js, etc). I had broken out my P5 code into multiple files while I was programming because it made it easier to keep track of things. However, when you upload these to Cargo as separate files, you’ll ask your site to request each one separately, which can cause your sketches to fail to load. So I recommend merging all of your JS code into sketch.js, then upload only that file.

Step 2— Add iframe to page

While you’re looking at your uploaded files, copy the URLs associated with your index.html file(s) (see above) and paste them somewhere handy. You’ll need them in a second.

Now, within Cargo, go to the page on which you want to show your sketch.

Click the “code view” button in the lower-right hand corner of the editor panel. A dialog box will pop up. Paste this code into that box:

<iframe class="p5" src="https://files.cargocollective.com/c11111/index.html" frameborder="0" width="960" height="540">
</iframe>

Replace the URL associated with the src property to the URL that references your own P5 index.html file.

What this does is display your P5 sketch in a “window” within your Cargo page.

Set the width and height parameters to the largest size you would ever want your sketch to be on the page. Cargo will dynamically resize the iframe if the user is viewing the page through a smaller window.

Now, you should be able to refresh the page and see your sketch. If you’re happy with how it looks, you’re done. If you want a little bit more control over how it and other page elements position themselves, read on.

Step 3— Centering and resizing

On my site, my sketch is centered in the middle of the browser window, and the title of the sketch is positioned just above the left-hand border of the sketch.

Side note: Cargo has many different site templates that may behave in unexpected ways. But, for me, the solution described below worked well with a standard single-column template.

Here’s how it looks on my site:

I also wanted the everything to readjust if the user resizes the window after the page has loaded. By “readjust”, I mean that all of the objects (title, sketch, etc) resize and re-center, and the sketch reloads so that the internal canvas is resized to the new dimensions of the iframe.

Cargo automatically resizes the iframe as the window is moved, so all we have to do is center it. We’ll do this by left-aligning the elements in the Cargo page, then using themargin-left property of those elements to center them.

Centering and resizing

The first thing to do is make sure that everything is left-aligned in your Cargo design settings:

Next, click the Code View button in the lower-left hand of the editor. Here, we’ll configure the iframe to be able to be centered. I give the iframe an id, a class, and, using onload, declare a callback function to be invoked when the sketch has loaded. My page also has a the sketch’s title above it, and I want that element to be lined up with the sketch. So, I’ve included a div whose class name is title.

Here’s the result:

<div class="title" style="text-align: left;"><h3>206/754</h3></div><iframe id = "mysketch", class="p5" src="https://files.cargocollective.com/c700175/b.index.html" frameborder="0" width="960" height="540" onload="margin(['title','p5'],'p5')"></iframe>

Themargin() function is what centers the sketch to the user’s window. We’ll need to define this function. The best place to do it is in Cargo’s Custom HTML section. In Cargo, go to the Design tab, then scroll to the bottom and click Custom HTML. Paste the code below into the box and hit Save.

<script type="text/javascript">/**
* Set the margin values for several HTML elements to center them in the browser window
*
* @param _type {string} "class" or "id". find elements using class name or div id
* @param _els {array} array of element ids or class names to resize
* @param _widE {string} class name or id to find element used to determine resize width
*/
function margin(_type,_els,_widE) {

// if there's no _type argument, default to "class"
let type, els, widE, widEl;
if(_type !== "class" && _type !== "id") {
type = "class";
els = _type;
widE = _els;
} else {
type = _type;
els = _els;
widE = _widE;
}
// find the elements referenced by the _els argument
let elements = [];
for (let i = 0; i < els.length; i++) {
let l = [];
if(type === "id") {
l.push(document.getElementById(els[i]));
widEl = document.getElementById(widE);
} else {
l = document.getElementsByClassName(els[i]);
if(els[i] == widE) {
widEl = l[0]
}
}
elements.push(l);
}
// get width of browser window, width of sketch, and padding
let screenWidth = document.body.clientWidth;
let sketchWidth = dePx(widEl.style.width);
let style = getComputedStyle(document.querySelector('.content_padding'));
let pads = [dePx(style.paddingLeft), dePx(style.paddingRight)];
// calculate margin
let p = Math.floor((screenWidth - sketchWidth)*0.5 - pads[0]);
if (p < 0) p = 0;

// set margin-left property for referenced elements
let pcss = p+"px";
for (let j = 0; j < elements.length; j++) {
let ll = elements[j];
for (let k = 0; k < ll.length; k++) {
ll[k].style.marginLeft = pcss;
}
}
}
// convert string like "60px" to integer like 60
function dePx(str) {
return(parseInt(str.slice(0,-2)))
}
</script>

This function gets the window width and the sketch width and subtracts the latter from the former while adjusting for any padding that may already be set up. The first argument, _els, is expected to be an array. This allows you to pass in multiple class names and set the same left margin for all of the elements that match that class.

The sketch width is determined using the _widE argument. When margin() is invoked with only two arguments, _widE is a class name. margin() will find the first element that matches that class and use its width to determine the sketch width.

In the case above, when the P5 loads, it will tell margin() to set the left margin for all of the elements on the page with the class title or p5. It does so by getting the width of the P5 sketch that’s already there.

Note: that this solution will work if you have multiple P5 sketches on the same, but they all have to be the same width. If your sketches are different shapes and sizes, see below.

If you have multiple sketches of differing widths…

The margin() function has two modes: "id” mode and "class" mode. When in class mode (the default), margin() acts as described above: it uses class names and sets margins for all the elements in that class.

When in id mode, the _els attribute of margin() expects an array of element IDs rather than an array of class names. It will update the margins of the exact elements you specify. In id mode, the _widE argument also expects the ID of a specific element.

Here’s an example that uses id mode.

<div id = "sketch1Title" class="title" style="text-align: left;"><h3>206/754</h3></div><iframe id = "sktech1", class="p5" src="https://files.cargocollective.com/c700175/index.html" frameborder="0" width="960" height="540" onload="margin('id',['sketch1Title','sketch1'],'sketch1')"></iframe>

Note: class is the default mode if you use only two arguments.

// class mode
margin(['title','p5'],'p5');
margin('class',['title','p5'],'p5');
// id mode
margin('id',['sketch1Title','sketch1'],'sketch1');

Finally: responding to changes in window size

What we have so far will make sure that sketches and their associated elements are positioned correctly when the page loads. However, if the window is resized by the user after the page loads, things will fall off center. Also, the P5 sketches will display incorrectly because the canvas has been sizes using old windowWidth and windowHeight values.

To fix this, we add a few more things to Cargo’s Custom HTML (in the Design Tab). This will detect when the window is resized, and reload the P5 sketches, which will in turn triggermargin() due to the onload property of our iframes.

<script type="text/javascript">
// fires callback when user resizes the window
window.addEventListener('resize', resize);
// find p5 sketches and reload them
function resize() {
let sk = document.getElementsByClassName('p5');
for (let i = 0; i < sk.length; i++) {
sk[i].src = sk[i].src;
}
}
</script>

~thanks~

--

--

Responses (2)