Why Not Canvas2D?

Canvas2D is everywhere. Every browser has it. No build step. No WASM. Just draw.

So why aren't we using it for production?

We are—for the demos in this series. But for a professional vector editor? Canvas2D hits walls.

What Canvas2D Does Well

Simplicity. Drawing a rectangle:

ctx.fillStyle = 'red';
ctx.fillRect(10, 10, 100, 50);

Done. No initialization, no context loss handling, no shader compilation.

Wide support. Works in every browser since 2010.

Text rendering. Canvas2D gets text right. Font loading, measurement, baseline alignment—all handled.

Good enough for many apps. Simple games, charts, basic drawing tools. Canvas2D excels.

Where It Falls Apart

Missing Blend Modes

Canvas2D has globalCompositeOperation. Twelve options:

ctx.globalCompositeOperation = 'multiply';

Photoshop has 27 blend modes. Figma uses many of them. Where's Linear Burn? Color Dodge? Subtract?

Not in Canvas2D.

Workaround: Implement in shaders (requires WebGL) or do pixel manipulation (slow).

No Clip Effects

You can clip paths. You cannot:

Figma's layer effects require all three.

Performance Cliff

Canvas2D is immediate mode. Every frame, redraw everything.

10 objects? Fine. 100 objects? Tolerable. 1,000 objects with effects? Slideshow.

// Every frame
function draw() {
  ctx.clearRect(0, 0, width, height);
  for (const obj of objects) {
    drawObject(ctx, obj);  // CPU-bound loop
  }
}

The GPU sits idle while JavaScript crunches.

No GPU Texture Caching

Draw a complex shape once. Move it. Canvas2D redraws it from scratch.

WebGL/Skia can cache the rasterized result in GPU memory. Move the texture. 60fps, no sweat.

Canvas2D has no such concept.

Stroke Limitations

Remember dashes? Canvas2D has them:

ctx.setLineDash([10, 5]);

But:

For Figma-style strokes, we'd implement everything manually anyway.

Filter Bottleneck

Canvas2D has filter:

ctx.filter = 'blur(4px)';

Sounds good. Reality:

  1. Applies to everything you draw after
  2. Can't blur just one layer's edge
  3. Performance tanks with complex filters
  4. Shadow + blur + blend = exponential slowdown

No Off-Thread Rendering

JavaScript is single-threaded. Canvas2D rendering blocks UI.

WebGL can offload to GPU while JavaScript continues. OffscreenCanvas helps but has limited adoption. Worker + Canvas2D loses context on postMessage.

For large canvases, main thread freezes during render.

When Canvas2D Makes Sense

Prototyping. Get ideas working fast.

Simple tools. Basic shapes, no effects.

Educational content. These demos work in Canvas2D because we keep them simple.

Compatibility priority. When IE11 support matters (it shouldn't, but sometimes it does).

When to Move Beyond

Professional features. Blend modes, effects, masks.

Performance requirements. 1000+ objects, 60fps.

Complex compositions. Nested groups with mixed blend modes.

Production rendering. Export quality, frame capture, video encoding.

The Migration Path

Start with Canvas2D. Prototype, learn, iterate.

When you hit the walls—and you will—migrate to:

  1. WebGL directly: Maximum control, maximum work
  2. CanvasKit/Skia: Figma-level features, WASM bundle size
  3. WebGPU: Future-proof, still maturing

We chose Skia/CanvasKit. It has everything Canvas2D doesn't, wrapped in a usable API.

Summary

Canvas2D is the gateway. Use it to learn. Use it to prototype.

But don't mistake the gateway for the destination. When you need:

Canvas2D won't get you there.

The demos in this series run on Canvas2D. The production renderer doesn't. That's the lesson.

Next: Why Not Fabric.js/Konva? →