Graphite has a clear architectural split between its Svelte/TypeScript frontend and its Rust backend. The frontend is responsible for rendering the user interface: panels, toolbars, the canvas overlay, and all interactive widgets. The Rust backend, compiled to WebAssembly, handles all editing logic — document state, tool behavior, history, and node graph execution. Neither side directly inspects the other’s internal state; all communication happens through a message-passing protocol across the Wasm boundary.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/GraphiteEditor/Graphite/llms.txt
Use this file to discover all available pages before exploring further.
Architecture Overview
The data flow between frontend and backend follows this path:wasm-bindgen
Thefrontend/wrapper crate is the glue layer between JavaScript and Rust. It uses wasm-bindgen to expose Rust types and functions to TypeScript. The EditorWrapper struct is the primary entry point: it is instantiated once on startup and provides methods that the frontend calls to dispatch messages into the editor.
The wasm-bindgen macro handles serialization automatically for primitive types. Richer message payloads are serialized as JSON using serde-wasm-bindgen, letting Rust structs and TypeScript interfaces stay in sync via generated type definitions.
Message Protocol
The frontend dispatches events by calling methods onEditorWrapper — for example, pointer move events, key presses, and menu actions. The Rust backend returns messages to the frontend by invoking the callback passed at construction time.
Backend response messages follow a tagged-union format. Messages with data are serialized as { "MessageName": { ...payload } } and messages with no data are serialized as the bare string "MessageName". The SubscriptionsRouter normalizes these two forms before routing.
Common response message categories include:
- Layout updates —
UpdateLayoutcarries widget diffs for the properties panel, layers panel, and toolbar - Document state — layer tree changes, node graph changes, selection changes
- Canvas — render requests, viewport updates, overlay drawing
- Dialogs — show/hide modal dialogs
Svelte Frontend Structure
The frontend source lives infrontend/src/:
| Path | Purpose |
|---|---|
App.svelte | Root component — initializes Wasm, creates the EditorWrapper and SubscriptionsRouter, then mounts the editor |
components/ | All Svelte UI components: panels, toolbars, dialogs, canvas |
managers/ | Non-component modules managing cross-cutting concerns: input.ts (keyboard/mouse), clipboard.ts, persistence.ts, panic.ts |
stores/ | Svelte reactive stores holding UI state derived from backend responses |
utility-functions/ | Helpers for platform detection, Wasm loading, and network requests |
subscriptions-router.ts | The message routing layer between Wasm callbacks and Svelte handlers |
App.svelte is intentionally minimal. It initializes the Wasm module, registers node implementations, creates the EditorWrapper, and passes the SubscriptionsRouter and editor handles down to the <Editor> component:
Subscriptions Router
frontend/src/subscriptions-router.ts is the central dispatch table that routes backend response messages to the correct Svelte handler. Components and stores register callbacks for specific message types using subscribeFrontendMessage. Layout update messages have a special routing path: they are dispatched to layout-specific callbacks keyed by LayoutTarget rather than the generic subscription map.
Hot Reload Dev Server
During development, thecargo run tool starts a watcher that monitors both Rust and Svelte/TypeScript source files:
- Svelte/TypeScript changes trigger an instant Vite hot-module reload with no Wasm recompile needed
- Rust changes trigger a
wasm-packrecompile and a full browser reload once the new Wasm module is ready
The
frontend/wrapper crate is the only Rust crate that directly imports wasm-bindgen. All other crates in the workspace are platform-agnostic and can be compiled for both Wasm and native targets.