
CanvasKit – Skia and WebAssembly - espeed
https://skia.org/user/modules/canvaskit
======
atomicUpdate
This page is infuriating to use on my phone. I can barely scroll, and when I
do get lucky, it bounces back to the top and more things load.

~~~
lowtolerance
Web development-centric websites often seem to totally ignore the need to
provide a usable mobile experience. It’s maddeningly weird.

~~~
acoard
It's not unsurprising. Web development happens practically exclusively on
desktop/laptops.

~~~
zapzupnz
And yet, desktop browsers have no more need of hijacking the browser's
scrolling behaviour than any other browser.

~~~
paulryanrogers
And leave scroll bar rendering to the browser or OS vendors!?

Think of the pixels!

/s

Seriously though I believe it's often done to make infinite scrolling and
scroll-transitions more visually pleasant. (Not that I agree with the
sentiment, but if visuals are more important than anything else...)

------
z3t4
I would like a Skia + v8 runtime. Some of my web apps are just a 2d canvas and
JavaScript. Would be nice to be able to use them without a browser and bundle
with something more lightweight then Electron.

~~~
snek
I would assume the skia wasm blob can be integrated with cairo canvas, as it
has the same exposed api.

------
bcheung
What is this for and why would someone want to use it?

There's basically nothing on the website but the API. No decent overview or
tutorial or even why someone would want to use this project.

~~~
kbumsik
C/C++ for WebAssembly mainly used for porting existing C/C++ application to
the web. I think it is one of them. You know what? Flutter uses Skia as a GUI
backend. So maybe we could run a Flutter app using this someday.

~~~
pests
I'm not sure if it's been publicly released but "project hemmingway" (I think)
is Flutter for web. I've been following it for awhile.

------
markdog12
They should use the desynchronized canvas flag for lower latency on the ink
example.

canvas.getContext('webgl', {desynchronized: true})

