Migrate from p5-svelte

API mapping and ten-minute port.

Migrating from p5-svelte

If you're on p5-svelte (last released 3.1.2 in 2022, Svelte 3 era) and want to switch, the port is usually ten minutes per component.

#Why move

  • Svelte 5 native. Runes, $bindable, $effect.
  • <P5Canvas> calls instance.remove() on unmount, so {#if} toggles, route transitions, and HMR don't accumulate p5 instances. See the cleanup recipe for what that means in practice and the perf comparison for numbers.
  • Active maintenance: conventional commits, release-please, dependabot.
  • Performance utilities (disableFES, createColorCache, createFontAtlas) included.
  • Optional higher-level layer (auto-resize, FPS, draggable windows) in svelte-p5-components.

#Install

bun remove p5-svelte
bun add svelte-p5
# optional:
bun add svelte-p5-components

#API mapping

p5-svelte svelte-p5 Notes
import P5 from 'p5-svelte' import { P5Canvas } from 'svelte-p5' Named export. The single-letter P5 was collision-prone.
<P5 {sketch} /> <P5Canvas {sketch} /> Same shape, minus the legacy props.
<P5 {sketch} on:instance> <P5Canvas {sketch} bind:instance /> Event dispatcher replaced by a bindable prop.
<P5 on:ref> bind:this on a wrapping element You already have the DOM ref through Svelte.
parentDivStyle style (or class) Same behaviour, shorter name.
target + action workaround Normal Svelte DOM flow The component mounts into the div it renders.
debug Removed Was a diagnostic console.log. Use DevTools.

#Minimal port

Before:

svelte
<script>
	import P5 from 'p5-svelte';

	let p5Instance;

	const sketch = (p5) => {
		p5.setup = () => p5.createCanvas(400, 400);
		p5.draw = () => {
			p5.background(240);
			p5.circle(p5.mouseX, p5.mouseY, 40);
		};
	};
</script>

<P5 {sketch} on:instance={(e) => (p5Instance = e.detail)} />

After:

svelte
<script lang="ts">
	import { P5Canvas } from 'svelte-p5';
	import type p5 from 'p5';

	let p5Instance = $state<p5 | null>(null);

	const sketch = (p: p5) => {
		p.setup = () => p.createCanvas(400, 400);
		p.draw = () => {
			p.background(240);
			p.circle(p.mouseX, p.mouseY, 40);
		};
	};
</script>

<P5Canvas {sketch} bind:instance={p5Instance} />

#State: replacing manual subscription

p5-svelte focused on the canvas wrapper itself, leaving state management to the consumer (typically writable stores). svelte-p5 ships createP5Bridge for the same job, plus reactive class patterns for shared state.

Before:

svelte
<script>
	import P5 from 'p5-svelte';
	import { writable } from 'svelte/store';

	const radius = writable(40);
	let currentRadius = 40;
	radius.subscribe((v) => (currentRadius = v));

	const sketch = (p5) => {
		p5.draw = () => p5.circle(100, 100, currentRadius);
	};
</script>

<P5 {sketch} />
<input type="range" bind:value={$radius} />

After:

svelte
<script lang="ts">
	import { P5Canvas, createP5Bridge } from 'svelte-p5';
	import type p5 from 'p5';

	const bridge = createP5Bridge({ radius: 40 });

	const sketch = (p: p5) => {
		p.draw = () => p.circle(100, 100, bridge.state.radius);
	};
</script>

<P5Canvas {sketch} />
<input type="range" bind:value={bridge.state.radius} />

No subscribe, no mirror variable.

#What you should notice after migrating

Detached-canvas counts stay flat on {#if} toggles, HMR no longer accumulates canvases, and route transitions with a <P5Canvas> inside release cleanly. You don't do anything to get this; the wrapper handles it.

#Dependency version

p5-svelte peered on p5 ^1.4. This library peers p5 >=1.11.0 <3. If you're on an older p5 minor, bump to 1.11.x for the latest bugfixes. p5 2.x support lands in a later release.

#What migrating doesn't change

Your sketch code is unchanged. p.setup, p.draw, p.loadFont, every p5 API call ports 1:1.

The loadFont() perf trap isn't a library issue either. Variable-font path rendering is slow in p5 regardless of the wrapper. See performance.md for the full treatment.

SSR is the same story. <P5Canvas> dynamically imports p5 on the client, so SvelteKit SSR works without {#if browser} guards.

#Stuck?

Open an issue with your before/after and I'll help.

Send feedback

Opens a pre-filled GitHub issue with the page URL and your browser. You'll review before submitting.
Continue on GitHub