Dan
Dan

Reputation: 2725

P5 with react at 60 FPS

To get P5 to work with React, I am using the P5Wrapper import.

I got a simple starfield animation to work on my tile, but the performance is an issue. The animation slows to a crawl at 512 "star" objects, so I scaled it back to 128. However, even at 128, the FPS seems much too low, averaging below 30 FPS. I am looking for ways to speed up P5's performance in React so that the animations can run closer to 60 FPS.

P5 code:

function sketch (p) {

  const star = () => {
    const x = p.random(-TILE_SIZE/2, TILE_SIZE/2)
    const y = p.random(-TILE_SIZE/2, TILE_SIZE/2)
    const z = p.random(TILE_SIZE)
    return { x, y, z }
  }

  const stars = new Array(128)

  p.setup = () => {
    p.createCanvas(TILE_SIZE, TILE_SIZE)

    for (let i = 0; i < stars.length; i++) {
      stars[i] = star()
    }
  }

  const update = (coord) => {
    const { z } = coord
    let newZ = z - 8
    if (newZ < 1) {
      newZ = p.random(TILE_SIZE)
    }
    return { ...coord, z: newZ }
  }

  const show = (coord) => {
    const { x, y, z } = coord
    p.fill(255)
    p.noStroke()

    const sx = p.map(x / z, 0, 1, 0, TILE_SIZE)
    const sy = p.map(y / z, 0, 1, 0, TILE_SIZE)

    const r = p.map(z, 0, TILE_SIZE, 4, 0)
    p.ellipse(sx, sy, r, r)
  }

  p.draw = () => {
    p.background(0)
    p.translate(TILE_SIZE/2, TILE_SIZE/2)
    for (let i = 0; i < stars.length; i++) {
      stars[i] = update(stars[i])
      show(stars[i])
    }
  }
}

How P5Wrapper is used:

import P5Wrapper from 'react-p5-wrapper'

...
render (
 <ItemContainer key={uuidv4()}>
   <header>
     {name}
     <p>{description}</p>
   </header>
   <P5Wrapper sketch={sketch} />
 </ItemContainer>
)

Sample of performance

How the starfield tile actually looks (2 tiles).

enter image description here

I am planning to add more animations depending on performance. Or switching to SVG.

Upvotes: 0

Views: 1546

Answers (1)

Dan
Dan

Reputation: 2725

Resolved the frame-rate issue without changing the actual animation logic. There could still be plenty of performance optimization that is still needed.

I noticed that the animation gets progressively slower as I un-mount and re-mount the component. Digging into the Github issue results in this post about performance degradation. The poster provided a PR and commit that was never merged and the repository haven't been updated for over a year.

Instead, it's better to remove the package and just create your own component following the poster's update:

import React from 'react'
import p5 from 'p5'

export default class P5Wrapper extends React.Component {
  componentDidMount() {
    this.canvas = new p5(this.props.sketch, this.wrapper)
    if( this.canvas.myCustomRedrawAccordingToNewPropsHandler ) {
      this.canvas.myCustomRedrawAccordingToNewPropsHandler(this.props)
    }
  }

  componentWillReceiveProps(newprops) {
    if(this.props.sketch !== newprops.sketch){
      this.canvas.remove()
      this.canvas = new p5(newprops.sketch, this.wrapper)
    }
    if( this.canvas.myCustomRedrawAccordingToNewPropsHandler ) {
      this.canvas.myCustomRedrawAccordingToNewPropsHandler(newprops)
    }
  }

  componentWillUnmount() {
    this.canvas.remove()
  }

  render() {
    return <div ref={wrapper => this.wrapper = wrapper}></div>
  }
}

This at the very least resolved the performance degradation issue for mounting and unmounting the component. Additionally, my frames have jumped from sub 30 to nearly 60 FPS. This could be because I also imported the latest P5 package since I no longer rely on react-p5-wrapper to do the imports.

enter image description here

Upvotes: 3

Related Questions