Vector Graphics Renderer Article Series - Complete Index
Introduction (Theme 0)
Foundational Knowledge
-
- What Skia is and why it's the best 2D graphics library
- History: 2005 acquisition to 3+ billion devices today
- Who uses it: Chrome, Android, Firefox, Flutter, Figma
- Why it's superior to alternatives (Cairo, Direct2D, Canvas)
0.2 Introduction to CanvasKit β
- Bringing Skia to the web via WebAssembly
- Reality check: broken fiddle, sparse docs, bundler hell
- Who uses it: Flutter Web, Shopify react-native-skia, AntV
- Memory management: manual deletion or leak forever
- When to use it (and when not to)
Interactive Examples (Planned)
0.3 Hello Rectangle π
- CanvasKit setup, surface creation, basic drawing
- Memory: Paint creation and deletion patterns
0.4 Animating a Circle π
- Render loops, transforms, requestAnimationFrame
- Memory: Reusing objects vs creating new ones
0.5 Path Operations π
- Path creation, boolean operations, stroking
- Memory: When to delete, when to reuse
0.6 Text Rendering π
- Font loading, text shaping, paragraph layout
- Memory: Font and paragraph object lifetimes
0.7 Layers and Effects π
- saveLayer(), blur, blend modes
- Memory: Layer stack management, filter ownership
Foundation & Integration (Theme 1)
1.1 Why We Can't Use Skia's Path API
- Vector networks vs paths - topology matters
- Custom C++ data structure for explicit vertex/segment model
1.2 240MB Per Frame: The saveLayer() Trap
- Unbounded layers kill performance at 3000Γ2000
- Bounded layers in world space: 30Γ memory reduction
1.3 The WebAssembly Memory Dance
- Passing arrays between JavaScript and C++
- Raw pointers, manual WASM heap management
Vector Graphics Rendering (Theme 2)
2.1 Sticky-Out Caps: When Round Isn't Round
- Skia caps extend beyond endpoints, Figma's don't
- Manual stroke generation with endpoint-aligned caps
2.2 Making Corners Round: The Four-Point Circle
- Four-step algorithm for corner radius
- Bezier tangent formula: RΓ(4Γ(β2-1))/3 β 0.5522847
2.3 Stroke Joins: The Devil in the Geometry
- Connecting dashed segments without gaps/overlaps
- Manual joins using perpendicular normals and SkStrokerPriv math
2.4 Per-Segment Dashes: Why Global Patterns Don't Work
- Figma's per-segment dash distribution
- Manual calculation with side-first algorithm
Blend Modes & Effects (Theme 3)
3.1 LinearBurn: The Blend Mode That Wasn't There
- Adding missing blend mode to Skia core
- Exposing via CanvasKit bindings
3.2 Diamond Gradients: The Forgotten Primitive
- Implementing SkGradientShader::MakeDiamond()
- Distance field: max(|dx|, |dy|)
3.3 Masking: Clips, Effects, and Coordinate Chaos
- Maskβeffectβblend sequencing
- Separate render passes with controlled restoration
3.4 Opacity Layers: Why Shadows Must Stay Opaque β NEW
- Nested layer system (compositing + effect layers)
- Context-aware opacity: shadows stay opaque relative to content
- Opacity affects groups, not individual elements
Shaders & Transforms (Theme 4)
4.1 Why path.transform() Killed Performance
- CPU path transformation bottleneck for animations
- Exposed mesh interface for GPU transforms
- Why Figma uses raw WebGL instead of CanvasKit
4.2 Image Filters: Exposure, Contrast, and the Missing Matrix
- ColorMatrix isn't enough
- Custom RuntimeEffect shader with unified uniforms
4.3 The Transform Matrix Puzzle: Figma Space to Skia Space
- Transform semantics mismatch
- Matrix decomposition and reconstruction
Performance & Architecture (Theme 5)
5.1 65,535 Vertices: When Text Export Breaks Your Renderer
- Flattened text from Figma: 100k+ vertices
- Uint16βUint32 migration with WASM binding updates
5.2 Loop Detection: Finding Holes in Polygon Soup
- Unordered segments β renderable loops
- Sequential ordering with virtual vertices
5.3 Effect Caching: When to Recalculate vs. Reuse
- Static shadows regenerated every frame
- Content-hash caching with dirty tracking
WebGL & Browser Integration (Theme 6)
6.1 CanvasKit Build Flags: The Partial Compilation That Wasn't
- no_skottie breaks PathOps, no_font breaks Skottie
- Manual BUILD.gn edits for undocumented dependencies
6.3 Frame Capture Performance: Pixels to Pixels
- readPixels() synchronization stalls
- Double-buffered async capture
Special Topics (Theme 7)
7.1 SDF Antialiasing [DEFERRED]
- Implementing later, article pending
7.2 Four Steps to Round Corners
- Topology transformation algorithm
- Detect corners β cut points β shorten segments β insert arcs
7.3 Figma's Blend Modes: Not Quite Photoshop
- Divergent blend formulas
- Manual shader implementation
Series Statistics
Total: 29 articles
- β Written: 2 (0.1, 0.2)
- π Interactive examples: 5 (text ready, code pending)
- π Technical deep-dives: 22 (fully planned)
Recommended Reading Order
For Beginners to Skia/CanvasKit
- Start with Theme 0 (Introduction)
- Read interactive examples as they're published
- Move to Theme 1 (Foundation)
- Pick articles by interest from other themes
For Graphics Engineers
- Skim Theme 0 for CanvasKit specifics
- Jump to Theme 2 (Graphics) for visual algorithms
- Theme 4 (Shaders) for GPU details
- Theme 5 (Performance) for optimization
For "How Does Figma Work?"
- 0.1, 0.2 - Foundation
- 1.1 - Vector networks
- 4.1 - Why raw WebGL
- 2.2 - Corner radius
- 2.4 - Dashes
- 5.1 - Scale challenges
Key Insights Across Series
- CPU-GPU boundary matters - path.transform() bottleneck explains why Figma uses raw WebGL (4.1)
- Real-world data breaks assumptions - 100k+ vertices in flattened text (5.1)
- Topology > paths - Segment boundaries needed for effects (1.1, 2.4)
- Memory allocation compounds - Unbounded layers: 240MB/frame (1.2)
- Math isn't magic - Bezier constant RΓ(4Γ(β2-1))/3 is derived (2.2)
- Build systems lie - Flags don't work as documented (6.1)
Article Status Legend
- β Written - Article complete and published
- π Planned - Outline ready, text descriptions written
- π Ready - Plan complete, ready to write
- βΈοΈ Deferred - Waiting on implementation
Last updated: 2025-10-26