canvas2d-geometry-ir
v0.1.2
Published
Deterministic Canvas-like recording and geometry IR engine
Downloads
31
Readme
Canvas2DGeometryIR
Canvas2DGeometryIR is a standalone Bun + TypeScript library for deterministic 2D vector composition, structured geometry inspection, and replay rendering.
It provides a Canvas-like recording API that does not draw pixels while composing. Instead, the context records paths and paint operations into a structured, JSON-safe intermediate representation (IR). Geometry operations and replay are derived from that IR.
v1 scope
- Path recording (
moveTo,lineTo,bezierCurveTo,quadraticCurveTo,arc,closePath) - Fills and strokes
- Transform operations (
translate,scale,rotate,setTransform,resetTransform) - State stack (
save,restore) - Replay to an injected canvas-like target
- Geometry queries
- axis-aligned bounds
- point hit testing
- path intersections (flattened)
- closest-point on boundaries
- anchor/anchor-candidate extraction
- path inspection (segment kinds, subpath count)
- JSON-safe serialization/deserialization with Zod validation
Text rendering is intentionally out of scope for v1.
Installation
bun installPublished npm releases are generated by GitHub Actions on every push to main or trunk. The committed package.json keeps a stable base version such as 0.1.0, and CI rewrites it at publish time to 0.1.<github.run_number> without committing that change back.
Scripts
bun run typecheck
bun test
bun run build
bun run playground:build
bun run playground:servebun run playground:build emits a static site in playground/site, which is the artifact used for GitHub Pages deployment.
Quick usage
import {
Canvas2DGeometryIRContext,
GeometryEngine,
replayDocument,
serializeDocument,
deserializeDocument,
} from "canvas2d-geometry-ir";
const ctx = new Canvas2DGeometryIRContext();
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(100, 0);
ctx.lineTo(100, 100);
ctx.closePath();
ctx.fillStyle = "#0ea5e9";
ctx.fill();
const doc = ctx.getDocument();
const engine = new GeometryEngine(doc);
console.log(engine.getBounds());
console.log(engine.hitTestPoint({ x: 20, y: 20 }));
console.log(engine.closestPoint({ x: 105, y: 30 }));
console.log(engine.getAnchorCandidates());
const json = serializeDocument(doc);
const roundtrip = deserializeDocument(json);
// replayDocument(roundtrip, yourCanvasLikeTarget)Browser playground (manual verification + bug capture)
A lightweight browser playground now ships in playground/ for realistic manual verification of:
- selecting visible paths or grouped shapes
- dragging path bodies directly
- editing anchors and control points directly
- interactively adding line, arc, and Bezier segments
- inspecting bounds and anchor overlays
- exporting reproducible scenes (scene + tool state + concise click trace)
Run locally
bun run playground:serveThen open the printed local URL (default http://localhost:4070).
GitHub Pages deployment
The repository now includes /.github/workflows/deploy-playground.yml, which builds the static playground and deploys it to GitHub Pages on pushes to main.
To enable it in GitHub:
- Open the repository settings.
- Go to Pages.
- Set Source to GitHub Actions.
After that, each push to main will publish the latest playground build.
npm publishing
The repository also includes /.github/workflows/publish-npm.yml for package publishing to npm.
To enable npm publishing in GitHub:
- Create an npm automation or granular access token that can publish this package and is allowed to bypass 2FA for publishing.
- Add it to the repository secrets as
NPM_TOKEN. - Push to
mainortrunk.
Important details:
package.jsonmust keep the repository URL exactly ashttps://github.com/alexandr-panchenko/Canvas2DGeometryIR- the workflow publishes with
NODE_AUTH_TOKENfrom theNPM_TOKENGitHub secret - use a token that can bypass your npm publish 2FA requirement, otherwise npm will reject the publish
Each eligible push to main or trunk will:
- install dependencies with Bun
- run tests
- build the package and declaration files
- replace the base version in
package.jsononly inside CI - publish the resulting package to the public npm registry
The workflow publishes only on the first run attempt for a given push so a manual rerun does not collide with an already-published version.
Bug-capture workflow
- Load one of the built-in scenes.
- Reproduce the incorrect interaction (selection, drag, point editing, grouping, etc.).
- Click Export Scene.
- Paste the exported JSON artifact into your bug report.
The export includes:
- current scene data
- ordered scene commands
- derived geometry document
- current tool state
- recent click-only interaction log
This makes failures reproducible without manually authoring numeric geometry expectations.
Replay target contract
Replay stays independent of browser/DOM APIs. A host can adapt any backend by implementing:
beginPath,moveTo,lineTo,bezierCurveTo,arc,closePathfill,strokesetFillStyle,setStrokeStyle,setLineWidth
Determinism
- IR emission order is stable.
- Draw operations receive stable generated IDs (
op-0,op-1, ...). - Serialization output is JSON-safe and deterministic for equivalent command streams.
Geometry model
Each path is composed from ordered subpaths and segments. Segment kinds are:
linebezier(cubic)arc
Segment-level operations are implemented and composed upward for path/document-level queries.
Serialization
Use:
serializeDocument(document): stringdeserializeDocument(json): GeometryDocument
Deserialization validates with Zod schemas to prevent untyped data from leaking into geometry code.
Examples
See examples/basic.ts.
Run it with:
bun run examples/basic.tsPerformance notes (v1)
Current implementation favors clarity and correctness.
Hotspots to optimize later if needed:
- Bézier/arc flattening used by hit tests and intersections.
- Repeated path flattening in repeated queries.
- Repeated bounds recomputation across immutable documents.
Safe future improvements:
- Cache flattened polylines per segment/path with tolerance keying.
- Cache per-op and full-document bounds.
- Add broad-phase spatial bucketing for hit and intersection queries.
Non-goals for v1
- Full browser Canvas API fidelity
- Text layout/rendering
- DOM-specific dependencies
- Speculative plugin framework internals
