Skip to content

Commit e60c6de

Browse files
authored
Add P5 constructor and plugin docs (#225)
1 parent 3918916 commit e60c6de

File tree

1 file changed

+140
-13
lines changed

1 file changed

+140
-13
lines changed

README.md

Lines changed: 140 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ Then just open `http://localhost:3001` in a browser.
5151

5252
### Javascript
5353

54-
```javascript
54+
```jsx
5555
import React from "react";
5656
import { ReactP5Wrapper } from "react-p5-wrapper";
5757

@@ -86,7 +86,7 @@ first and only argument.
8686

8787
#### Option 1: Declaring a sketch using the `P5CanvasInstance` type
8888

89-
```typescript
89+
```tsx
9090
import React from "react";
9191
import { P5CanvasInstance, ReactP5Wrapper } from "react-p5-wrapper";
9292

@@ -116,13 +116,13 @@ Using the `Sketch` type has one nice benefit over using `P5CanvasInstance` and
116116
that is that the `p5` argument passed to the sketch function is auto-typed as a
117117
`P5CanvasInstance` for you.
118118

119-
> Sidenote:
119+
> Side note:
120120
>
121-
> In general it comes down to personal preference as to how you declare your
121+
> In general, it comes down to personal preference as to how you declare your
122122
> sketches and there is nothing wrong with using the `P5CanvasInstance` manually
123123
> in a regular `function` declaration.
124124
125-
```typescript
125+
```tsx
126126
import React from "react";
127127
import { ReactP5Wrapper, Sketch } from "react-p5-wrapper";
128128

@@ -157,12 +157,12 @@ below, we create a custom internal type called `MySketchProps` which is a union
157157
type of `SketchProps` and a custom type which has a `rotation` key applied to
158158
it.
159159

160-
> Sidenote:
160+
> Side note:
161161
>
162162
> We could also write the `MySketchProps` type as an interface to do exactly the
163163
> same thing if that is to your personal preference:
164164
>
165-
> ```typescript
165+
> ```ts
166166
> interface MySketchProps extends SketchProps {
167167
> rotation: number;
168168
> }
@@ -174,7 +174,7 @@ correctly typed as a `number`.
174174
175175
##### Usage with the `P5CanvasInstance` type
176176
177-
```typescript
177+
```tsx
178178
import React, { useEffect, useState } from "react";
179179
import {
180180
P5CanvasInstance,
@@ -228,7 +228,7 @@ export function App() {
228228
229229
##### Usage with the `Sketch` type
230230

231-
```typescript
231+
```tsx
232232
import React, { useEffect, useState } from "react";
233233
import { ReactP5Wrapper, Sketch, SketchProps } from "react-p5-wrapper";
234234

@@ -278,7 +278,7 @@ export function App() {
278278

279279
### Using abstracted setup and draw functions
280280

281-
```javascript
281+
```jsx
282282
import React from "react";
283283
import { ReactP5Wrapper } from "react-p5-wrapper";
284284

@@ -329,7 +329,7 @@ wrapper are changed, if it is set within your sketch. This way we can render our
329329
`ReactP5Wrapper` component and react to component prop changes directly within
330330
our sketches!
331331

332-
```javascript
332+
```jsx
333333
import React, { useEffect, useState } from "react";
334334
import { ReactP5Wrapper } from "react-p5-wrapper";
335335

@@ -377,7 +377,8 @@ export function App() {
377377

378378
To render a component on top of the sketch, you can add it as a child of the
379379
`ReactP5Wrapper` component and then use the exported `P5WrapperClassName`
380-
constant in your to style one element above the other via css.
380+
constant in your css-in-js library of choice to style one element above the
381+
other via css.
381382

382383
For instance, using [styled components](https://styled-components.com), we could
383384
center some text on top of our sketch like so:
@@ -430,10 +431,136 @@ export function App() {
430431
}
431432
```
432433

433-
Of course you can also use any other css-in-js library or by just using simple
434+
Of course, you can also use any other css-in-js library or by just using simple
434435
css to achieve almost anything you can imagine just by using the wrapper class
435436
as your root selector.
436437

438+
## P5 plugins and constructors
439+
440+
As discussed in multiple issues such as
441+
[#11](https://github.com/P5-wrapper/react/issues/11),
442+
[#23](https://github.com/P5-wrapper/react/issues/23),
443+
[#61](https://github.com/P5-wrapper/react/issues/61) and
444+
[#62](https://github.com/P5-wrapper/react/issues/62), there seems to be
445+
confusion as to how we can use P5 plugins and constructors out of the box. This
446+
section aims to clarify these!
447+
448+
### Plugins
449+
450+
Since P5 is being used in
451+
[P5 instance mode](https://github.com/processing/p5.js/wiki/Global-and-instance-mode)
452+
as part of this project, P5 will not automatically load global plugins like it
453+
usually might in global mode.
454+
455+
Let's say we want to use the
456+
[P5 sound plugin](https://p5js.org/reference/#/libraries/p5.sound) in our
457+
component, we could do the following:
458+
459+
```tsx
460+
import * as p5 from "p5";
461+
import React, { useEffect, useState } from "react";
462+
import { ReactP5Wrapper, Sketch } from "react-p5-wrapper";
463+
464+
(window as any).p5 = p5;
465+
466+
await import("p5/lib/addons/p5.sound");
467+
468+
const sketch: Sketch = p5 => {
469+
let song: p5.SoundFile;
470+
let button: p5.Element;
471+
472+
p5.setup = () => {
473+
p5.createCanvas(600, 400, p5.WEBGL);
474+
p5.background(255, 0, 0);
475+
button = p5.createButton("Toggle audio");
476+
477+
button.mousePressed(() => {
478+
if (!song) {
479+
const songUrlPath = "/piano.mp3";
480+
song = p5.loadSound(
481+
songPath,
482+
() => {
483+
song.play();
484+
},
485+
() => {
486+
console.error(
487+
`Could not load the requested sound file ${songPath}`
488+
);
489+
}
490+
);
491+
return;
492+
}
493+
494+
if (!song.isPlaying()) {
495+
song.play();
496+
return;
497+
}
498+
499+
song.pause();
500+
});
501+
};
502+
503+
p5.draw = () => {
504+
p5.background(250);
505+
p5.normalMaterial();
506+
p5.push();
507+
p5.rotateZ(p5.frameCount * 0.01);
508+
p5.rotateX(p5.frameCount * 0.01);
509+
p5.rotateY(p5.frameCount * 0.01);
510+
p5.plane(100);
511+
p5.pop();
512+
};
513+
};
514+
515+
export default function App() {
516+
return <ReactP5Wrapper sketch={sketch} />;
517+
}
518+
```
519+
520+
In this Typescript + React example, we can see a few key things.
521+
522+
- Firstly we need to set `p5` on the `window` object manually. This is because
523+
`p5.sound` requires that it be executed client side only AND that `p5` be
524+
available BEFORE it is imported into the global (`window`) scope.
525+
- Secondly, we ensure that audio is played after a user action, in our case this
526+
happens on a button click. This is because in some browsers, without waiting
527+
for a user interaction before playing audio, the audio will be blocked by the
528+
browser from playing at all.
529+
- Thirdly and relevant especially to Safari users, Safari blocks audio from all
530+
tabs by default, you will need to manually change this setting in your Safari
531+
settings. This could affect other browsers but sadly this is a browser
532+
decision and until [P5 Sound](https://github.com/processing/p5.js-sound) is
533+
updated to support newer audio APIs and browser requirements. This could
534+
happen at anytime in other places and is a
535+
[P5 Sound](https://github.com/processing/p5.js-sound) issue most generally
536+
because it does not ask for permissions by default, even though browsers have
537+
been requiring it for some time.
538+
539+
> **Note:** The above example requires support for
540+
> [top level await](https://caniuse.com/mdn-javascript_operators_await_top_level),
541+
> [dynamic import statements](https://caniuse.com/es6-module-dynamic-import) and
542+
> [the stream API](https://caniuse.com/stream) to be supported in your browser.
543+
> Furthermore, [the stream API](https://caniuse.com/stream) built into the
544+
> browser requires that HTTPS is used to ensure secure data transmission.
545+
546+
### Constructors
547+
548+
To access P5 constructors such as `p5.Vector` or `p5.Envelope`, you need to use
549+
the instance mode syntax instead. For example:
550+
551+
| Constructor | Global mode accessor | Instance mode accessor |
552+
| ----------- | -------------------- | ----------------------- |
553+
| Vector | p5.Vector | p5.constructor.Vector |
554+
| Envelope | p5.Envelope | p5.constructor.Envelope |
555+
556+
So now that we know this, let's imagine we want a random 2D Vector instance. In
557+
our `sketch` function we would simply call `p5.constructor.Vector.random2D()`
558+
instead of `p5.Vector.random2D()`. This is because of how the
559+
[P5 instance mode](https://github.com/processing/p5.js/wiki/Global-and-instance-mode)
560+
was implemented by the P5 team. While I am not sure why they decided to change
561+
the API for instance mode specifically, it is still quite simple to use the
562+
constructs we are used to without much extra work involved.
563+
437564
## Development
438565

439566
**NOTE:** The source code for the component is in the `src` directory.

0 commit comments

Comments
 (0)