Bringing Skia to the Web

If you do not know what Skia is — read this article first.

The Problem

You want Skia's power. In the browser. Without plugins.

Historically, impossible. Browsers give you HTML Canvas—a simple 2D drawing API that's fine for basic graphics but lacks advanced features. No complex path operations. Limited text shaping. No hardware-accelerated effects.

Then WebAssembly arrived.

What CanvasKit Is

CanvasKit is Skia compiled to WebAssembly (WASM). The entire C++ graphics library, running in your browser at near-native speed.

It exposes Skia's API through JavaScript bindings. You get:
— Full path operations
— Advanced text layout (HarfBuzz, paragraph layout)
— Hardware-accelerated effects
— All blend modes
— Shader support (RuntimeEffect)
— Everything else Skia provides

Download size: ~2.9MB gzipped. That's the entire graphics engine.

Who Uses CanvasKit

Flutter Web: Switched exclusively to CanvasKit renderer. Every Flutter web app uses it.

Shopify: Built react-native-skia with CanvasKit for web support. React Native apps can now use Skia across mobile and web.

AntV (Ant Group): Created @antv/g-canvaskit renderer for their graphics library.

Figma (partially): Uses custom WebGL code primarily, but leverages Skia for specific algorithms.

43+ npm packages: Various frameworks and libraries building on CanvasKit.

The First Failed Attempt: The Fiddle

Official documentation points to https://jsfiddle.skia.org/canvaskit for an online playground.

Problem: It doesn't work. As of 2024, users get 403 Forbidden after Google login. The CanvasKit fiddle has been down intermittently.

The main Skia Fiddle (fiddle.skia.org) works for C++ code, but that's not helpful for web developers trying to learn CanvasKit.

Reality: No working online playground means learning from docs and examples alone. The docs exist, but...

The Documentation Problem

CanvasKit documentation exists at https://skia.org/docs/user/modules/canvaskit/

Here's what's actually there:

Here's what's missing:

The documentation assumes you know Skia's C++ API and can translate. If you don't, good luck.

What the Docs Don't Tell You: Memory Management

Critical rule: Objects created with new or Make* methods must be deleted manually.

const paint = new CanvasKit.Paint();
// ... use paint ...
paint.delete();  // MUST call this

JavaScript's garbage collector does not clean up WASM memory. Forget to delete, memory leaks forever (until page reload).

The mistake everyone makes: Creating paths/paints inside render loops without deleting them. 100 frames = 100 leaked objects.

We'll cover proper patterns in the interactive examples.

Bundler Integration Hell

Webpack Issues

Vite Issues

The Reality

Most CanvasKit projects include a webpack.config.js or vite.config.js with cryptic workarounds. Copy-paste and pray.

React Native Skia: Shopify's Approach

Shopify built react-native-skia to bring Skia to React Native. For web support, it uses CanvasKit.

Key insight: They wrapped CanvasKit in a declarative API. Instead of imperative draw calls, you describe what to draw:

<Canvas>
  <Rect x={0} y={0} width={256} height={256} color="blue" />
</Canvas>

The library handles:

This is not a CanvasKit fork. It's a high-level API that uses CanvasKit for web and native Skia for mobile.

Worth considering if you're building in React.

Who Actually Uses CanvasKit Directly

Flutter: No choice, it's the renderer.

Custom renderers: Building design tools, canvas apps, visualization libraries.

Performance-critical apps: Need GPU acceleration HTML Canvas can't provide.

Cross-platform tools: Want identical rendering on web and desktop (via Skia).

Most developers: Use a framework on top (Flutter, react-native-skia, AntV).

The Advantages

1. Real GPU Acceleration

CanvasKit uses WebGL/WebGPU. Complex paths render fast. Effects are hardware-accelerated. HTML Canvas can't compete.

2. Feature Parity

Need custom blend modes? Advanced text? Path boolean operations? CanvasKit has it. Canvas API doesn't.

3. Identical Rendering

Skia on desktop + CanvasKit on web = pixel-perfect match. Critical for design tools.

4. Control

Low-level API means no magic. You control clipping, layers, GPU resources. When you need performance, this matters.

The Disadvantages

1. Size

2.9MB gzipped is significant. Your bundle is instantly larger. First load is slower.

2. Learning Curve

Not HTML Canvas. Not SVG. It's Skia's API, translated to JavaScript. Expect weeks of learning.

3. Memory Management

Manual deletion or memory leaks. No escaping it.

4. Debugging

Errors in WASM are cryptic. Stack traces are useless. console.log() is your friend.

5. Bundler Hell

Every build tool needs custom configuration. Expect half a day fighting Webpack/Vite.

Interactive Examples

Let's start by writing simple examples that are still valuable due to tiny amount of real examples on other resources:

We're building interactive examples you can run in your browser:

Example 1: Hello, Rectangle!

Basic CanvasKit setup, creating a surface, drawing a rectangle

const surface = CK.MakeCanvasSurface('canvas-id');
const canvas = surface.getCanvas();
const paint = new CK.Paint();

const color = CK.Color(255, 0, 0, 1.0);
const rect = CK.LTRBRect(50, 50, 200, 170);

// important to notice that rect and color are Float32Arrays

paint.setColor(color);
canvas.drawRect(rect, paint);
surface.flush();

// but paint is a skia wasm object wrapper, so we are deleting it

paint.delete();

Example 2: Moving circle

Simplest render loop with requestAnimationFrame integration

const w = 420, h=256;

const surface = CK.MakeCanvasSurface('canvas-circle-id');
const canvas = surface.getCanvas();
const paint = new CK.Paint();
const color = CK.Color(128, 0, 0, 1.0);

const oval = CK.LTRBRect(50, 50, 200, 170);
paint.setColor(color);

let x = 50, y = 50, r = 30, sx=3, sy = 3;

function update(){
  x+=sx;
  y+=sy;
  if(x+r>w || x-r<0) sx*=-1;
  if(y+r>h || y-r<0) sy*=-1;

  oval[0] = x-r;
  oval[1] = y-r;
  oval[2] = x+r;
  oval[3] = y+r;

  // it draws an oval in the same way as rect
  canvas.drawOval(oval, paint);
  surface.flush();
  requestAnimationFrame(update);
}
update();

// we are not deleting the paint, because we are reusing it each frame
//paint.delete();

When to Use CanvasKit

Use CanvasKit when:
— You need features HTML Canvas lacks
— GPU performance is critical
— Cross-platform rendering consistency matters
— You're building a design/graphics tool
— 2.9MB download is acceptable

Don't use CanvasKit when:
— HTML Canvas is sufficient
— Bundle size is critical
— Simple 2D drawing is all you need
— You can't invest time learning it

The Reality Check

CanvasKit brings Skia's power to the web. But it's not polished. Documentation is sparse. Bundler integration is messy. Memory management is manual.

I spent months learning this, hit every edge case and wrote workarounds for everything.

This series documents what actually works. Not what the docs say. What survives production.

Resources