Generative art is a captivating discipline where the artist doesn't create the final piece directly. Instead, they write a set of rules—a program—and let the computer generate the art for them. The results can be unpredictable, complex, and beautiful.
p5.js is a beloved JavaScript library that makes this accessible to everyone. It's a "sketchbook" for the web, providing simple functions to draw shapes, handle colors, and respond to user input, all on an HTML canvas.
React, on the other hand, is a library for building user interfaces. It works by describing a declarative UI ("what the UI should look like"), and it manages its own "virtual" DOM.
The challenge is clear: p5.js is imperative—it wants to draw directly to the screen (createCanvas(), ellipse(), background()). React is declarative and hates when other things mess with the DOM it's supposed to be managing.
So how do we make them work together? We need to build a "bridge" or a "wrapper" component. This component will do two things:
Create a stable div in React's DOM for p5.js to "own."
Use React's lifecycle hooks (useEffect) to initialize and clean up the p5.js sketch, keeping it contained.
While there are libraries like react-p5 that do this for you, building it yourself once is the best way to understand exactly what's happening.
Here's how to build a simple, reusable p5.js wrapper component.
Step 1: Create the Wrapper Component
Create a new file, P5Sketch.jsx. We'll use useRef to get a direct reference to a DOM element, and useEffect to manage the p5.js lifecycle.
JavaScript
import React, { useRef, useEffect } from 'react';
import p5 from 'p5';
const P5Sketch = ({ sketch }) => {
const sketchRef = useRef(null);
useEffect(() => {
const p5Instance = new p5(sketch, sketchRef.current);
return (). => {
p5Instance.remove();
};
}, [sketch]);
return <div ref={sketchRef} />;
};
export default P5Sketch;import React, { useRef, useEffect } from 'react';
import p5 from 'p5';
const P5Sketch = ({ sketch }) => {
const sketchRef = useRef(null);
useEffect(() => {
const p5Instance = new p5(sketch, sketchRef.current);
return (). => {
p5Instance.remove();
};
}, [sketch]);
return <div ref={sketchRef} />;
};
export default P5Sketch;import React, { useRef, useEffect } from 'react';
import p5 from 'p5';
const P5Sketch = ({ sketch }) => {
const sketchRef = useRef(null);
useEffect(() => {
const p5Instance = new p5(sketch, sketchRef.current);
return (). => {
p5Instance.remove();
};
}, [sketch]);
return <div ref={sketchRef} />;
};
export default P5Sketch;
Step 2: Write Your First "Sketch"
A p5.js sketch is just a function that takes one argument, typically called p (for "p5"). This p object contains all the p5.js drawing functions. The sketch must have at least a setup function and, for animations, a draw function.
Let's make a file for our sketch, MySketch.js.
JavaScript
export const sketch = (p) => {
p.setup = () => {
p.createCanvas(400, 400);
};
p.draw = () => {
p.background(10, 10, 20);
p.fill(255, 200, 0);
p.noStroke();
const diameter = 100 + p.sin(p.frameCount * 0.05) * 50;
p.ellipse(p.width / 2, p.height / 2, diameter, diameter);
};
};
export const sketch = (p) => {
p.setup = () => {
p.createCanvas(400, 400);
};
p.draw = () => {
p.background(10, 10, 20);
p.fill(255, 200, 0);
p.noStroke();
const diameter = 100 + p.sin(p.frameCount * 0.05) * 50;
p.ellipse(p.width / 2, p.height / 2, diameter, diameter);
};
};
export const sketch = (p) => {
p.setup = () => {
p.createCanvas(400, 400);
};
p.draw = () => {
p.background(10, 10, 20);
p.fill(255, 200, 0);
p.noStroke();
const diameter = 100 + p.sin(p.frameCount * 0.05) * 50;
p.ellipse(p.width / 2, p.height / 2, diameter, diameter);
};
};
Step 3: Use Your Sketch in Your App
Now, in your App.js or any other component, you can import and use your wrapper and your sketch.
JavaScript
import React from 'react';
import P5Sketch from './P5Sketch';
import { sketch } from './MySketch';
function App() {
return (
<div style={{ display: 'grid', placeItems: 'center', minHeight: '100vh' }}>
<h1>My First p5.js Sketch in React</h1>
<P5Sketch sketch={sketch} />
</div>
);
}
export default App;import React from 'react';
import P5Sketch from './P5Sketch';
import { sketch } from './MySketch';
function App() {
return (
<div style={{ display: 'grid', placeItems: 'center', minHeight: '100vh' }}>
<h1>My First p5.js Sketch in React</h1>
<P5Sketch sketch={sketch} />
</div>
);
}
export default App;import React from 'react';
import P5Sketch from './P5Sketch';
import { sketch } from './MySketch';
function App() {
return (
<div style={{ display: 'grid', placeItems: 'center', minHeight: '100vh' }}>
<h1>My First p5.js Sketch in React</h1>
<P5Sketch sketch={sketch} />
</div>
);
}
export default App;That's it! You now have a pulsating generative art piece running inside your React application. You can pass props to your P5Sketch wrapper to make your art interactive, responding to React's state. This combination gives you the best of both worlds: the robust component model of React and the creative, visual freedom of p5.js.