[https://www.chromestatus.com/feature/6360971442388992](https://www.chromestatus.com/feature/6360971442388992)

~~~
mkl
That seems like it would have to happen within CanvasKit itself, as the code
using it only uses abstractions over the canvas itself. I can't find anywhere
in CanvasKit where it does getContext('webgl'), only '2d', so I'm not sure how
it's doing it.

The latency of that example seems great with a mouse, and a little laggy with
a pen (a bit better than a pure canvas drawing app I've been working on). The
frame rate is much better than my app.

------
xfalcox
I just created a proposal to support lottie based animated stickers in
Discourse two days ago, and this looks very useful to get more performance
when playing those using the new Skottie module. Very cool!

[https://meta.discourse.org/t/plugin-for-animated-
stickers/12...](https://meta.discourse.org/t/plugin-for-animated-
stickers/123212?u=falco)

------
alleycat5000
I wonder if this is in part driven by Flutter for Web?

~~~
espeed
See the Skia/Flutter note below. Skia has been open-source since 2008 -- it's
the wasm bit that's new.

[https://skia.org/dev/flutter](https://skia.org/dev/flutter)

~~~
alleycat5000
Right, I just mean specifically the new web stuff in Flutter:

[https://flutter.dev/web](https://flutter.dev/web)

Presumably having Skia available in the browser makes supporting the web
platform easier.

------
espeed
Here's one of the Skia Path Ops videos referenced in the Presentation section
-- for some reason it links to slides but not the videos...

Skia Path Ops : High Performance Set Operations for Geometry

[https://www.youtube.com/watch?v=OmfliNQsk88](https://www.youtube.com/watch?v=OmfliNQsk88)

------
pier25
Anyone knows why text looks so blurry?

Where are the docs for CanvasKit?

~~~
kllrnohj
> Anyone knows why text looks so blurry?

If you're on desktop it's probably because it's not doing any form of subpixel
hinting or AA.

It's both harder to do that in a GL context, and I don't know if there's even
any way to get the display's subpixel layout in JS in the first place.

~~~
pier25
It looks like there is antialiasing, although it's huge. It only happens for
text resizing demo though, the text on the star demo looks better.

~~~
kllrnohj
It should be doing AA but not subpixel AA (aka, it's not going to match
cleartype). Those are 2 different things.

------
tapirl
Can anyone show the url of the source code (C API?) which is outputted to the
wasm file? I mean the subfolder in the skia project.

~~~
kimown
[https://github.com/google/skia/blob/master/experimental/c-ap...](https://github.com/google/skia/blob/master/experimental/c-api-
example/skia-c-example.c)

do you mean this?

~~~
tapirl
Almost. Thanks.

------
skybrian
It sounds like this allows you to draw to a WebGL surface as if it were
Canvas?

~~~
espeed
It means you can now include Chrome's Skia vector graphics rendering engine as
part of your custom code in WebAssembly (skia/wasm in Chrome or elsewhere).

See the Slug thread from earlier today for context...

Slug: Dynamic GPU Font Rendering and Advanced Text Layout
[https://news.ycombinator.com/item?id=20475111](https://news.ycombinator.com/item?id=20475111)

[https://en.wikipedia.org/wiki/Skia_Graphics_Engine](https://en.wikipedia.org/wiki/Skia_Graphics_Engine)

~~~
skybrian
Right, but I'm wondering if it runs entirely on the CPU or uses GPU-based
acceleration via WebGL.

~~~
Lerc
It is certainly a WebGL Context.

Running this on one of the examples.

let c=document.querySelector("#canvas");

c.getContext("2d"); >>>> null

c.getContext("webgl"); >>>> WebGLRenderingContext { vertexAttribDivisor:
vertexAttribDivisor(), drawArraysInstanced: drawArraysInstanced(),
drawElementsInstanced: drawElementsInstanced(), createVertexArray:
createVertexArray(), deleteVertexArray: deleteVertexArray(), bindVertexArray:
bindVertexArray(), isVertexArray: isVertexArray(), drawBuffers: drawBuffers(),
Yu: null, canvas: canvas#canvas }

c.getContext("webgl2"); >>>> null

Of course that doesn't stop it from using any amount of software rendering
layers before it hits the canvas, but it's a good sign.

------
dvh
It takes 1 minute to draw star (first demo). Why is it so slow?

~~~
zamadatix
There are a few MB of .wasm included in the page. Takes about half a second to
load it and another half second to initialize that for me on Chrome/FF. If
it's not connection speed related then there is probably something buggy going
on with running WASM in your browser.

------
srameshc
Yesterday I saw a post about tinygo (tinygo.org) which can produce WebAssembly
code and now Skia + WebAssembly. Fun times are ahead with Go :)

------
aloer
Would be great if anyone here has some experience with this and examples of
use cases. I have been looking at this for a few weeks now for a tile based
view with multi threaded rendering and server side prerendering. But I just
can't figure out what the benefits would be over canvas natively, if there
even are any. As I wrote in
[https://news.ycombinator.com/item?id=20339574](https://news.ycombinator.com/item?id=20339574)

" context: I want to build a tile based view (tiles in x/y dimensions + zoom
levels). The content of the tiles is loaded from server (shapes mostly) and
rendered into tile images client side. I also want the same tiles prerendered
as identical images on the server.

For this I have a feeling that something like skia is the way to go. Skia can
be used via wasm bindings. How I would fetch the shapes and render the tiles
(or fetch the prerendered ones) transparently and then where to render the
tiles into, that is what I am trying to figure out. It feels like
multithreading could be very useful here. Right now only chrome appears to
support OffscreenCanvas (which can be accessed from webworkers), hence the
idea of using skia directly and possibly going a level higher to write
whatever kind of multithreaded render logic in rust and run it with wasm and a
single "canvas output". Whether skia is the right choice here or not is also
something I have yet to figure out

The ultimate goal is quick startup (prerendered tiles) while simultaneously
high performance when updating the entire view (=multiple tiles in parallel).
This is mostly a learning project for me context: I want to build a tile based
view (tiles in x/y dimensions + zoom levels). The content of the tiles is
loaded from server (shapes mostly) and rendered into tile images client side.
I also want the same tiles prerendered as identical images on the server.

For this I have a feeling that something like skia is the way to go. Skia can
be used via wasm bindings. How I would fetch the shapes and render the tiles
(or fetch the prerendered ones) transparently and then where to render the
tiles into, that is what I am trying to figure out. It feels like
multithreading could be very useful here. Right now only chrome appears to
support OffscreenCanvas (which can be accessed from webworkers), hence the
idea of using skia directly and possibly going a level higher to write
whatever kind of multithreaded render logic in rust and run it with wasm and a
single "canvas output". Whether skia is the right choice here or not is also
something I have yet to figure out

The ultimate goal is quick startup (prerendered tiles) while simultaneously
high performance when updating the entire view (=multiple tiles in parallel).
This is mostly a learning project for me "

Essentially what I am wondering is if using skia via wasm would allow me to
bypass limits on canvas natively (and canvas in webworkers) so that I can
render multiple tile images in parallel and then draw them into a single view
for the user to interact with. Think google maps or similar but tiles rendered
client side

Overall this feels like there might be a better way and I am just not seeing
it

------
patientplatypus
Can someone explain to me what this drink example is doing?

[https://jsfiddle.skia.org/canvaskit/e7ac983d9859f89aff1b6d38...](https://jsfiddle.skia.org/canvaskit/e7ac983d9859f89aff1b6d385190919202c2eb53d028a79992892cacceffd209)

Just from a brief parse of the code I don't see how the drink could possibly
be transforming the way that it is doing. The renderer - drawFrame in this
case - isn't doing any heavy lifting to really make all the side animations
(drink splash etc) occur.

It appears that at least with this example, possibly the others (the lego
one?) if they work they've hidden a lot of data in the fetched json data.
Which seems not great. I mean the drink json
([https://storage.googleapis.com/skia-
cdn/misc/drinks.json](https://storage.googleapis.com/skia-
cdn/misc/drinks.json)) is not human parse-able so I don't see how they made
these examples at all. Maybe they made them in Adobe After Effects and then
exported the keyframes as json or something, but there's no tutorial or
directions that I can see.

This is incredibly frustrating because a decent canvas tool is sorely needed
(generally). Does anyone see what is going on?

EDIT: So they do use lottie files ([https://lottiefiles.com/410-lego-
loader](https://lottiefiles.com/410-lego-loader)). But lottie files can
already be exported to javascript incredibly easily, so unless you _really_
need the extra performance of WASM this doesn't appear to add anything. Am I
still missing something?

------
pier25
It's less than 50KB Gzipped, not bad at all.

[https://bundlephobia.com/result?p=canvaskit-
wasm@0.6.0](https://bundlephobia.com/result?p=canvaskit-wasm@0.6.0)

~~~
kllrnohj
I don't think that's accurate at all. Watching Chrome's network profiler it's
pulling down 2.5MB .wasm for the star fiddle:
[https://jsfiddle.skia.org/canvaskit/ea89749ae8c90bce807ea2e7...](https://jsfiddle.skia.org/canvaskit/ea89749ae8c90bce807ea2e7e34fb7b09b950cee70d9db0a9cdfd2d67bd48ef0)

50KB Gzipped seems like it's just the wrapped JS code, not the .wasm code.

~~~
pier25
The NPM package doesn't list any dependencies:

[https://github.com/google/skia/blob/master/modules/canvaskit...](https://github.com/google/skia/blob/master/modules/canvaskit/package.json)

I'm not saying you are wrong, but if the NPM package needed more stuff it
would be in the package.json, no?

~~~
kllrnohj
I'm more saying bundlephobia.com is wrong in that it doesn't appear to be
including .wasm files at all.

If you just do an npm install of canvaskit-wasm you'll find a 6.4M
canvaskit.wasm file. That'd have to be some insane gzip magic to bring that
down to 50Kb.

If I gzip the entire canvaskit module (including the font files) I'm getting a
size of 3.4M. If I just gzip the .wasm file I get a size of 2.4M.

~~~
pier25
Thanks for checking that out. You are right of course.

------
c-smile
Cool, but why?

Any browser already includes the very same set of graphic primitives that Skia
provides. Just to render what you see on the screen right now.

Why not just to expose all that as API for WASM or whatever?

~~~
kevingadd
HTML canvas is incredibly slow and just a generally bad API. Using it from
WASM is a mistake. It uses strings for everything and has a particularly slow
imperative API, and the spec/de-facto-spec behaviors perform very slowly even
on high-end hardware. I've seen relatively simple 2D canvas-based games drop
frames in chrome on a GTX 2080ti because canvas is that bad.

~~~
z3t4
The slow downs usually comes from abstraction layers. If you just use the api
it will be fast. But if you create 1 million new objects and have 2 million
function calls in excess on every frame it will be slow. Eg. If you dont like
imperative style it will be slow. A compilation step and a bunch of
optimization runs could make anything fast thought.

~~~
kevingadd
It really is not. If you want to set the current color you _have to assemble a
css color string from your color values_. There is no way to make this fast.
When you ship production 2D games using canvas, stuff like this shows up in
your profiles and there's no way to optimize it out other than not using
canvas. (Many people switch to using WebGL to do 2D themselves for this reason
- it's faster.)

~~~
z3t4
If setting the color is the bottleneck you can for example paint all red
shapes, then paint the blue shapes, etc, instead of setting the color for
every shape. You can also use different canvases . Offscreen and worker
canvases is in experimental which enables further optimizations. 2d canvas has
some very expensive calls, the trick is to only call them once, not for each
object, not even for each frame. If you have 3d objects eg. Z-index its better
to use 3d canvas. Yes 2d canvas is slow, but 2d scenes should be able to be
optimized by only painting stuff once, then translate, rotate, reset and
reuse. Set some upper limit of moving parts on the screen, then use pooling
and caching. Put _all_ canvas calls in the same file/ function then it will be
easy to see what code can be removed/re-ordered eg. set the color once, not
1000 times after each other.

