Skip to content
106 changes: 106 additions & 0 deletions contributor_docs/p5.svg.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<!-- An overview of the goals of p5's native SVG export, import, and vector shape recording system. -->

# p5.svg Overview

`p5.svg` is an experimental native vector graphics system provided in p5.js starting from version 2. It aims to bring resolution-independent vector rendering, SVG file importing, and SVG XML exporting directly into p5.js without requiring external third-party addons. It allows users to record 2D drawing operations using familiar p5 APIs (`rect`, `circle`, `path`, `fill`, `stroke`, `translate`, `rotate`, etc.) and convert them into scalable vector structures.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: from version 2.4

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this paragraph should use very simple terms, nothing significantly technical (ie not "resolution-independent")


The specifics of these APIs are currently experimental and subject to evolution based on community feedback. A valuable contribution to the project is testing these APIs, reporting edge cases, and sharing feedback on usability and performance!

## Project Goals

`p5.svg` addresses several key goals:

- **Resolution-Independent Vector Output**: Traditional canvas rendering in p5.js is raster (pixel) based. `p5.svg` enables artists, designers, and educators to generate scalable vector graphics suitable for high-DPI displays, print, pen plotters, CNC routers, laser cutters, and embroidery machines.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"p5.svg enables artists, designers, and educators to generate scalable vector graphics suitable for high-DPI displays, print, pen plotters, CNC routers, laser cutters, and embroidery machines." Feels like it should be in the opening paragraph?

- **Familiar p5 Drawing Workflow**: Rather than introducing a complex vector editing paradigm, `p5.svg` hooks directly into the existing 2D drawing pipeline. You can record shapes using standard p5 drawing functions inside `createShape()` or `buildShape()`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this phrasing should be simplified, eg not "Rather than introducing a complex vector editing paradigm, p5.svg hooks directly into the existing 2D drawing pipeline. You can record shapes using standard p5 drawing functions inside createShape() or buildShape()." but, "to create SVG output, the only thing you need to do differently is use crateshape and build shape. Inside these functions, use standard drawing API (rect, circle, path, fill, stroke, translate, rotate, etc.) as you usually would

- **Vector Asset Interoperability**: `p5.svg` provides a two-way pipeline for vector assets: importing external SVG files via `loadSVG()` into p5 `RecordedShape` structures, and exporting recorded p5 scenes to valid SVG 2.0 XML files using `saveSVG()` or `getSVG()`.
- **Integrated Native Addon**: Unlike p5 1.x addons that required extra script tags, `p5.svg` is built directly into core p5.js as an experimental module registered via `markExperimental('p5.svg', p5)`.

## What Needs Feedback

The main ways you can help develop `p5.svg` are:

- **API Ergonomics & Shape API**: Test vector shape recording and rendering functions like `createSVG()`, `loadSVG()`, `buildShape()`, `createShape()`, `shape()`, `getSVG()`, and `saveSVG()`. Share feedback on `shape()` playback, coordinate bounds, positioning, scaling options, and alignment modes (`CORNER`, `CENTER`, `VIEWBOX`).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section could be just "Shape API". I do not recommend the phrase "ergonomics," because in general API ergonomics does not imply a focus on learning and beginners as a user group, which is a specific focus in p5.js (part of access statement that relates to this feature)

In this case, in the whole feedback section, maybe it is useful top provide a bit more context on what the priorities are: maintaining familiarity with rest of p5.js; creating non-OOP, top-level, readable function calls to allow smooth svg recording / loading. The question is not ergonomics generally but: "how does this feel as a beginner?" or "if you teach with p5.js, do you see API choices that would be tricky for students?"

That's still quite broad, but for example, feedback like "I think this should be more compact / more OOP" would not really be relevant/applicable.

Just a general feedback, please feel free to implement as much as it makes sense

- **SVG Path Parsing**: Test importing SVGs generated by vector design tools (Adobe Illustrator, Inkscape, Figma). Report any unsupported path commands (`M`, `L`, `H`, `V`, `C`, `S`, `Q`, `T`, `A`, `Z`) or malformed elements.
- **Styling and CSS Properties**: Help verify that stroke weights, colors, fill rules, opacities, gradients, and transform stacks behave consistently across export and import pipelines.
- **Performance & Memory**: Test large or complex SVG scenes to help identify bottlenecks in DOM parsing, AST construction, or XML string generation.

## Technical Overview

Behind the scenes, `p5.svg` operates in three main layers:

1. **Shape Recording (`src/shape/svg/svg_recorder.js`)**:
- Intercepts 2D drawing calls (`rect`, `ellipse`, `line`, `beginShape`/`endShape`, `fill`, `stroke`, `push`, `pop`, `translate`, `rotate`, `scale`, `applyMatrix`).
- Builds an Abstract Syntax Tree (AST) composed of node instances (`ScopeNode`, `ShapeNode`, `BackgroundNode`, `ClearNode`, `ImageNode`).
- Tracks coordinate transformations using an internal `TransformStack`.

