Architecture
This page condenses ADR 0005 (docs/decisions/0005-hand-drawn-canvas-renderer.md). The ADR holds the reasoning and the benchmarks.
Where the package sits
Section titled “Where the package sits”| Layer | Where | Job |
|---|---|---|
| Sketch | @draw/sketch |
Turn a polyline or a polygon, a roughness and a seed into rough strokes. |
| Paint | @draw/sketch |
Turn a Shape into a list of draw operations. Planned. |
| Canvas | the app | Replay the operations on a canvas, own the camera loop and measure text. |
- Pure. An exported function returns the same output for the same input. A seeded generator holds mutable state only inside one call and is never exported.
- Plain data out. Results are readonly and serializable, with points in frame-local space as
LocalPoint. - Seeded. A
Seedfrom@draw/shapesfixes every random choice, so the wobble is stable across frames, reloads and machines. - No runtime dependency except
@draw/shapes, for its branded types.
Edges and curves
Section titled “Edges and curves”Straight-edged outlines (rectangle, diamond, arrowheads, line polylines) are roughened edge by edge. Smooth outlines (ellipse, arrow arc) are resampled to a few points and a jittered smooth curve is drawn through them. Roughening a 64-point ellipse edge by edge reads as a polygon, so the caller says which kind of outline it passes.
Fills follow the stroke’s base points. A curve returns the jittered points it was drawn through, so a later hachure stays inside the drawn outline.
Type checking against @draw/shapes
Section titled “Type checking against @draw/shapes”Sketch type-checks against the source of @draw/shapes, not its build. tsconfig.json maps @draw/shapes to ../shapes/src/index.ts, so vp check and vp test see a shapes change at once, with no build in between.
The package build uses tsconfig.build.json instead, which has no such mapping. With the mapping, the declaration build treats the shapes sources as part of its program and writes a .d.ts file next to each of them in packages/shapes/src. Without it, the build leaves @draw/shapes as an external import. isolatedDeclarations lets the declaration build emit the types without reading the shapes package, so the build doesn’t depend on shapes being built first.