Using React with p5.js (ES6+ support)

fts

Check the online version here: http://fractal-tree-simulator.surge.sh/
Source code is available here: https://github.com/atorov/fractal-tree-simulator

Using React with p5.js

This project demonstrates the possibility of combining React, Bootstrap, p5.js and other modern technologies.
React is one of the most popular JavaScript libraries for creating single page applications.
Bootstrap is an open source toolkit for developing with HTML, CSS, and JS.

The basic idea is that the p5.js sketch is wrapped in a React component. The data that comes into the sketch is passed on to this component as props. Callbacks are used to return information back from the sketch to the application.

ES6+ Support

This project supports a superset of the latest JavaScript standard. In addition to ES6 syntax features, it also supports:

  • Exponentiation Operator (ES2016).
  • Async/await (ES2017).
  • Object Rest/Spread Properties (stage 3 proposal).
  • Dynamic import() (stage 3 proposal)
  • Class Fields and Static Properties (part of stage 3 proposal).
    – JSX and Flow syntax.
  • Syntax Highlighting and Displaying Lint Output in the Editor
  • Тhe most popular editors should be covered and your editor should report the linting warnings.
  • Code Splitting - allows you to split your code into small chunks which you can then load on demand. Supports code splitting via dynamic import().

Adding a Router and Redux

React Router is the most popular option. Redux is a predictable state container for JavaScript apps. React Router and Redux can easily be added to the project.

Running Tests

Jest is a Node-based runner. While Jest provides browser globals such as window thanks to jsdom, they are only approximations of the real browser behavior. Jest is intended to be used for unit tests of your logic and your components rather than the DOM quirks.

p5.js sketch

  • p5.Graphics for creating a graphics buffer;

  • p5.Vector for describing a two dimensional vectors;

  • p5.noise() - Perlin noise random sequence generator that produces a more natural ordered, harmonic succession of numbers compared to the standard random() function;

  • p5.colorMode() - both RGB and HSB color spaces;

  • p5.translate() and p5.rotate() matrix transformations, etc.

9 Likes

A much more simpler example, based on the same concept, can be found here: http://react-p5js.surge.sh/
Source code is available here: https://github.com/atorov/react-p5js

2 Likes

UPDATE 2018-11-09 / REACT-P5JS version 0.3:

It was interesting year in the ReactJS world and most probability we will see more new features from the React team to the end of 2018.

Anyway, with so many new features introduced in React I had to make some refactoring and improvements to my react-p5js project. Here are some of the changes I made:

â—Ź Updated: Synchronized with the new React (16.3+) lifecycle methods;
â—Ź Updated: Improved and simplified communication between main React app and p5js sketches;
â—Ź New feature: Multiple independent p5js sketches on the same page;
● New feature: Splitting the sketch’s code into separate files.

React hooks are hot topic now but since they are still in alpha stage and just proposals so I’m planning to wait to be included to the stable release and once they are ready for production I may update the project again. Till then using React.Component classes is the best way to wrap p5js sketches and to avoid unnecessary re-rendering (at least according to me). In fact, even when the Hooks are officially included in the React, this approach will remain perfectly valid.

So, I hope the project is useful for anybody that wants to intercorporate these two great libraries – React and p5js – and don’t want to spend countless hours/days setting up the boilerplate code that is needed to start a new project. Enjoy :slight_smile:

http://react-p5js.surge.sh/
Source code: https://github.com/atorov/react-p5js

5 Likes

Is there a tutorial that you recommend as an intro to React? Any of those many tutorials out there that picked your attention?

Thanks for sharing!

Kf

1 Like

@kfrajer React is a hot topic now and there are tons of resources and tutorials on the Internet. You can start with some Udemy courses. Stephen Grider’s courses for example are good, this one for example: https://www.udemy.com/react-redux/

If you don’t afraid to read you should take a look at this free online book: https://survivejs.com/react/

Feel free to ask if you have any further questions.

2 Likes

UPDATE 2019-04-21 (React Hooks and React Context)

An example, build on the same concept but using the latest React features such as hooks and context), can be found here

4 Likes

Hi @atoro,

I’m learning React myself and was wondering how to combine p5js and React, this is just what I need.

Thank you very much for sharing this.

Cheers

Thanks @nardove!

Check this out too:
https://discourse.processing.org/t/p5js-react-web-workers-demo-app/11513
https://github.com/atorov/react-p5js-hypothermia-demo

1 Like

Awesome stuff @atoro
I’m currently researching how to do p5.js with React and your material comes timely.
Haven’t checkout your stuff yet but I wonder does your approach support other p5 libraries too? Eg: p5.sound?

Thanks!

@venn That’s an interesting question. The answer is - YES, it does support libraries such as p5.sound. I updated one of my repositories to illustrate the idea. Check this out:
https://github.com/atorov/react-hooks-p5js
http://react-hooks-p5js.surge.sh/
I would recommend to use this repo to start your experiments with React and p5js because it is based on the latest React features such as hooks and context.
screencast-2

1 Like

@atoro I’ll certainly give this a try. Given that I’m not super code savvy. I’m wondering if you’ll abstract all the complexity in the future and release it as a wrapper/lib similar to https://www.npmjs.com/package/react-p5-wrapper ?

