FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

processing/p5.js-compatibility: Add-on libraries that add p5.js 1.x features to p5.js 2.x for backwards compatibility · GitHub

Latest commit

 

History

100 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

p5.js v2 is 🌻 live 🌻

🆕 Teachers' Guide to p5.js v2: p5.js v2 Transition Guide for Middle School and High School Educators

p5.js-compatibility

The p5.js v2 library is now the default version on our website and in the editor. Although p5.js v1 remains available (reference at [hv1.p5js.org/][https://v1.p5js.org/]), it will not be maintained.

Between v1 and v2, there are many additions, and some breaking changes. Most sketches work in v2 directly. When sketches do not work, you can either update them (read p5.js v2 Transition Guide for Middle School and High School Educators for most common updates, or the rest of this README for the full list), or use one of the compatibility add-on libraries. These libraries make v1 features available in v2.

  1. preload.js

  2. shapes.js

  3. data.js

These add-on libraries are available in the p5.js Editor in the Settings > Library Management modal:

List of changes in p5.js API

These changes affect authoring of p5.js sketches. Read on for more information on how to transition, or how to use relevant compatibility addons.

  1. There's a new version of p5.sound.js. This new sound will work with all versions of p5.js (v1 and v2). The old p5.sound.js library, bundled with v1, will not work with v2. For a list of changes in the new p5.sound.js, skip to the p5.sound.js section
  2. Instead of bezierVertex(x1, y1, x2, y2, x3, y3) and quadraticVertex(x1, y1, x2, y2), use multiple bezierVertex(x, y) calls, and set order with bezierOrder (to revert to 1.x use, include shapes.js)
  3. Instead of curveVertex, use splineVertex - and expect endShape(CLOSE) to create a smoothly connected shape (for 1.x usage, include shapes.js)
  4. The previous usage of of textWidth(..) is now covered by fontWidth(..) in 2.x. Use textWidth(..) to measure text without space (tight bounding box). In 2.x, fontWidth(..) measures text width including trailing space.
  5. Instead of keyCode === UP_ARROW (and similar), use keyIsDown(UP_ARROW) (works in both versions) or code === UP_ARROW (works in 2.x). (to revert to 1.x use, include events.js)
  6. Instead of mouseButton === RIGHT (and similar), use mouseButton.right
  7. Instead of preload(), loadImage(...), loadJSON(...) and all other load... functions return promises can be used with async/await in setup() (or with callbacks) (to revert to 1.x use, include preload.js)
  8. Affecting only add-on libraries: read below how registerPreloadMethod can support both preload (1.x) and promises (2.x)
  9. Use JavaScript versions of the following functions, which have been removed in 2.x: createStringDict(), createNumberDict(), p5.TypedDict, p5.NumberDict, append(), arrayCopy(), concat(), reverse(), shorten(), sort(), splice(), subset() (to revert to 1.x use, include data.js)
  10. In 1.x, createVector() was a shortcut for createVector(0, 0, 0). In 2.x, p5.js has vectors of any dimension, so you must provide your desired number of zeros. Use createVector(0, 0) for a 2D vector and createVector(0, 0, 0) for a 3D vector - this will work in all versions.
  11. In 1.x, custom shaders would apply to different things (such as fills and strokes) based on what uniforms are present in the shader and what the current p5 state is (for example, a shader that does not read any p5 lighting state would be silently turned off if you draw a sphere with lights() applied.) In 2.x, shader(yourShader) will always apply your shader to fills, and the new strokeShader and imageShader functions can be used to apply a separate shader to strokes and image() calls.

Changes to make if your sketch includes...

...loading images, sound, fonts, and other assets (preload.js)

One of the biggest changes in 2.0 is involves how you can include other files, media, and assets. The p5.js 1.x style of using `preload()` does not reflect anymore how assets are loaded on the web, so p5.js 2.0 uses JavaScript’s async/await keywords to support asynchronicity.

If you’re interested in the history of async read on here!

To play around, check out the example from the p5.js 1.x preload() reference, but get a very big file instead of bricks.jpg.

p5.js 1.xp5.js 2.x
Blank “Loading…” screen, then image shownRed background while image loads
let img;

function preload() {
  img = loadImage('bricks.jpg');
}

function setup() {
  createCanvas(100, 100);

  // Red backgorund is ignored
  background(255, 0, 0);

  // Draw the image.
  image(img, 0, 0);

  describe('A red brick wall.');
}
let img;

async function setup() {
  createCanvas(100, 100);

  // Red background while asset loads
  background(255, 0, 0);

  // Wait for the image to load
  img = await loadImage('bricks.jpg');

  // Draw the image.
  image(img, 0, 0);

  describe('A red brick wall.');
}

If it takes a while to load the image, the sketch will be "paused" on the line img = await loadImage('/assets/bricks.jpg'); - once the image is loaded, it will resume.

Laslty, some loader functions have been updated:

All of the above usages in p5.js 1.x remain available with the preload.js compatibility add-on library.

...using registerPreloadMethod in an add-on libraries

Important notes for developers of p5.js add-on libraries who want to support both p5.js v1 and p5.js v2

Under the hood, returns a Promise from each loadImage, loadSound, and similar functions. Promises are widely used in JavaScript, so it is possible to use a callback in p5.js 1.x to create a Promise, but p5.js 1.x doesn't expect promises to be used, so you have to ensure yourself that, for example, your draw function doesn't start running before loading is done. For an example of a Promise using a callback, check out the example below that makes p5.sound.js compatible with both 1.x and 2.0:

If your add-on library built with p5.js 1.x uses registerPreloadMethod such as in this example from p5.sound.js:

p5.prototype.registerPreloadMethod('loadSound', p5.prototype);

Then to make your add-on library compatible with both p5.js 1.x (preload) and p5.js 2.0 (promises), this this line can be removed (the method loadSound, in this example, does not need to be registered) and the method can be updated as follows:

function loadSound (path) {
   if(self._incrementPreload && self._decrementPreload){
     // tTis is the check to determine if preload() is being used, as with
     // p5.js 1.x or with the preload compatibility add-on library. The function
     // returns the soundfile.
 
     self._incrementPreload();
 
     let player = new p5.SoundFile(
       path,
       function () {
         // The callback indicates to preload() that the file is done loading
         self._decrementPreload();
       }
     );
     return player;
 
   }else{
     // Otherwise, async/await is being used, so the function returns a promise,
     // which loads the soundfile asynchronously.

     return new Promise((resolve) => {
       let player = new p5.SoundFile(
         path,
         function () {
           // The callback resolves the promise when the file is done loading
           resolve(player);
         }
       );
     });
   }
 }

And that's it! You can check this example of making an add-on library backwards-compatible and work with p5.js 2.0 here: the p5.sound.js example

...making shapes (shapes.js)

If you use `vertex` and `bezierVertex` is the p5.js v1 code, here are the changes your code will need.

The below code is based on the custom shapes tutorial:

function setup() {
  createCanvas(windowWidth, windowHeight);
  background(100);
}
function draw() {
  translate(width/2, height/2);
  
  // Draw the curved star shape - use one of the snippets below, depending on p5.js version

  describe("A white star on a gray background in the middle of the canvas")
}
p5.js 1.xp5.js 2.x
// Draw the curved star shape.
beginShape();

// Original anchor at top.
vertex(0, -100);

// Top-right curve.
bezierVertex(0, -50, 50, 0, 100, 0);

// Bottom-right curve.
bezierVertex(50, 0, 0, 50, 0, 100);

// Bottom-left curve.
bezierVertex(0, 50, -50, 0, -100, 0);

// Top-left curve.
bezierVertex(-50, 0, 0,-50, 0,-100);
endShape();
// Draw the curved star shape.
beginShape();

// The default value is 3, 
bezierOrder(3);

// Original anchor at top.
bezierVertex(0, -100);

// Top-right curve.
bezierVertex(0, -50);
bezierVertex(50, 0);
bezierVertex(100, 0);

// Bottom-right curve.
bezierVertex(50, 0);
bezierVertex(0, 50);
bezierVertex(0, 100);

// Bottom-left curve.
bezierVertex(0, 50);
bezierVertex(-50, 0);
bezierVertex(-100, 0);

// Top-left curve.
bezierVertex(-50, 0);
bezierVertex(0, -50);
bezierVertex(0,-100);

endShape();
p5.js 1.xp5.js 2.x
// https://p5js.org/reference/p5/quadraticVertex/
function setup() {
  createCanvas(100, 100);

  background(200);

  // Start drawing the shape.
  beginShape();

  // Add the curved segments.
  vertex(20, 20);
  quadraticVertex(80, 20, 50, 50);
  quadraticVertex(20, 80, 80, 80);

  // Add the straight segments.
  vertex(80, 10);
  vertex(20, 10);
  vertex(20, 20);

  // Stop drawing the shape.
  endShape();

  describe('White puzzle piece on gray background.');
}
// Achieve the same curve with bezierOrder(2)
function setup() {
  createCanvas(100, 100);

  background(200);

  // Start drawing the shape.
  beginShape();

  bezierOrder(2);

  // Add the curved segments.
  bezierVertex(20, 20);
  bezierVertex(80, 20);
  bezierVertex(50, 50);
  bezierVertex(20, 80);
  bezierVertex(80, 80);

  // Add the straight segments.
  vertex(80, 10);
  vertex(20, 10);
  vertex(20, 20);

  // Stop drawing the shape.
  endShape();

  describe('White puzzle piece on gray background.');
}

The custom shapes tutorial has a bit more detail on this, but Bézier curves need multiple points. In p5.js 1.x, they use three control points. In p5.js 2.0, that number is set by bezierOrder. Then, in p5.js 1.x each bezierVertex(...) was actually a set of three points describing a smooth curve. In p5.js 2.0, each bezierVertext(x, y) is just one point; you need the first point to anchor, and each curve after that needs 3 points.

Additional shanges to shapes in p5.js 1.x, compared to p5.js 2.0, are as follows:

  • Name changes - functionality stays the same, but the functiona is more consistently named:
    • curveVertex() renamed to splineVertex()
    • curvePoint() renamed to splinePoint()
    • curveTangent() renamed to splineTangent()
    • curve() renamed to spline()
    • curveTightness(t) renamed to splineProperty('tightness', t)
  • Geometry cleanup
    • p5.js 1.x has functionality in beginGeometry(), endGeometry() that is covered in buildGeometry()
    • p5.js 2.0 only keeps buildGeometry()
  • Sampling detail cleanup
    • p5.js 1.x has separate curveDetail() and bezierDetail()
    • p5.js 2.0 uses curveDetail() to cover both, as the more general function
  • Defaults updated: in p5.js 1.x, endContour() is the same as endContour(CLOSE)

Finally, the behavior of endShape allows creating different shapes.

p5.js 1.x

p5.js 2.x

function setup() {
  createCanvas(100, 100);
  background(200);
  beginShape();

  // Add the first control point.
  curveVertex(32, 91);
  curveVertex(32, 91);

  // Add the anchor points.
  curveVertex(21, 17);
  curveVertex(68, 19);

  // Add the second control point.
  curveVertex(84, 91);
  curveVertex(84, 91);

  // Stop drawing the shape.
  endShape(CLOSE);
}
function setup() {
  createCanvas(100, 100);
  background(200);
  beginShape();

  // Control points not needed
  // if the goal is a smooth close
  // splineVertex(32, 91);
  splineVertex(32, 91);

  // Add the anchor points.
  splineVertex(21, 17);
  splineVertex(68, 19);

  // Second control point also excluded
  // splineVertex(82, 91);
  splineVertex(82, 91);
  endShape(CLOSE);
}

All of the above usages in p5.js 1.x remain available with the shapes.js compatibility add-on library.

...using fontWidth()

In p5.js 2.x, there are two ways to measure text: [fontWidth(...)](https://p5js.org/reference/p5/fontwidth/) and [textWidth(...)](https://p5js.org/reference/p5/textwidth/). In 2.x, `textWidth()` calculates the text's tight bounding box, which is what p5.js 1.x `fontWidth()` does.
p5.js 1.x p5.js 2.x
function setup() {
  createCanvas(100, 100);
  let s = "    Hello    ";
  console.log(textWidth(s));
  // Measurement that includes the spaces
}
function setup() {
  createCanvas(100, 100);
  let s = "    Hello    ";
  console.log(fontWidth(s));
  // Measurement that includes the spaces
}

No equivalent

function setup() {
  createCanvas(100, 100);
  let s = "    Hello    ";
  console.log(textWidth(s));
  // Measurement does not include
  // leading/trailing spaces
}

...using data structures and functions that have improved alternatives (data.js)

One big change relates to data structures in JavaScript. The following funcitons have been removed in p5.js v2.

These were originally in p5.js v1 because, historically, they were also in Processing. However, p5.js is a JavaScript library, and JavaScript objects and key-value maps can be used instead of these functions:

  • createStringDict()
  • createNumberDict()
  • p5.TypedDict
  • p5.NumberDict

Instead of the above functions, we would recommend using built-in JavaScript Objects

The below functions are also better supported in JavaScript itself:

  • append()
  • arrayCopy()
  • concat()
  • reverse()
  • shorten()
  • sort()
  • splice()
  • subset()

All of the above usages in p5.js 1.x remain available with the data.js compatibility add-on library.

...using mouseButton events

In v1, where the `mouseButton` was a single variable that could have values `left`, `right` and `center`, we cannot detect if the `left` and `right` button have been pressed together. In v2, the `mouseButton` is now an object with properties: `left`, `right` and `center`, which are booleans indicating whether each button has been pressed respectively. This means that we can now detect if multiple buttons are pressed together (like if the `left` and `right` button are pressed together).
function setup() {
  createCanvas(100, 100);

  describe(
    "A gray square. Different shapes appear at its center depending on the mouse button that's pressed."
  );
}
p5.js 1.xp5.js 2.x
function draw() {
  background(200);
  fill(255, 50);

  if (mouseIsPressed === true) {
    if (mouseButton === LEFT) {
      circle(50, 50, 50);
    }
    if (mouseButton === RIGHT) {
      square(25, 25, 50);
    }
    if (mouseButton === CENTER) {
      triangle(23, 75, 50, 20, 78, 75);
    }
  }
}
function draw() {
  background(200);
  fill(255, 50);

  if (mouseIsPressed === true) {
    if (mouseButton.left) {
      circle(50, 50, 50);
    }
    if (mouseButton.right) {
      square(25, 25, 50);
    }
    if (mouseButton.center) {
      triangle(23, 75, 50, 20, 78, 75);
    }
  }
}

Notice that when you press multiple buttons at the same time, multiple shapes can be obtained.

Finally, touch and mouse event handling has been combined to improve sketch consistency across devices. In p5.js 2.0, instead of having separate methods for mouse and touch, we now use the browser's pointer API to handle both simultaneously. Try defining mouse functions as usual and accessing the global touches array to see what pointers are active for multitouch support!

p5.js 1.xp5.js 2.x
  • touchStarted()
  • touchEnded()
  • touchMoved()
// On a touchscreen device, touch the canvas using one or more fingers
// at the same time.

function setup() {
  createCanvas(100, 100);

  describe(
    'A gray square. White circles appear where the user touches the square.'
  );
}

function draw() {
  background(200);

  // Draw a circle at each touch point.
  for (let touch of touches) {
    circle(touch.x, touch.y, 40);
  }
}

...using keyCode events:

We recommend using `keyIsDown(...)` in both v1 and v2 The sketch below works in both versions, but try to use it while quickly pressing different arrow keys - you will notice that the event handling in p5.js 2.x is smoother:
let x = 50;
let y = 50;

function setup() {
  createCanvas(100, 100);

  background(200);

  describe(
    'A gray square with a black circle at its center. The circle moves when the user presses an arrow key. It leaves a trail as it moves.'
  );
}

function draw() {
  // Update x and y when arrow keys are pressed
  // Using keyIsDown() will work in all version of p5.js
  if (keyIsPressed) {
    if (keyIsDown(UP_ARROW)) {
      y -= 1;
    } else if (keyIsDown(DOWN_ARROW)) {
      y += 1;
    } else if (keyIsDown(LEFT_ARROW)) {
      x -= 1;
    } else if (keyIsDown(RIGHT_ARROW)) {
      x += 1;
    }
  }
  
  // Style the circle.
  fill(0);

  // Draw the circle at (x, y).
  circle(x, y, 5);
}

Although keyIsDown() can be used with system-level constants like UP_ARROW in all versions, the below shows a bit of what has changed behind the scenes - so if you use key or code in your sketch, you may need to update your code:

p5.js 1.xp5.js 2.x
function draw() {
  // Update x and y when arrow keys are pressed
  if (keyIsPressed === true) {
    if (keyCode === UP_ARROW) {
      y -= 1;
    } else if (keyCode === DOWN_ARROW) {
      y += 1;
    } else if (keyCode === LEFT_ARROW) {
      x -= 1;
    } else if (keyCode === RIGHT_ARROW) {
      x += 1;
    }
  }

  // Style the circle.
  fill(0);

  // Draw the circle at (x, y).
  circle(x, y, 5);
}
function draw() {
  // Update x and y when arrow keys are pressed
  if (code === UP_ARROW) {
    y -= 1;
  } else if (code === DOWN_ARROW) {
    y += 1;
  } else if (code === LEFT_ARROW) {
    x -= 1;
  } else if (code === RIGHT_ARROW) {
    x += 1;
  }

  // Style the circle.
  fill(0);

  // Draw the circle at (x, y).
  circle(x, y, 5);
}

keyCode is still a Number system variable in 2.x.

if (keyCode === 13) {
  // Code to run if the enter key was pressed.
}

In 1.x system variables could be used using keyCode

if (keyCode === "ENTER") {
  // Code to run if the enter key was pressed.
}

Instead, in 2.x you can use the key or code function to directly compare the key value.

Using key:

if (key === 'Enter') {
  // Code to run if the Enter key was pressed.
}

Using code:

if (code === 'KeyA') { 
  // Code to run if the 'A' key was pressed.
}

Both numeric and string key codes can be found at keycode.info.

p5.sound.js Compatibility

The new p5.sound.js includes some breaking changes. Many of these changes include the removal of classes that we deemed redundant when considering the existence of [Tone.js](https://tonejs.github.io/). You can read more about this decision here: [Announcing the New p5.sound.js Library](https://medium.com/processing-foundation/announcing-the-new-p5-sound-js-library-42efc154bed0).

If you want to use any of the deprecated classes or features, you may find the original p5.sound library in the editor (with any of the v1 versions) and the original reference docs here: https://v1.p5js.org/reference/p5.sound/

Examples

A collection of examples using the 2.0 library can be found in the following places.

  1. p5 Website examples: https://p5js.org/examples/
  2. Simple Melody example: https://p5js.org/tutorials/simple-melody-tutorial
  3. In the reference documentation for individial p5.sound classes and methods here: https://p5js.org/reference/p5.sound/
  4. In the /examples folder of the p5.sound.js GitHub repository.

List of changes in p5.sound.js API

Most things are the same between p5.sound.js 1.x and 2.0, but there are some big differences: For starters, p5.sound.js 2.0 has fewer classes than the original library. There is also a more streamlined API (fewer redundancies, etc...). If you need access to some of the deprecated classes such as p5.MonoSynth() for example, use Tone.js. For more information on these changes read the announcement for the new p5.sound.js library here.

...loading sounds with loadSound

One of the biggest changes in the new p5.sound.js library is how we handle loading sound files. Instead of loading a sound file in the preload() function, the loadSound(...) function returns a promise in an async/await in setup() (or with callbacks) (to revert to 1.x use, include preload.js)

For example:

let sound;

async function setup() {
  sound = await loadSound('path/to/soundFile');
  
  createCanvas(100, 100);
  describe(
    'A gray square with text that reads "click to play the sound".');
  textWrap(WORD)
  textAlign(CENTER)
}

function draw() {
  background(200);
  text('click to play the sound', 0, 20, width);
}

function mousePressed() {
  sound.play();
}

Deprecated Classes

When using a deprecated class such as MonoSynth, EQ, Convolver, Distortion, OnsetDetect, Filter, Effect, Compressor, AudioVoice, Part, Phrase, PolySynth, Pulse, Score, SoundLoop you will see an alert that tells you that the class 'is deprecated' and to 'Try using the equivalent Tone.js class'.

To combine p5.sound.js and Tone.js nodes, you will have to pass the p5.sound.js AudioContext to Tone.js. You can do this like so:

<!-- include Tone.js after the p5.sound.js library-->
 <script src="p5.sound.js"></script>
 <script src="https://cdn.jsdelivr.net/npm/tone@15.0.2/build/Tone.js"></script>
let sound_location = 0
let panner, synthy

function setup() {
  createCanvas(400, 400)

  // Tone.js is loaded from its own script tag. Hand it p5.sound's audio
  // context before making any Tone.js object, and the two libraries build on
  // one context, so their nodes can be connected to each other.
  Tone.setContext(getAudioContext())

  synthy = new Tone.MonoSynth()
  panner = new p5.Panner()

  // connect a Tone.js audio node to a p5 sound effect
  panner.setInput(synthy)

  describe('A grey sketch that plays a Tone.js synth through a p5.sound panner. Click to play a note at a random stereo position.')
}

function draw() {
  background(220)
  text("sound is here", ((sound_location + 1) * 0.5 ) * width, height/2)
}

function mousePressed() {
  sound_location = random(-1,1)
  panner.pan(sound_location)
  synthy.triggerAttackRelease("D#5", (1.5))
}

List of Breaking Changes

Here is the spreadsheet that lists the deprecated clases and methods.

List of Changes

...custom shaders

In most cases, nothing needs to change! However, if your sketch relied on p5 automatically turning off a shader, you may need to manually scope your shader between a `push` and `pop`.

For example, if you had a shader that did not use p5's lighting system, in 1.x, it would stop applying once you turn on lighting:

shader(myShaderWithoutLighting);
sphere(100); // Draws with your shader

translate(100, 0);
lights();
sphere(100); // Draws without your shader

In 2.x, you would need to manually contain the shader to the first shape:

push();
shader(myShaderWithoutLighting);
sphere(100);
pop(); // Resets the shader

translate(100, 0);
lights();
sphere(100);

Alternatively, you can use resetShader:

shader(myShaderWithoutLighting);
sphere(100);
resetShader(); // Resets the shader

translate(100, 0);
lights();
sphere(100);

About

Add-on libraries that add p5.js 1.x features to p5.js 2.x for backwards compatibility

Resources

Stars

18 stars

Watchers

5 watching

Forks

Releases

Packages

Used by

Contributors

Languages


Back | FazBrowse Home | New Git URL