
Show HN: Skia-canvas, a browserless implementation of the Canvas API for Node.js - _samizdat
https://github.com/samizdatco/skia-canvas
======
untog
If you're already using Rust, an alternative might be raqote, which would
avoid the need to interface with C++ at all... and it's the canvas library
used by Firefox's Servo:

[https://github.com/jrmuizel/raqote](https://github.com/jrmuizel/raqote)

~~~
gardaani
The good things about raqote is that it's a Rust library so it may be safer
than Skia. Raqote is also easier to compile than Skia.

The downside is that it's slower than Skia [1].

But, it should be possible to modify skia-canvas to work with raqote. The JS
API would stay the same.

[1]
[https://twitter.com/pcwalton/status/1252281006263988224](https://twitter.com/pcwalton/status/1252281006263988224)

~~~
muizelaar
Raqote is also noticeably faster than it was in April. Still not as fast as
Skia but getting closer.

------
gfxgirl
I'm curious, what if anything is different from node-canvas? Not that I'm
complaining in any way, just curious.

~~~
wffurr
This uses the Skia backend instead of Cairo. It will give results closer to
Chrome's Canvas implementation.

It also doesn't appear to support Windows yet, whereas node-canvas does.

There's probably other differences, e.g. performance, size, etc. but you'd
have to measure them.

------
adamnemecek
If you are looking for a Rust native, not too complicated implementation of
canvas api, check out
[https://github.com/cytecbg/gpucanvas](https://github.com/cytecbg/gpucanvas).
It's a rust port of nanovg.

Check the demo by running 'cargo run --example demo'.

~~~
adamnemecek
I'll be adding more backends to this. I'm almost done with a metal backend
([https://github.com/adamnemecek/gpucanvas](https://github.com/adamnemecek/gpucanvas)),
I'll add vulkan and wgpu next. If you want to get involved, we are definitely
looking for contributors!

It's a really good way to understand 2d graphics.

------
pier25
How does text rendering on images work on different platforms? Do you always
get the same result regardless of the platform?

I'm asking because usually Skia produces different aliasing results on
different platforms (eg: Windows vs macOS).

~~~
c-smile
If by "on different platforms" you mean "on different server platforms" then
result will be different but close enough.

On Windows, for example, Skia will render text closer to Direct2D/DirectWrite.
On MacOS to CoreText.

For the reference, here is rendering of the same UI (HTML/CSS) in Sciter using
Direct2D (left) and Skia/OpenGl (right):

[https://sciter.com/wp-content/uploads/2020/09/d2d-vs-
skia.pn...](https://sciter.com/wp-content/uploads/2020/09/d2d-vs-skia.png)

~~~
pier25
> _On Windows, for example, Skia will render text closer to Direct2D
> /DirectWrite. On MacOS to CoreText._

So what you're saying is that Skia changes the aliasing to look like the
platform it's running on?

Damn, I wish I could make Chrome on Windows render text like it does macOS.

~~~
c-smile
I do not see any significant difference (other than different system fonts):

Here is MacOS rendering [https://sciter.com/wp-content/uploads/2020/09/mac-os-
front.p...](https://sciter.com/wp-content/uploads/2020/09/mac-os-front.png)

~~~
pier25
Maybe not when using Sciter, but on Chrome the differences are massive. I will
try to make some screenshots later.

------
octref
Nice work! The other day I was doing a SVG renderer for Shiki[0] (a code
syntax highlighter), but found no easy way to measure font width/height (SVG
needs absolute positioning) in Node.js. The only library that does that
records fonts' metadata in the package source[1].

I ended up using puppeteer[2]. Would love to switch to `skia-canvas`, however
`ctx.measureFont` doesn't seem to work correctly yet. Measuring cap `M`
produces positive `actualBoundingBoxDescent`, although `M` should sit
comfortably on top of baseline.

[0]: [https://shiki.matsu.io/](https://shiki.matsu.io/)

[1]: [https://github.com/adambisek/string-pixel-
width](https://github.com/adambisek/string-pixel-width)

[2]:
[https://github.com/shikijs/shiki/blob/master/packages/render...](https://github.com/shikijs/shiki/blob/master/packages/renderer-
svg/src/measureMonospaceTypeface.ts)

------
dheera
> can generate output in both raster (JPEG & PNG) and vector (PDF & SVG) image
> formats

I thought browser canvas was a completely raster tool? What if I drew a
million lines onto skia-canvas -- which is something you can do in a browser
canvas -- would that result in a huge stack of vector operations getting saved
somewhere for the possibility of future vector export?

------
sitkack
Are there server side webgl implementations as well? Either GPU accelerated or
pure software.

~~~
dhritzkiv
There's headless-gl ([https://github.com/stackgl/headless-
gl](https://github.com/stackgl/headless-gl)). Note: I'm a maintainer

~~~
kimown
But headless-gl need a DISPLAY, egl api provided by angle is not headless
completely like Nvidia. [https://developer.nvidia.com/blog/egl-eye-opengl-
visualizati...](https://developer.nvidia.com/blog/egl-eye-opengl-
visualization-without-x-server/)

~~~
dhritzkiv
Thanks for the link! I haven't read that before.

And true - on Linux a X11 frame buffer DISPLAY is required, but I would still
consider this to be headless. On other platforms, it should be truly headless.
(I could be sorely mistaken)

~~~
kimown
[https://bugs.chromium.org/p/chromium/issues/detail?id=103484...](https://bugs.chromium.org/p/chromium/issues/detail?id=1034840)

See this issue, Angle can support headless completely with vulkan backend, but
progress is BlockedOn angleproject.

------
aloer
What are the differences between this and canvaskit, the official wasm version
(?) of skia?

[https://www.npmjs.com/package/canvaskit-
wasm](https://www.npmjs.com/package/canvaskit-wasm)

Can be used with node as well

~~~
esrauch
This is a native dependency where that's compiling skia itself to wasm. The
native dependency should be able to be faster by a wide margin by taking
advantage of hardware acceleration but it's not obvious if it does (similarly
skia compiled to wasm must be much slower than using the proper canvas API in
the browser)

~~~
aloer
Why is wasm necessarily slower than the browser if the browser is already
implementing canvas via skia? Just the hardware acceleration? Does that mean
only gpu access or something more?

Not that that would make much sense. I’ve not been very successful at
understanding what kind of problem canvaskit is trying to solve

What I am trying to do is the same as it was more than a year ago
([https://news.ycombinator.com/item?id=20339574](https://news.ycombinator.com/item?id=20339574)):
A tile based, web based view with prerendered image tiles from the server and
on the fly rendered tiles on the client. Ideally the client side rendering is
super fast, whether that means gpu is enough or multi threaded cpu I don’t
know yet

So what I understand so far is that I can’t have anything multithreaded (yet?)
in the web client and probably native canvas is the most performant. Even
canvaskit with webgl won’t reach that performance (not sure here)

Otoh canvaskit could possibly be used in other browsers if they don’t natively
implement canvas via skia itself to minimize render diffs

And lastly this project here could be the backend renderer instead of either
headless browser via canvas api (hacky) or canvaskit in node (slow because cpu
only)

But none of these offer and kind of performance increase over native canvas
which is limited to single thread because of spectre and meltdown

~~~
esrauch
Yeah, Skia itself has multiple backends, including CPU and GPU
implementations. The wasm one must use the CPU path, where Skia in your
browser can use a CPU or GPU path based on whatever is fastest. So already
wasm is necessarily equal-or-slower.

Wasm also does have some overhead too; some of that will get better with
upcoming changes; at least wasm-SIMD support and threads are both only
available in some environments.

But don't get me wrong, I'm certainly not suggesting the skia-wasm project is
pointless, there's just trade-offs here.

~~~
aloer
What are the uses of skia wasm then?

So far I’ve mostly heard about porting apps that require native skia to the
web. But that wouldn’t explain why google spent all that effort building a
canvas compatible api and didn’t just stick to the skia api

I was hoping that skia would be more efficient in this tiling context where
rendering tile images and putting them together in the final view isn’t that
great with browser canvas. But I realize now that lack of multithreaded tile
rendering means it doesn’t matter much anyways whether image tiles can be
copied to another image quickly or not

Now I have a feeling that webgl might be a necessary step for me to look at.

This project has been dormant for a year now and I have to start again. Not an
easy area to navigate with all those different and not necessarily comparable
solutions

Last I played around with canvaskit I found the performance in the browser to
be better than native canvas for certain things but worse for most. Why that
is or if this follows any rules I don’t know. Perhaps I’m also mistaken

------
phoe-krk
It does not seem to have a LICENSE file or any license mention in the README.

~~~
MikeCoats
It looks like it's MIT licensed, according to their `package.json`.

[https://github.com/samizdatco/skia-
canvas/blob/master/packag...](https://github.com/samizdatco/skia-
canvas/blob/master/package.json#L7)

------
LAMike
Anyone have a demo, or "Hello World" for this library?

------
Sophistifunk
Ugh, there's like ten of these. When will I be able to open a damned window
and draw on it?

~~~
runbyfruity
1973, with the Xerox PARC-developed Alto personal computer. It's still
possible to do today.

------
tenaciousDaniel
Calling it "server-side canvas" really throws me off. Why would a drawing API
be needed on a server? For headless rendering?

If the benefit is to use the drawing API in a Node context _outside of_ a
server environment, that makes more sense to me.

~~~
giovannibonetti
I was thinking on creating a turn-based multiplayer game for group chat in an
app like Telegram. Then, whenever a player gave a command the server would
draw the board and send the updated image to the chat.

It could start with something simple like tic-tac-toe and only two players. A
player would tell the app a number between 1 and 9 corresponding to the place
he/she chose. Then the server/bot would send the updated image to the
conversation.

What do you think? :)

~~~
mufufu
Not sure why that would have to be done server side though, couldn’t the
server only be used for coordination/maintain state and then submit the
current state/positions to the client and the client would render the board?

~~~
julienb_sea
In his example, there is no client browser. The users are just sending
commands into a telegram chat, the app is presumably only able to send API
calls based on chat events responses. Its unlikely the app would be
sufficiently featured to do the board rendering itself, although I am not
really familiar with this space so I'm making assumptions.

As such, the backend would do the heavy lifting of rendering the board, and
send the rendered board back into the message group.