2. **SVG Export & Visitor (`src/shape/svg/svg_export.js`)**:
- Implements `SVGExportAddon` and `SVGVisitor` (extending `p5.PrimitiveVisitor`).
- Traverses the shape AST to output standard SVG 2.0 XML markup string via `getSVG()` or triggers browser file downloads via `saveSVG()`.

3. **SVG Import & Parsing (`src/shape/svg/svg_import.js`)**:
- Implements `SVGImportAddon` to parse external SVG DOM trees.
- Tokenizes path data commands (`PATH_COMMANDS`) and converts SVG elements (`<path>`, `<rect>`, `<circle>`, `<ellipse>`, `<line>`, `<polyline>`, `<polygon>`, `<g>`) into internal p5 `RecordedShape` structures ready for playback via `shape()`.

## Usage Example

### Exporting an SVG

```js
function setup() {
createCanvas(400, 400);

// Record drawing commands into a vector shape
const record = buildShape(() => {
background(245);
fill(99, 102, 241);
stroke(0);
strokeWeight(2);
circle(200, 200, 150);
});

// Save as vector file
saveSVG(record, 'my-vector.svg');
}
```

### Importing and Replaying an SVG

```js
let botLogo;

async function setup() {
createCanvas(500, 500);

try {
// loadSVG returns a promise; await the resolved RecordedShape
botLogo = await loadSVG('assets/robot.svg');
console.log('SVG Loaded successfully!');
} catch (err) {
console.error('Failed to load SVG:', err);
}
}

function draw() {
background(255);

// Render the SVG once it is fully loaded
if (botLogo) {
shape(botLogo, 100, 100);
} else {
fill(100);
text('Loading SVG...', 20, 30);
}
}
```

## Contributing

We welcome contributions to `p5.svg`! You can get involved by:

* **Testing existing SVG workflows** and reporting bugs or unexpected behavior on GitHub.
* **Proposing and implementing new SVG features**, such as expanded SVG element support, clipping paths, gradients, filters, and custom SVG attributes.
* **Improving SVG import and parsing**, including support for additional path commands and CSS/SVG attributes.
* **Adding tests and examples** to validate new functionality and demonstrate real-world SVG workflows.
* **Creating tutorials and creative coding examples** that showcase how `p5.svg` can be used in p5.js projects.
* **Reviewing and providing feedback on experimental APIs** to help improve their usability, consistency, and performance.

2 changes: 2 additions & 0 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ import shader from './webgl/p5.Shader';
p5.registerAddon(shader);
import strands from './strands/p5.strands';
p5.registerAddon(strands);
import svg from './shape/svg/p5.svg';
p5.registerAddon(svg);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💟


import { waitForDocumentReady, _globalInit } from './core/init';
waitForDocumentReady().then(_globalInit);
Expand Down
1 change: 1 addition & 0 deletions src/core/experimental.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { FES } from '../friendly_errors/fes';
const experimentalMessages = {
webgpu: 'WEBGPU mode is experimental, so its functions and constants may change in future versions. You can get involved by giving feedback to help direct its development!',
'p5.strands': 'p5.strands shaders are experimental, so functions for building shaders and the hooks available within them may change in future versions. You can get involved by giving feedback to help direct its development!',
'p5.svg': 'SVG features are experimental, so SVG export, import, and shape recording functions may change in future versions. You can get involved by giving feedback to help direct its development!'
};

// Just in case it's not possible to get access to the p5 instance from something,
Expand Down
41 changes: 41 additions & 0 deletions src/shape/svg/p5.svg.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* @module SVG
* @submodule p5.svg
* @for p5
*/

import { SVGExportAddon } from './svg_export.js';
import { SVGImportAddon } from './svg_import.js';
import { markExperimental } from '../../core/experimental.js';

// Initializes the p5.js SVG module by combining export and import functionality.
// Registers public APIs on p5.prototype and marks experimental features with
// warning decorators to inform users about API stability during the 2.x lifecycle.
function svg(p5, fn, lifecycles) {
// Register core export (shape recording, vector output) and import (SVG parser) extensions.
SVGExportAddon(p5, fn, lifecycles);
SVGImportAddon(p5, fn, lifecycles);

// List of user-facing SVG methods marked as experimental.
// Decorators log friendly error warnings when these methods are invoked in user sketches.
const experimentalMethods = [
'createSVG',
'loadSVG',
'createShape',
'buildShape',
'getSVG',
'shape',
'saveSVG'
];

for (const method of experimentalMethods) {
if (fn[method]) {
p5.registerDecorator(
`p5.prototype.${method}`,
markExperimental('p5.svg', p5)
);
}
}
}

export default svg;
Loading
Loading