@fr0/renderer-canvas
v0.1.0
Published
Isomorphic Canvas 2D renderer for fr0. Targets the standard CanvasRenderingContext2D interface so the same code runs in browsers, OffscreenCanvas, and node-side Skia (@napi-rs/canvas).
Readme
@fr0/renderer-canvas
Isomorphic Canvas 2D renderer for fr0. Targets the standard
CanvasRenderingContext2Dinterface so the same code runs in browsers,OffscreenCanvas, and Node-side Skia (@napi-rs/canvas).
Status
🚧 Phase δ-1 — thin slice in progress
- ✅ shapes: rect / circle / polygon / path
- ✅ fills: solid + linear / radial / conic gradients
- ✅ transform stack (translate, anchor, rotate, scale, opacity)
- ⏳ text / image / video (Phase δ-2)
- ⏳ filters / mask / blend modes (Phase δ-3)
- ⏳ group recursion + custom layer registry (Phase δ-4)
Why this exists
Phase δ replaces the html2canvas-based bitmap pipeline with a renderer
that draws directly into a CanvasRenderingContext2D. The same renderer
runs in:
- Browsers — preview (
<canvas>in a React component) + export (OffscreenCanvas→WebCodecs VideoEncoder) - Node — server-side rendering (
@napi-rs/canvas→ ffmpeg or WebCodecs-in-headless-Chromium)
There is no DOM intermediary. The single source of truth is core/resolve()
output → drawTimeline(ctx, …).
See docs/phase-delta-plan.md for the
full Phase δ plan.
Quick start
import { createCanvas } from '@napi-rs/canvas';
import { drawTimeline } from '@fr0/renderer-canvas';
import type { Timeline } from '@fr0/core';
const canvas = createCanvas(timeline.width, timeline.height);
const ctx = canvas.getContext('2d');
drawTimeline(ctx, timeline, /* frame */ 0);
await fs.writeFile('frame-0.png', canvas.encode('png'));Public API
| Export | Purpose |
|---|---|
| drawTimeline(ctx, timeline, frame, options?) | Top-level entry. Resolves the timeline at frame and draws every layer in z-order. |
| drawLayer(ctx, resolvedLayer, options) | Draw a single resolved layer. Used by drawTimeline and by group recursion (Phase δ-4). |
| applyTransform(ctx, transform) | Set up the canvas transform stack from a ResolvedTransform. |
| applyFill(ctx, fill, bounds) | Set ctx.fillStyle from a FillValue, materializing gradients against the layer bounding box. |
What this package is NOT
- Not a general-purpose 2D rendering engine (only knows about fr0 Layer types)
- Not React (zero React imports — pure functions over
CanvasRenderingContext2D) - Not the encoder (ships separately in
renderer-browserandrenderer-node)
Testing
@napi-rs/canvas provides the CanvasRenderingContext2D implementation
in Node, so the same code we ship to browsers can be tested without a
headless browser. Pixel checks live under __tests__/.
