<DrawService />
<DrawService /> connects an embedded <Visualizer /> to a running draw server over WebSocket so any client/api calls made elsewhere render in this instance live. Skip it if you only render snapshots — snapshots are entirely client-side and don’t need the server.
The plugin is headless: it provides the connection context and starts the stream; it doesn’t render anything itself.
The Connect RPC client is required for the <DrawService />.
Install
Section titled “Install”| Prop | Type | Description |
|---|---|---|
config | { backendIP: string; websocketPort: string } | Host and port of the draw server. Default dev port is 3030. |
The config prop is reactive — change it and the plugin reconnects to the new endpoint.
Connection logs
Section titled “Connection logs”<DrawService /> opens two connections: a WebSocket for the legacy draw API, and a Connect-RPC stream for entity changes.
The WebSocket connection reports its lifecycle events — connecting, connected, disconnected, reconnecting, and parse or socket errors — through the useLogs hook. Mount <Logs /> alongside it to see them in a panel; without it the messages are dropped.
The entity stream does not report to useLogs. It reconnects on its own with exponential backoff, and resubscribing replays the full scene, so a dropped connection recovers without anything to act on. Stream errors go to the browser console.
Running a draw server
Section titled “Running a draw server”<DrawService /> is a client — it needs a draw server to talk to. You have three options:
Use the motion-tools local app
Section titled “Use the motion-tools local app”The fastest path: run motion-tools locally (make up or pnpm dev). The local app already hosts a draw server on port 3030, and client/api calls to the same host land in it.
Host the Go DrawService yourself
Section titled “Host the Go DrawService yourself”Motion-tools exports a ready-made DrawService from github.com/viam-labs/motion-tools/draw — the same Connect-RPC handler the local app uses. Wire it up to your own HTTP server:
Your client/api producer code then points at this server, and <DrawService config={{ backendIP, websocketPort: '3030' }} /> in the browser subscribes to its stream.
Implement your own server
Section titled “Implement your own server”If you need a custom backend (different language, different storage, extra side effects), the proto definitions are published under github.com/viam-labs/motion-tools/draw/v1 (Go) and ship as .proto files in the source tree under protos/draw/v1/. Implement the DrawService RPCs in whichever language you like; <DrawService /> just needs a Connect-RPC server at the configured host:port.
Start with these, which between them cover everything client/api calls:
| RPC | Why it’s needed |
|---|---|
AddEntity | Single-entity draws (DrawGeometry, DrawPoints, DrawLine, and friends). |
AddEntities | Batch draws. DrawFrameSystem, DrawWorldState, DrawGeometriesInFrame, and DrawFrames send one batch rather than one request per entity, so a backend without this fails on the most common calls. |
UpdateEntity | Partial updates, and the delivery channel for chunked point-cloud payloads. |
RemoveEntity, RemoveAll, RemoveAllTransforms, RemoveAllDrawings | Removal. |
StreamEntityChanges | The subscription <DrawService /> consumes. Replay the current scene as ADDED events on connect so a reconnecting client resyncs. |
GetEntityChunk | Only if you support chunked entities. |
SetScene, StreamSceneChanges | Only if you drive the camera from the server. |
CreateRelationship, DeleteRelationship | Only if you use entity relationships. |
The cleared_scope field on StreamEntityChangesResponse is optional. It lets a bulk removal travel as one event instead of one per entity, but a backend that emits an individual REMOVED per entity works against the client unchanged.
See the client/api reference for the producer-side calls regardless of which server you use.