003 - Rendering System
The Unified Rendering System enables renderer-agnostic code.
The Problem
Imagine: You've programmed a beautiful artwork with Canvas2D. Now you want:
- More performance – Thousands of particles stutter, WebGL would be better
- Export SVG – For your pen plotter
- Different outputs – Live on screen AND as a vector file
Without abstraction, you'd have to write your code three times. With the Unified Rendering System, you write it once.
The Solution
The RenderContext is a facade – a unified interface behind which different renderers operate. Your code calls ctx.drawCircle(), and depending on the active renderer, it ends up on Canvas2D, WebGL, or in an SVG file.
Why Facade? You don't want to worry about implementation details. A circle is a circle, whether it's drawn with arc() (Canvas2D), shader triangles (WebGL), or <circle> (SVG).
Architecture
┌─────────────────────────────────────────────────────────────┐
│ Artwork Code │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Brush/Agent Code │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Facade │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ RenderContext │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────┐
│ Renderer │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Canvas2D- │ │ WebGL- │ │ SVG- │ │
│ │ Renderer │ │ Renderer │ │ Renderer │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘Components
| Class | File | Function |
|---|---|---|
IRenderer |
IRenderer.ts |
Interface for all Renderers |
RenderContext |
RenderContext.ts |
Facade, selects active Renderer |
Canvas2DRenderer |
Canvas2DRenderer.ts |
Standard Canvas 2D |
WebGLRenderer |
WebGLRenderer.ts |
GPU-accelerated |
SVGRenderer |
SVGRenderer.ts |
Vector Export |
RenderContext
The main facade for rendering.
Factory Functions:
| Function | Description |
|---|---|
createCanvas2DContext(canvas) |
Canvas 2D Renderer |
createWebGLContext(canvas) |
WebGL Renderer |
createSVGContext(width, height) |
SVG Renderer |
createAutoContext(canvas) |
Auto-Detect |
Switch Renderer:
const ctx = createAutoContext(canvas);
ctx.setRenderer('webgl'); // or 'canvas2d', 'svg'IRenderer Interface
All renderers implement this interface.
Primitive Methods:
| Method | Description |
|---|---|
drawCircle(x, y, r, style) |
Circle |
drawRect(x, y, w, h, style) |
Rectangle |
drawLine(x1, y1, x2, y2, style) |
Line |
drawPolygon(points, style) |
Polygon |
drawPath(commands, style) |
SVG-like Path |
drawEllipse(x, y, rx, ry, style) |
Ellipse |
State Methods:
| Method | Description |
|---|---|
save() |
State to Stack |
restore() |
State from Stack |
translate(x, y) |
Translate |
rotate(angle) |
Rotate (Radians) |
scale(sx, sy) |
Scale |
setTransform(matrix) |
Set Matrix |
Utility Methods:
| Method | Description |
|---|---|
clear(color?) |
Clear Canvas |
resize(w, h) |
Change Size |
getImageData() |
Read Pixels |
PrimitiveStyle
Styling for all primitives.
| Property | Type | Description |
|---|---|---|
fill |
RenderColor |
Fill Color |
stroke |
RenderColor |
Stroke Color |
strokeWidth |
number |
Stroke Width |
lineCap |
'butt' | 'round' | 'square' |
Line Ends |
lineJoin |
'miter' | 'round' | 'bevel' |
Line Corners |
opacity |
number |
Transparency (0-1) |
RenderColor
Colors can be specified in various formats:
type RenderColor =
| string // '#FF0000', 'red', 'rgb(255,0,0)'
| { r, g, b, a? } // Object
| [r, g, b, a?] // ArrayUtility Functions:
| Function | Description |
|---|---|
parseColor(input) |
To {r,g,b,a} |
colorToHex(color) |
To #RRGGBB |
colorToRgba(color) |
To rgba(...) |
Canvas2DRenderer
Standard renderer for most use cases.
Advantages:
- Broadest browser support
- Simple debugging
- Good performance for 2D
Limitations:
- No GPU acceleration for complex effects
- Slower with many particles
WebGLRenderer
GPU-accelerated rendering.
Advantages:
- High performance with many objects
- 60fps even with thousands of particles
- Shader effects possible
Limitations:
- More complex implementation
- Not all primitives equally efficient
SVGRenderer
For vector export (e.g., pen plotter).
Advantages:
- Lossless scaling
- Perfect for plotter export
- Layer support
Specifics:
- No realtime rendering
- Collects primitives for export
- Layers via
svgLayerName
Export:
const svgCtx = createSVGContext(1000, 1000);
// ... draw ...
const svgString = svgCtx.toSVGString();Transform2D
Transformation matrix.
interface Transform2D {
a: number; // scale x
b: number; // skew y
c: number; // skew x
d: number; // scale y
e: number; // translate x
f: number; // translate y
}Helpers:
| Function | Description |
|---|---|
identityTransform() |
Identity Matrix |
createTransform(opts) |
From translate/rotate/scale |
Brush → Style Conversion
import { brushToStyle } from '@carstennichte/cc-toolbox';
const style = brushToStyle(brush);
// → { fill, stroke, strokeWidth, ... }