尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证Infinite-canvas SDK with a Rust/WebAssembly engine and a tldraw-shaped API
翻译准备中
取自仓库 README
正在加载完整 README。 以下为已保存的摘录。
An infinite-canvas SDK for the web with a familiar Editor / ShapeUtil /
StateNode API and a Rust + WebAssembly engine underneath.查看完整 README ↗An infinite-canvas SDK for the web with a familiar Editor / ShapeUtil /
StateNode API and a Rust + WebAssembly engine underneath.
shape: / page: ids, createShapes, updateShapes,
select, zoomToFit, ShapeUtil, StateNode, .tldr files. The target is
tldraw 5.4; docs/COMPAT.md says exactly how much of it
exists, what is deliberately excluded, and where behaviour differs on purpose.We built products on tldraw, and it is a genuinely excellent library — the API model here is shaped after it because we think they got that design right. What we ran out of was headroom: our canvases grew past the point where a DOM-and-SVG renderer kept up, and the work we needed off the main thread had nowhere to go.
mocanvas is that same shape of API with a different engine underneath, built so
an existing tldraw app can come across rather than be rewritten.
docs/MIGRATION.md walks through the move;
@mocanvas/compat supplies the TL-prefixed spellings so imports can change
first and names later.
Creating shapes, median milliseconds — lower is better:
| Scene | mocanvas | tldraw 5.4 | |
|---|---|---|---|
| 1,000 geo shapes | 17 ms | 44 ms | 2.6× faster |
| 5,000 geo shapes | 43 ms | 131 ms | 3.1× faster |
| 20,000 geo shapes | 145 ms | 387 ms | 2.7× faster |
| 1,000 mixed | 29 ms | 70 ms | 2.4× faster |
| 5,000 mixed | 80 ms | 191 ms | 2.4× faster |
| 20,000 mixed | 234 ms | 609 ms | 2.6× faster |
Dragging a selection, median milliseconds per frame:
| Scene | mocanvas | tldraw 5.4 | |
|---|---|---|---|
| 1,000 geo shapes | 9.2 ms | 16.7 ms | 1.8× faster |
| 5,000 geo shapes | 58.3 ms | 108.3 ms | 1.9× faster |
| 20,000 geo shapes | 233.3 ms | 541.6 ms | 2.3× faster |
| 1,000 mixed | 16.6 ms | 16.8 ms | a tie, at the frame floor |
| 5,000 mixed | 66.6 ms | 116.4 ms | 1.8× faster |
| 20,000 mixed | 280.9 ms | 608.4 ms | 2.2× faster |
mocanvas scales: about 2.6× on building a scene at every size, and the interaction gap widens as the scene grows — 1.8× at a thousand shapes, 2.3× at twenty thousand.
With their caveats. Measured on an Apple M3 Pro, headless Chromium on the Metal backend, default settings on both sides. Two things count against mocanvas and are worth knowing: every shape here uses the hand-drawn stroke, which is its most expensive path, and pan and zoom is left out entirely because mocanvas sits at the harness's own frame floor in every case — it cannot be measured, so it is not claimed. It is our own benchmark of our own library.
An earlier version of this table was measured under software rasterisation and was wrong in both directions: it flattered mocanvas on creation (~3× where the truth is ~2.6×) and reported a loss at a thousand shapes that turned out to be an artefact of the CPU rasteriser rather than anything either library did.
docs/BENCHMARK.md has the method, the full tables, the rest of the caveats, and a rendering-fidelity comparison against tldraw at 99.2% interior IoU.
npm install @mocanvas/mocanvas react react-dom
import { Mocanvas } from "@mocanvas/mocanvas"
import "@mocanvas/mocanvas/mocanvas.css"
export const App = () => (
<div style={{ position: "absolute", inset: 0 }}>
<Mocanvas />
</div>
)
The stylesheet import is required. Without it the canvas renders but the
toolbar, panels and menus come up unstyled. Import it once, anywhere in your
app — the same way tldraw/tldraw.css is imported in a tldraw app.
Upgrading from 4.0.2 or earlier? Add that one line. Until 4.0.2 the stylesheet was pulled in by
@mocanvas/mocanvasitself, which broke the package everywhere there is no bundler — vitest, SSR, any plain Node import threwERR_UNKNOWN_FILE_EXTENSION: Unknown file extension ".css". The JS entry no longer imports CSS, so the import has to come from your app.
A version being prepared is published under the next dist-tag, never latest,
so a caret range will not hand you one:
npm install @mocanvas/mocanvas@next
Why it exists: a release candidate goes out early, while the version is
still being worked on. If your supply-chain policy refuses packages below some
age — one team here cannot install anything under 24 hours old — that clock
starts when the RC is published rather than when the final is, so you can test
against it and have your findings land before the release is cut instead of
after. All seven packages carry the same version under next; mixing one from
next with the rest from latest is not a supported combination.
Every package is ESM only — there is no CommonJS build. The engine locates
its .wasm asset with import.meta.url, which has no CommonJS equivalent;
Vite, webpack 5, Next, Astro, Remix and Rollup all consume the packages as-is.
See packages/wasm/README.md for what to do in a
bundler that does not understand new URL(..., import.meta.url).
| Package | What |
|---|---|
@mocanvas/mocanvas | <Mocanvas />, default shapes, tools, UI |
@mocanvas/editor | Editor, ShapeUtil, StateNode, geometry, <Canvas /> |
@mocanvas/store | records, Store, schema, migrations, .tldr IO |
@mocanvas/state | signals |
@mocanvas/wasm | engine bindings |
@mocanvas/sync | multiplayer: record diffs, presence, cursors |
@mocanvas/compat | TL-prefixed aliases for a tldraw migration |
Rust crates live in crates/: mocanvas-geo, mocanvas-scene,
mocanvas-render, mocanvas-wasm.
Requirements: Node 20+, pnpm 10, Rust stable with the wasm32-unknown-unknown
target, wasm-pack.
pnpm install
pnpm build:wasm:dev # or build:wasm for the optimized build
pnpm dev # playground on http://localhost:5180
Apps and tests in this repository resolve the packages from src; pnpm build
compiles the wasm and then every package to dist in dependency order, which
is what is published.
Tests:
pnpm test # vitest across packages
pnpm test:rust # cargo test
pnpm typecheck
Read docs/ARCHITECTURE.md. Short version: the TypeScript store owns the document; every committed change is mirrored to the engine as a compact command stream; the engine culls, tessellates and batches; the canvas uploads buffers by pointer and draws one call per batch. Shapes that render as React components go to a DOM overlay positioned in page space.
mocanvas has never been built by reading tldraw's source. Not the repository,
not a copy in node_modules, not a fork. The compatibility described above was
built against the public API reference and guides on tldraw.dev, sample .tldr
documents, and first-principles implementation — and it is measured against
that same reference, from an enumeration of the documented symbol names.
Names are not copyrightable expression (Google v. Oracle, 2021); the implementation behind them is original work. The full policy, including the single bounded exception for the private benchmark harness, is docs/CLEAN_ROOM.md.
mocanvas is not affiliated with or endorsed by tldraw.
4.0.2. The API model is tldraw 5.4 — a different architecture from the 3.x model 1.x was shaped after, not a rename. Coming from 1.x, read docs/MIGRATION.md.
Every symbol tldraw 5.4's reference documents exists under the same name —
1,420 of 1,420 across the seven packages this project maps, held there by
pnpm --filter bench api-coverage -- --reference, which fails if one regresses.
Four documented packages are not implemented; see What is not covered.
Names are not behaviour, though. Across the symbols both sides share, 86.4% of
@tldraw/editor's members are present: Vec, Box and Mat are complete,
Editor has 304 of 313 members, ShapeUtil 52 of 77.
docs/COMPAT.md has the breakdown and names what is missing.
Four packages the reference documents are not implemented. None of them is a capability mocanvas cannot have; the reasons differ and are worth stating separately.
| Package | Why not | |
|---|---|---|
@tldraw/mermaid | Mermaid text to shapes on the canvas. Self-contained, nothing blocks it. | Wanted |
@tldraw/driver | An imperative API over the editor for tests, automation and REPLs, built on public editor calls. Nothing blocks it either. | Wanted |
@tldraw/sync / @tldraw/sync-core | A different multiplayer architecture, not a missing one — see below. | By design |
On sync, precisely. mocanvas has multiplayer: @mocanvas/sync, with its own
transport and a field-level CRDT. What it does not have is tldraw's sync API —
useSync, TLSocketRoom, TLSyncClient and the storage wrappers around them.
That is an architecture choice, not a service dependency: tldraw's sync server is
one you run yourself, on Node, Cloudflare Durable Objects, Bun or any WebSocket
server, and only useSyncDemo points at a host tldraw operates. So an app that
wants collaboration is served; an app that wants to keep its existing tldraw sync
server is not, and would have to move to @mocanvas/sync.
Genuinely tied to services tldraw runs, and therefore never coming: licensing and
watermark checks, useSyncDemo, the asset CDN defaults, and third-party embed
unfurling. Each has a seam to supply your own.
Beyond the packages, 45 symbols that the packages export but the reference does
not document are also absent — almost all @tldraw/utils helpers such as
debounce, modulate and FileHelpers. import { debounce } from "tldraw"
fails today. They are deliberately not written from guessed semantics.
Two known performance defects are written up in docs/ARCHITECTURE.md: the spatial index degenerates when many shapes share a bounding box, and snapping rebuilds geometry the engine already holds. Both were found by measurement, both have a described fix, neither is done.
docs/BENCHMARK.md measures mocanvas against the library it is shaped after, on the same workloads, and renders the same document in both.
Source-available, not open source. Free to use for:
Shipping it in a commercial product, service or website needs a written agreement with us. That includes anything sold, anything that earns revenue directly or through advertising, and internal tools running a for-profit business.
To arrange one, or if you are unsure which side of the line you are on, write to [email protected] — we would rather answer the question than have you guess.
The full terms are in LICENSE, at the repository root.