Skip to content

Instantly share code, notes, and snippets.

@mattdesl
Last active October 3, 2021 21:32
Show Gist options
  • Save mattdesl/768d2b016b325389b2562e9a06d8890a to your computer and use it in GitHub Desktop.
Save mattdesl/768d2b016b325389b2562e9a06d8890a to your computer and use it in GitHub Desktop.
svelte musings

Reactive & Editable p5.js Sketches

Demo: https://svelte.dev/repl/93a34e18da1d44b29283eb0d1e293eb5?version=3.19.2

This is a proof-of-concept showing how to integrate flexible UI components for a p5.js sketch. This uses Svelte.

Goals

A framework that allows us to write simple p5.js sketches with:

  • An easy way to add in GUI sliders, color pickers, etc
  • Possibility to build your own UI sliders/tooling on a per-project basis
  • Reactivity builtin, so that binding UI to variables is easy
  • The possibility of more advanced features in a bespoke tool

Examples

See the two files below:

  • SimpleSketch.svelte — just a simple example showing a UI binding
  • ComplexSketch.svelte — a more advanced example decoupling one UI change from another

Next Steps

The next step is to try and wrap this within a tool/UI interface so that it isn't confined to the Svelte REPL.

Future Tooling

This is testing some ideas and musings on ways to improve creative coding tools within JavaScript. The goal is to maintain a low barrier to entry while supporting more flexible and advanced use cases.

For example, the above could be simplified and made into a more purpose-built tool:

// a helper to allow p5 functions/variables as if in global mode
@with(p5)

// a helper that binds the variable to a new color picker UI
@Color()
export let color = 'red';

@Slider({ min: 0, max: 1, step: 0.01 })
export let radius = 0.5;

export function draw () {
  background(color);
  circle(width / 2, height / 2, radius * min(width, height));
}
<script>
import delaunay from 'delaunay-triangulate';
import { Slider, Color } from 'texel/ui';
// This will get passed in with the P5 instance
export let p5;
// Size of the canvas in pixels
export let width = 256;
export let height = 256;
// A user prop that causes a re-render
export let color = '#ffffff';
// We only want to re-triangulate when point count changes
// (but not when color changes)
export let pointCount = 10;
export let points = [];
export let cells;
// Generate the points initially on setup
generate(pointCount)
// And then again whenever pointCount changes
$: generate(pointCount);
function generate (n) {
points = new Array(n).fill(0).map(() => {
const x = p5.random(0, 1);
const y = p5.random(0, 1);
return [ x, y ];
});
cells = delaunay(points);
}
export function setup () {
p5.createCanvas(width, height);
p5.noLoop();
}
export function draw () {
p5.background('black');
const positions = points.map(p => {
return [ p[0] * width, p[1] * height ];
});
cells.forEach(cell => {
const trianglePoints = cell.map(i => positions[i]);
p5.stroke(color);
p5.noFill();
p5.triangle(...trianglePoints.flat());
});
positions.forEach(point => {
p5.fill(color);
p5.noStroke();
p5.circle(point[0], point[1], 5);
});
}
</script>
<!-- Add in your UI panels below -->
<Color label='Line Color' bind:value={color} />
<Slider label='Vert Count' min={0} max={100} step={1} bind:value={pointCount} />
<script>
// Import some UI
import { Slider } from 'texel/ui';
// This will get passed in with the current P5 instance
export let p5;
// Size of the canvas in pixels
export let width = 256;
export let height = 256;
// A reactive prop/variable
export let radius = 0.5;
// Setup a static artwork
export function setup () {
p5.createCanvas(width, height);
p5.noLoop();
}
// Render it
export function draw () {
p5.background('black');
const diameter = radius * p5.min(width, height);
p5.circle(width / 2, height / 2, diameter);
}
</script>
<!-- Wire up the GUI to your props -->
<Slider label='Radius' bind:value={radius} />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment