Generating vector graphics with Paper.js
There is one hard problem in vector graphics: boolean path operations. Many applications simply rephrase their problem to avoid doing boolean path operations. A while ago, when Graphite needed too do boolean path operations, we found two open source solutions: Inkscape’s lib2geom, and Paper.js. lib2geom’s code is … :S I forgot. All I remember is that it’s written C++. Paper.js does boolean operations exceptionally well. We ended up doing neither.
Fast forward to yesterday - I was making an icon in Inkscape and it keeps crashing. The icon also turns out to have too thin strokes, and I have to redo it over again.
Then I tried Penpot. It doesn’t have a grid. Then Graphite. Oh right, there’s no boolean operation yet.
At this point, I realized that I don’t have time to spend 5 minutes designing an icon, just to found out that some part of it is a little bit off, then traverse the edit history to fix it. Since Paper.js, I can design icons as code!
So off I went into the world of integrity damage learning.
Paper.js Is Hurty
The library is buggy, and you may be suprised to find new Path({pathData: X, position: Y})
to behave differently than new Path({position: Y, pathData: X})
. Nevertheless, it has a robust boolean path operation algorithm.
I tried to extract only the part that has to do with boolean path operation, and I gave up.
- The code consists of many header files and its own preprocessor as the build system (bundler)
- It is partially written in the style of Java beans
- It uses its own object system
- It uses child methods in parent without even checking that the method exists
If I’m not clear, the project is written in Javascript, not C.
I hope that one day the algorithm will be freed from the Paper.js codebase. It will likely be a battle of floating point errors, although, I wonder if libbf can get rid of errors from the algorithm entirely.
Before I question my life choices even further, I quickly slapped together the DOM library from uhtml and paper-core.js as an ES module to be used from Deno. It worked immediately and marked the end of my suffering.
Anyway, here’s the source code: https://git.envs.net/iacore/paper.js-deno/
Enjoy my suffering :)
Generating A Logo
One more caveat I hit before the code is correct: operator overloading is only available in Paperscript, not Javascript. If you just copy your code from sketch.paperjs.org, it will not work.
Here is the prerendered icon.
You should see this with or without Javascript enabled on this page.
Here is the JIT rendered icon.
You need Javascript to see this. The icon-generating script is here.
Now I can finally have style.
What’s Next
I should make a better version of the official editor (sketch.paperjs.org) that reflects your changes within 50ms of an edit that you can run yourself.
Other than that, just like how media container files contain audio or video streams, font files contain vector graphics with some programming logic to adjust the path position (kerning) and choose which path to display (ligurature etc).
Which brings us to an interesting observation: all graphical elements need kerning.
Here is a screenshot of startpage.com with terrible icon-text kerning. Look at the uneven spacing between icon and text.
More generally, a large paprt of typography is applicable to non-text vector graphics, and I suspect the distinction between text-like graphical elements and non-text-like graphical elements is largely irrelevant to graphics design. I will have to see how it works in practice. For now, try make some icons of your own!
Further Readings
Writing path data string (d="..."
) by hand is an essential skill in web development and also when you use Paper.js.
If you are interested in learning normal typography, take a look at Butterick’s Practical Typography and buy his fonts!
plutovg renders vector paths by treating them as passing them as FreeType glyphs to a FreeType renderer.