https://github.com/lingdong-/psvg
Programmable Scalable Vector Graphics -- drawings that draw themselves
https://github.com/lingdong-/psvg
image-format programming-language svg vector-graphics
Last synced: 10 months ago
JSON representation
Programmable Scalable Vector Graphics -- drawings that draw themselves
- Host: GitHub
- URL: https://github.com/lingdong-/psvg
- Owner: LingDong-
- Created: 2020-11-16T02:33:35.000Z (about 5 years ago)
- Default Branch: main
- Last Pushed: 2023-01-23T16:17:47.000Z (about 3 years ago)
- Last Synced: 2025-03-30T01:09:05.197Z (10 months ago)
- Topics: image-format, programming-language, svg, vector-graphics
- Language: TypeScript
- Homepage: https://psvg.netlify.app/
- Size: 750 KB
- Stars: 307
- Watchers: 5
- Forks: 14
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
[](examples/textanim.psvg)
# PSVG - Programmable SVG
**[Doc](QUICKSTART.md) | [Playground](https://psvg.netlify.app/) | [Examples](examples/) | [NPM](https://www.npmjs.com/package/@lingdong/psvg)**
PSVG is an extension of the SVG (Scalable Vector Graphics) format that introduces programming language features like functions, control flows, and variables -- Instead of writing a program that draws a picture, write a picture that draws itself!
PSVG is compliant with XML and HTML specs, so it can be easily embedded in a webpage or edited with an XML editor.
This repo contains a [PSVG→SVG complier](psvg.ts) that transforms PSVG files to just regular SVG's. It can also automatically render all PSVG's on an HTML page when included as a ``.
> Note: Experimental and under development, currently the compiler is not very friendly and might misbehave at times; Contributions/Issues welcome.
For example, define a recursive function that draws the Sierpiński's triangle:
```xml
<psvg width="300" height="260">
<def-sierptri x1="{WIDTH/2}" y1="0" x2="{WIDTH}" y2="{HEIGHT}" x3="0" y3="{HEIGHT}" d="7">
<path d="M{x1} {y1} L{x2} {y2} L{x3} {y3} z"/>
<if false="{d}">
<return/>
</if>
<sierptri x1="{x1}" y1="{y1}" x2="{(x1+x2)/2}" y2="{(y1+y2)/2}" x3="{(x3+x1)/2}" y3="{(y3+y1)/2}" d="{d-1}"/>
<sierptri x1="{x2}" y1="{y2}" x2="{(x2+x3)/2}" y2="{(y2+y3)/2}" x3="{(x1+x2)/2}" y3="{(y1+y2)/2}" d="{d-1}"/>
<sierptri x1="{x3}" y1="{y3}" x2="{(x3+x1)/2}" y2="{(y3+y1)/2}" x3="{(x2+x3)/2}" y3="{(y2+y3)/2}" d="{d-1}"/>
</def-sierptri>
<fill opacity="0.1"/>
<sierptri/>
</psvg>
```
Which looks like this (after running it through the PSVG to SVG complier):

Since PSVG is a superset of SVG, all the elements in SVG are also in PSVG, and all of them are programmable. For example, you can use a `for` loop to generate a bunch of gradients whose `stop`s are determined by a function of the index.
```xml
<var n="12"/>
<defs>
<for i="0" true="{i<n}" step="1">
<var t="{i/(n-1)}"/>
<linearGradient id="grad{i}">
<stop offset="0%" stop-color="black"/>
<stop offset="100%" stop-color="rgb(200,{FLOOR(LERP(0,255,t))},0)"/>
</linearGradient>
</for>
</defs>
```
Which will generate gradients with `id`s `grad0`, `grad1`, `grad2`, ... To use, simply write:
```xml
<rect fill="url(#grad7)"/>
```
The above is a simplified excerpt from [`examples/pythagoras.psvg`](examples/pythagoras.psvg), which utilizes this "gradient of gradient" to colorize a tree:

To transform shapes in vanilla SVG, the "group" metaphor (`<g transform="...">`) is often used. In addition to groups, PSVG also introduces Processing/p5.js-like `pushMatrix()` `popMatrix()` metaphors. For example, from the same `examples/pythagoras.psvg` as above, the `<push></push>` tag combined with `<translate/>` `<roatate/>` are used to draw a fractal tree:
```xml
<def-pythtree w="" d="{depth}">
<push>
<fill color="url(#grad{depth-d})"/>
<path d="M0 {w/2} L{w/2} 0 L{w/2} {-w} L{-w/2} {-w} L{-w/2} 0 z"/>
</push>
<if true="{d==0}">
<return/>
</if>
<push>
<translate x="{-w/4}" y="{-w-w/4}"/>
<rotate deg="-45"/>
<pythtree w="{w/SQRT(2)}" d="{d-1}"/>
</push>
<push>
<translate x="{w/4}" y="{-w-w/4}"/>
<rotate deg="45"/>
<pythtree w="{w/SQRT(2)}" d="{d-1}"/>
</push>
</def-pythtree>
```
You can have your own pick of degree or radians: `<rotate deg="45">` or `<rotate rad="{PI/4}"/>` are the same. You can also use `<scale x="2" y="2"/>` to scale subsequent drawings.
Similarly, styling can also be written as commands to effect subsequent draw calls:
```xml
<stroke color="red" cap="round"/>
<fill color="green"/>
<path d="...">
<polyline points="...">
```
In addition to simple fractals shown above, PSVG is also capable of implementing complex algorithms, as it's a full programming language. For example, an implementation of Poisson disk sampling described in [this paper](https://www.cs.ubc.ca/~rbridson/docs/bridson-siggraph07-poissondisk.pdf), [`examples/poisson.psvg`](examples/poisson.psvg):

## The PSVG to SVG Compiler
A baseline PSVG to SVG complier is included in this repo. It is a very "quick-and-dirty" implementation that `eval()`s transpiled JavaScript. So for now, don't compile files you don't trust!
### As command-line tool
Install it globally via [`npm`](https://www.npmjs.com/)
<pre>
npm i -g <a href="http://npmjs.com/package/@lingdong/psvg">@lingdong/psvg</a>
</pre>
and use it with:
```
psvg input.svg > output.svg
```
For example, to compile the hilbert curve example in this repo:
```
psvg examples/hilbert.psvg > examples/hibert.svg
```
or try it without installing via [`npx`](https://www.npmjs.com/package/npx) (comes together with npm)
```
npx -s @lingdong/psvg input.svg > output.svg
```
### For the browser
PSVG is also available for browser via CDN, or [directly download](http://unpkg.com/@lingdong/psvg)
```html
<script src="http://unpkg.com/@lingdong/psvg">
```
By including the script, all the `` elements on the webpage will be compiled to `` when the page loads. Again, don't include PSVG files that you don't trust.
### As a library
Install locally in your project via npm
```
npm i @lingdong/psvg
```
```js
import { compilePSVG } from "@lingdong/psvg"
console.log(compilePSVG("..."))
```
or
```js
const { compilePSVG } = require("@lingdong/psvg")
console.log(compilePSVG("..."))
```
Additionally, `parsePSVG()` `transpilePSVG()` and `evalPSVG()` which are individual steps of compilation are also exported.
In browsers, functions are exported under the global variable `PSVG`.
**Check out [QUICKSTART.md](QUICKSTART.md) for a quick introduction to the PSVG language.**
## Editor Support
Syntax highlighting and auto-completion can be configured for editors by:
### VS Code
Add the following lines to your `settting.json`. [details](https://code.visualstudio.com/docs/languages/overview#_can-i-map-additional-file-extensions-to-a-language)
```json
"files.associations": {
"*.psvg": "xml"
}
```
### GitHub
To get highlighting for PSVG files in your repositories on GitHub, create `.gitattributes` file at the root of your repo with the following content. [details](https://github.com/github/linguist#using-gitattributes)
```ini
*.psvg linguist-language=SVG
```
### Other editors
Since PSVG is compliant with XML and HTML specs, you can always alias your language id to XML or SVG via the corresponding config on your editor.