@venn The original idea of Ivan Malyugin and its implementation as react-p5-wrapper NPM package by Andreas Wolf are great. However, I have some concerns about it. For example, it uses componentWillReceiveProps(). Since React 16.3 it has been replaced by UNSAFE_componentWillReceiveProps(). This lifecycle method is deprecated and will be removed from the next major release of React.

Anyway, the goal of react-hooks-p5js is different. This is a ready to use project with zero need of additional configurations.

You can use many cool features just out-of-the-box:

  • dynamic imports
  • code splitting
  • all modern ES6+ syntax improvements
  • the latest React features such as Hooks and the new Context API
  • multiple p5 sketches on the same screen
  • multiple instances of one sketch mounted in a same component
  • mount sketches in different points of the DOM tree
  • dynamically switch on/off sketches
  • app state management and bidirectional communication between the main React app and sketches

You don’t have to configure webpack, babel, eslint, and so on. All of them are already configured and if someone wants to customize them to better fit his/her needs just have to make small tweaks.

It is easy to add React Router, Redux or Jest to the project.

react-p5js-hypothermia-demo extends this idea and demonstrates how to use Web Workers.

Both p5js and react are great but combining them can be tricky. One reason for that is a possibility of collisions between React’s declarative programming approach and imperative one used by p5.

As I said in another post thread sometimes we don’t have to use React or another similar library and framework. Here is another example: ml-tfjs-fitting-curve, where only tensorflow.js and p5.js libraies are used.

2 Likes

I’m having some trouble getting this to play nicely with images overlaid on a canvas.

Code for reference, which displays only the background on the canvas:

var canvas = { width: 640, height: 600 }
var patterns = {
    random: { init: true, img: undefined },
    fern: { init: false, img: undefined },
}

export default function (s) {
    s.state = { }
    s.dispatch = () => { }

    s.setup = () => {
        patterns.fern.img = s.createImage(canvas.width, canvas.height)
        patterns.random.img = s.createImage(canvas.width, canvas.height)
        s.createCanvas(canvas.width, canvas.height)
        s.colorMode(s.HSB)
        s.pixelDensity(1)
        s.background(91)
        console.log('::: fern sketch has been initialized')
    }

    function drawPointFern(x, y, img) {
        let px = Math.round(s.map(x, -2.1820, 2.6558, 0, img.width))
        let py = Math.round(s.map(y, 0, 9.9983, img.height, 0))
        let index = (px + py * img.width) * 4;
        img.pixels[index + 0] = 0
        img.pixels[index + 1] = s.random(255)
        img.pixels[index + 2] = 0
        img.pixels[index + 3] = 255
    }

    function nextPointFern(x, y) {
        let nextX, nextY

        let r = s.random(1)

        if (r < 0.01) {
            nextX = 0
            nextY = 0.16 * y
        } else if (r < 0.86) {
            nextX = 0.85 * x + 0.04 * y
            nextY = -0.04 * x + 0.85 * y + 1.60
        } else if ( r < 0.93) {
            nextX = 0.20 * x + -0.26 * y
            nextY = 0.23 * x + 0.22 * y + 1.60
        } else {
            nextX = -0.15 * x + 0.28 * y
            nextY = 0.26 * x + 0.24 * y + 0.44
        }
        return [nextX, nextY]
    }

    function initFern(img) {
        img.loadPixels()
        let x=0, y=0
        for (let i=0; i<100000; i++) {
            drawPointFern(x, y, img)
            let nextP = nextPointFern(x, y)
            x = nextP[0]
            y = nextP[1]
        }
        img.updatePixels()
        patterns.fern.init = true
    }

    function drawFern(img) {
        img.loadPixels()
        img.updatePixels()
    }

    function drawRandomNextPoint(x, y, img) {
        img.set(x, y, x, s.random(255), y, 100)
        /*
        let index = (x + y * img.width) * 4;
        img.pixels[index + 0] = x
        img.pixels[index + 1] = s.random(255)
        img.pixels[index + 2] = y
        img.pixels[index + 3] = 100
        */
    }

    function drawRandom(img) {
        img.loadPixels()
        let x=0, y=0
        for (let i=0; i<s.height; i++) {
            for (let j=0; j<s.width; j++) {
                x = Math.round(s.random(s.width))
                y = Math.round(s.random(s.height))
                drawRandomNextPoint(x, y, img)
            }
        }
        img.updatePixels()
    }

    s.draw = () => {
        s.loadPixels()

        if (!patterns.fern.init) {
            initFern(patterns.fern.img)
        }

        drawRandom(patterns.random.img)
        s.image(patterns.random.img, 0, 0)

        /*
        drawFern(patterns.fern.img)
        s.image(patterns.fern.img, 0, 0)
        */

        s.updatePixels()
    }
}

Any ideas why images won’t display? Without images display works nicely, but then I can’t create the layering in the way that I would like. Separately, why are you using noLoop()?

Edit: My issue was the first and last lines in draw(). There is no need to load and update the pixels from the canvas. Doing so actually resets the full canvas image to the originally loaded pixel array, which in my case was blank.