Pixerate canvas
The canvas is an xyflow (React Flow / SvelteFlow) graph: an array of nodes and edges.
A node is { id, type, position: { x, y }, width?, height?, parentId?, zIndex?, data }; edges are
{ id, source, target, label? }. Everything a design feature knows lives in node.data, so it
survives copy, paste and JSON:
data.label/data.name: text content and layer name.data.shape: a shape node ({ kind: rect | ellipse | polygon | star | path, fill, stroke, strokeWidth, ... }).data.layout: an auto layout frame (modehorizontal / vertical / grid,gap,padding,alignMain,alignCross,sizing); its children are the nodes whoseparentIdis the frame, in node-array order.data.childLayout: a child's sizing (fixed / hug / fill) and constraints.data.component(a main component),data.componentSet(a set of variants),data.instance({ componentId, properties }) anddata.componentOverrides.data.locked,data.animation(keyframe tracks),data.tokenBindings(design tokens).
Positions are in canvas (flow) units; a child's position is relative to its parent. Node ids come
from get_editor_state; component and set ids from list_components. Tools that take ids or
id default to the current selection when you leave them out, so pass ids explicitly unless you
just selected with canvas_select. See pixerate-agent-tools for namespacing, run_commands
batching and error codes.
Workflow skills
For these jobs, load the workflow skill too: it has the method, the checks between steps and the pitfalls, and uses the tools below.
pixerate-canvas-design-system-components: components, instances, variant sets and overrides.pixerate-canvas-layout: align, distribute, order and lock.
Working method
- Read.
get_editor_statewithincludeDocument: falsereturnscanvas.nodesandcanvas.edges. For a design system also calllist_components, andinspect_instancebefore changing an instance. - Plan the structure first: which frames, which reusable components and variants, what spacing scale. Build the main component once, then place instances; do not copy nodes by hand.
- Write. Use the dedicated tools for graph edits (
canvas_add_node,canvas_connect,canvas_update_node) and components (create_component,create_instance, ...). Userun_commandswithatomic: truefor arrange, shape and animation commands, and whenever several steps should be one undo step (a param"$0.ids[0]"uses an id an earlier step returned). - Tidy.
canvas_layoutfor graphs;canvas.align*/canvas.distribute*for designs; layer order withcanvas.bringToFrontand friends; lock finished backgrounds withcanvas.lock. - Look.
view_editor(default viewport, JPEG, 1024 px) once a batch is done; usescope: "canvas"for the whole drawing. Fix overlaps, misalignment and clipped text, then show the user withcanvas_selectorcanvas_highlight.
Working alongside the user (they are building and you add to it unasked): suggest instead of
editing. propose_nodes shows ghosts the user accepts or dismisses, and nothing reaches the canvas
or its history until then. Keep each proposal small and about one idea (a caption, the next card in
a series, a missing connector), give it a title and a one-sentence rationale, and anchor it to
the node it complements with nearNodeId. Never accept your own proposals unless the user asked
you to. Withdraw stale ones with reject_proposal: when the user changes what a proposal builds on,
list_proposals shows it under missing.
Best practices
-
Design-system thinking. Anything that appears twice should be a component: build one main,
combine_as_variantsfor states and sizes (State=Default,State=Hover,Size=Small), thencreate_instanceeverywhere. Change the main to change every instance; use overrides only for per-use content (label text, an icon swap). Name variant properties and values consistently (Title Case property, short values) and keep combinations unique. -
Name layers that must survive a variant switch with a stable
data.name. Matching falls back todata.label, which for text is its content and differs between variants. -
Use component properties, not detaching. Switch variants and set text or boolean properties with
set_instance_properties;swap_instancefor another component. Detach only when the instance must stop following its main. -
Spacing scale. Pick a base unit (4 or 8) and use multiples for gaps and padding. Put rows and stacks in auto layout frames (
data.layout) instead of positioning children by hand, so they reflow when content changes. -
Alignment and distribution. Align to a shared edge or centre, then distribute with equal gaps; a design reads as ordered when edges line up. Lock a node you want as the anchor: locked nodes never move but still count towards the selection bounds.
-
Layering. Backgrounds at the back, content above, overlays on top. Use the restack commands; they set
zIndexper sibling group and never reorder the array. -
Graph layout. Lay flows out in one direction (
LRfor pipelines,TBfor hierarchies), keep edges short, label edges only when the relationship is not obvious. -
Motion. Use ease-out (or
ease-out-backfor a little overshoot) for things arriving, ease-in for things leaving, ease-in-out for moves between two resting places, andlinearonly for continuous motion such as rotation. Keep UI transitions around 150 to 400 ms and entrances under about 600 ms; stagger related elements by 30 to 80 ms withdelayrather than moving everything at once. Animateopacity,x,y,scaleandrotation; colour tweens interpolate in OKLab. -
Brushed strokes. Use a brush when the user wants a hand-drawn look (sketching, ink, pencil, marker); keep plain strokes for diagrams and UI. Send points every 2 to 8 canvas units along a curve, and shape the line with a short
pressureprofile (tapered ends:[0.2, 0.9, 1, 0.9, 0.2]) rather than many points of different widths. Leavewidthout to use the preset's own size (canvas.listBrushesgives it); pass it only to make the mark finer or bolder. -
Brush character. Each preset draws with its own character: charcoal and pastel are broad and grainy (they catch the paper's tooth), as are crayon and chalk; pencil is fine and slightly grainy; ink, marker, brush pen, ballpoint, gel pen and fineliner are clean (the fineliner keeps one width), the airbrush is a soft spray. The grain and jitter are seeded per stroke, so a stroke always looks the same, and neighbouring strokes share one sheet of paper. Pick the brush for the look, then the colour; for a sketch, use one or two brushes rather than all of them. A brush with a default colour (ballpoint blue, highlighter yellow, chalk) paints in it when you pass no
color; passcolorto use another. Light / dark defaults (chalk) resolve for the canvas's colour scheme (canvas.setColorScheme; the host usually matches its theme). -
Setting up the pen for the person, not drawing.
canvas.selectBrushchooses the brush the person draws with by hand, as the brush picker does: it takes the brush's default colour unless they locked theirs (canvas.setColorLocked, "Keep my colour") or you passkeepColour. A default taken this way follows the scheme until another colour is set. Respect a lock the person set; don't unlock it to change their colour. These are editor state, not undo steps, and don't affectcanvas.drawFreehand, which takes its ownbrushandcolor. -
Annotate with gestures, not points. To mark up work the way a designer marks up a print, use the gesture commands (
canvas.circle,canvas.underline,canvas.arrow,canvas.bracket,canvas.strike,canvas.cross,canvas.check) with node ids ({ "nodeId": "..." }) or boxes, rather than working out points forcanvas.drawFreehand. One call draws one hand-drawn mark. An annotation helps when you are pointing at something on the canvas while you talk about it: circle the pick, underline the line that matters, draw an arrow from a swatch to where it is used, bracket a group you are describing, strike or cross out a rejected option, tick an approved one. Skip it when nothing on the canvas is being referred to, and keep it to a few marks per point: a page covered in marks says nothing. -
Gesture style. Use one colour for one review pass (a red or a strong blue pen reads as markup over most designs), the highlighter only for circles and underlines, and the same
brush(markerorink-pen) for every mark of a pass if you want them to look hand-drawn. Marks are seeded by their request, so asking again draws the same mark; pass anotherseedfor a different take. Put a margin note (a text node) beside a bracket or at an arrow's tail when the mark needs words.
Tool reference
Read
| Tool | Use it to |
|---|---|
get_editor_state | Read the canvas graph (canvas.nodes, canvas.edges) and, by default, the prompt document too. Pass includeDocument: false on a canvas-only task to keep the result small, includeCanvas: false for only the prompt. Call it first and again after edits made by the user. |
view_editor | Get an image of the editor: scope viewport (default) or canvas (the whole drawing's bounds), format jpeg (default) or png, maxEdge in pixels (default 1024). Use it after a batch to judge overlap, balance and legibility; agent cursors are left out. |
capture_view | Screenshot part of the canvas to check a result: region viewport (default, as on screen), content (every node), selection, nodes (with ids from get_editor_state) or rect (canvas units); padding (default 16), maxEdge (default 1024), format png (default) or jpeg. The result gives the captured rect and scale, so image pixel (px, py) is canvas point (rect.x + px / scale, rect.y + py / scale). Prefer nodes to inspect what you just added at full detail. Selection boxes and agent cursors are left out unless includeOverlays. |
list_components | List main components (id, name, variant values, instance count) and component sets with their variant options. Call it before create_instance, swap_instance or any variant tool to get ids and the exact property names and values. |
inspect_instance | Describe one instance (or a layer inside it; default the first selected node): its main, variant, component property values with types, and per-layer overrides. Call it before set_instance_properties or reset_overrides so you use the right property names and override groups. |
Write: graph
| Tool | Use it to |
|---|---|
canvas_add_node | Add a node: node (id optional and must be unused, type registered by the host, data, position, width, height, parentId for a frame). Leave position out to auto-place it without overlaps beside nearNodeId on side (default right), which also connects an edge from that node, or beside the last node. Returns the new node id and edge id. |
canvas_connect | Add an edge from source to target (two different existing nodes), with an optional label. Returns the edge id. |
canvas_update_node | Merge fields into a node's data (for example { "label": "Checkout" }). Treat the merge as shallow: pass nested objects such as shape or layout whole. It does not move or resize nodes. |
canvas_remove_nodes | Delete nodes and their edges; every id must exist. Destructive: confirm with the user before removing content they made. |
canvas_layout | Arrange the graph automatically: direction LR (default), TB, RL or BT. incremental (default true) keeps nodes that already have a position; pass false to re-lay everything. Nodes at exactly (0, 0) count as unplaced. |
canvas_select | Replace the selection (an empty list clears it). Use it to show the user a result and to target commands that act on the selection. |
canvas_highlight | Flash nodes to point the user at them: color (default #3b82f6), durationMs (default 2000, max 60000). Changes nothing. |
Explode and collapse
Hosts with expandable nodes (a task that opens into subtasks, a Gantt bar into its steps) add these. Exploding shows the node's child nodes and moves the surrounding nodes to make room; collapsing removes the children and puts the moved nodes back. It is a view change, not an edit: it is not an undo step and is not saved with the document. The host decides what the children are.
| Tool | Use it to |
|---|---|
list_explodable_nodes | List the nodes that can explode (id, label, exploded, collapsing), the ids exploded and collapsing now, whether several can be open at once (multiple), and the readOnly reason if exploding is disabled. Call it first: it is the only source of explodable ids. |
explode_node | Explode node (an id from list_explodable_nodes). Unless multiple, the node exploded before collapses (it comes back in collapsed). Exploding an exploded node changes nothing (changed: false). Returns the ids now exploded. Then use get_editor_state or capture_view to read or see the children. |
collapse_node | Collapse an exploded node. Returns the ids still exploded and those still collapsing (their children leave when the collapse animation ends). Fails when the node is not exploded. |
Proposals (suggest, don't edit)
Hosts that let agents suggest additions add these. A proposal is new nodes and edges shown as ghosts
(not draggable, selectable or saved). Accepting adds them as one undo step, recorded as made by
whoever proposed it (agent:<id>); rejecting leaves the canvas and its history untouched.
| Tool | Use it to |
|---|---|
propose_nodes | Suggest nodes and edges without adding them: title (also the undo step name), rationale, nodes (each optional id, type, data, position, width, height, nearNodeId, side), edges (source, target, label). A node with nearNodeId (a canvas node or an earlier node of the proposal) is placed beside it and connected from it. Returns proposalId, the planned node ids and positions, edge ids and anchors (the canvas nodes it builds on). Read ids with get_editor_state first. |
list_proposals | List pending proposals: id, title, rationale, origin, nodes, edges, anchors, missing (anchors since removed: it can no longer be accepted) and the readOnly reason if accepting is disabled. Call it before accept_proposal or reject_proposal. |
accept_proposal | Add a pending proposal (proposalId) to the canvas as one undo step and drop it from the list. Returns the added nodeIds, edgeIds and historyLabel. Refuses, changing nothing, when the canvas is read-only or an anchor is missing. Only when the user asked. |
reject_proposal | Dismiss a pending proposal (proposalId): its ghosts go and nothing else changes. Use it to withdraw a suggestion that no longer fits. Returns the proposalId and title. |
Write: components
These are strict-schema tools. Each returns { ids } of what it created or changed, or fails with
the reason (for example a variant combination that already exists, or a cycle).
| Tool | Use it to |
|---|---|
create_component | Turn one frame-like node (ids) into a main component, or wrap several sibling layers in a new component frame. Returns the component id. |
combine_as_variants | Wrap sibling components in a component set. Names already in Property=Value form become variant values; others get Property 1=Default, Variant2, ... Rename afterwards with rename_variant_property / rename_variant_value. |
create_instance | Place an instance of componentId (a component, or a set's default variant), optionally inside frame parentId, at x / y (canvas coordinates, or relative to the parent), with properties (variant and component property values). Returns the instance id. |
set_instance_properties | Set property values on instances (ids): a variant property switches the variant (compatible overrides kept), a boolean toggles a layer, a text property sets text, an instance-swap property takes a component id. Get names from inspect_instance. |
swap_instance | Make instances show another component (componentId), keeping overrides on layers whose names match. |
reset_overrides | Reset overrides of a whole instance (default), of one layer (pass the layer as id), or one group such as data.label or shape.fill. Destructive. |
push_overrides | Copy an instance's overrides to its main component, and so to every other instance. Destructive for other instances' look: use it only when the user wants the change everywhere. |
detach_instance | Turn instances into plain frames that keep their look and stop following the main. Destructive; prefer properties or swap_instance. |
go_to_main_component | Select an instance's main component and get its id, to edit the main itself. |
restore_main_component | Recreate a deleted main component from one of its instances (its old id comes back, so other instances re-link). |
add_variant | Add a variant to set setId by duplicating from (default the set's default variant) with new variant values such as { "State": "Hover" }; unnamed properties keep the source's values, a new property name is added to every variant as Default. x / y are optional (default next to the source). Without values the next VariantN is used. |
remove_variant | Remove variant id from its set (never the last one); its instances switch to the closest remaining variant, keeping overrides. Destructive. |
set_variant_properties | Merge values into variant id's property values. A combination another variant has is refused. |
rename_variant_property | Rename property name to newName across a set; instances keep their variant. |
rename_variant_value | Rename value of property to newValue across a set; instances keep their variant. |
Save and open
The host may add the shared document tools (see pixerate-agent-tools). A canvas document is { nodes, edges, scenes, viewport }: every node (id, position, data, type, parentId...) and edge without selection, measurement or the fields the host leaves out, plus the saved scenes and, when the host saves it, the viewport ({ x, y, zoom }) the canvas was looking at. Large inline images in node data may come back as pixerate-asset: references: pass them back unchanged, they are only valid in this session.
| Tool | Use |
|---|---|
get_canvas_document | Take a copy of the whole canvas as { kind, formatVersion, revision, document } before a risky rewrite, or to hand it on. To read the canvas, use get_editor_state: it is smaller and says more. Refuses very large canvases. |
load_canvas_document | Put back a copy from get_canvas_document (its document, or the whole result). Checked first: duplicate ids, edges to missing nodes, parents that do not exist, a bad viewport and anything the host's own node check rejects (the error names the node and says why) are refused and nothing changes. A viewport in the document moves the view; leave it out to keep the current view. One undo step (undo_canvas brings the previous canvas back). To add or change a few nodes use canvas_add_node / canvas_update_node. |
undo_canvas | Undo the last canvas change (one tool call, command or user gesture such as a drag). Returns undone (the step's name) and whether more can be undone. Fails with "Nothing to undo." when there is nothing. |
redo_canvas | Redo the last undone canvas change. |
save_canvas_document | Save the canvas under a name in the host's storage (the same name replaces that copy). Returns the key. Only listed when the host has storage. Save when the user asks, not after every edit. |
list_saved_canvas_documents | List saved canvases (key, name, savedAt), newest first. |
open_saved_canvas_document | Replace the canvas with a saved one by key. One undo step. Ask before opening over changes the user has not saved. |
Commands
Run these through run_command / run_commands. Each takes ids (default: the selection) unless
noted, is one undo step, and reports in list_commands why it cannot run.
Arrange
| Command | Use it to |
|---|---|
canvas.alignLeft | Align the left edges of two or more nodes to the selection bounds. |
canvas.alignCenter | Align horizontal centres (a vertical centre line). |
canvas.alignRight | Align right edges. |
canvas.alignTop | Align top edges. |
canvas.alignMiddle | Align vertical centres (a horizontal centre line). |
canvas.alignBottom | Align bottom edges. |
canvas.distributeHorizontal | Give three or more nodes equal horizontal gaps; the outermost stay put. |
canvas.distributeVertical | Give three or more nodes equal vertical gaps. |
canvas.lock | Lock nodes: they cannot be moved, resized, restacked, deleted or combined, but stay selectable and act as alignment anchors. |
canvas.unlock | Unlock nodes, restoring their previous drag and delete flags. |
canvas.bringForward | Move nodes one step up among their siblings (past the next overlapping sibling). |
canvas.sendBackward | Move nodes one step down among their siblings. |
canvas.bringToFront | Put nodes on top of their siblings. |
canvas.sendToBack | Put nodes below their siblings (backgrounds). |
Shapes
These act on shape nodes (data.shape) and replace their inputs with a new path shape (curves
are kept exactly). They need unlocked shapes; other selected nodes are ignored.
| Command | Use it to |
|---|---|
canvas.unite | Merge two or more shapes into one outline. |
canvas.intersect | Keep only the area all shapes share; refused when they do not overlap. |
canvas.subtract | Cut every other shape out of the back-most one (stacking order decides which is the base). |
canvas.exclude | Keep the areas covered an odd number of times (XOR). |
canvas.divide | Split overlapping shapes into one shape per region, each painted like the top shape covering it. |
canvas.offsetPath | Grow (distance > 0) or shrink (< 0) shapes by distance canvas units (default 10), with join miter (default), round or bevel and miterLimit (default 4). |
canvas.outlineStroke | Turn shapes' strokes into filled shapes: join (default miter), cap butt (default), round or square, miterLimit (default 4). The fill stays as a separate shape below. |
Draw
Freehand strokes are path shape nodes, so they select, move, group, erase and export like any
shape. A stroke with data.shape.brush is painted with a dry paint brush instead of an SVG stroke
(the path stays the data; the paint is rendered from it); brush.paint gives it gradient paint, sampled dab by dab
along the path, instead of the stroke colour). New brushed strokes store a brush.seed (their
character: seeded jitter and the paper grain they catch) and, when a width was chosen, brush.size.
| Command | Use it to |
|---|---|
canvas.drawFreehand | Draw a pen or highlighter stroke through points ([x, y] pairs in canvas units, in drawing order; one undo step). tool picks the defaults (pen: blue, width 3; highlighter: yellow, width 24, opacity 0.35); color, width, opacity override them (with a brush and no color, the brush's default colour if it has one; with a brush, width is the brush diameter and defaults to the preset's own size). Pass brush (a preset or saved brush id from canvas.listBrushes, or tip settings) to paint it with a dry brush, plus pressure / tiltX / tiltY profiles spread along its length ([0.2, 1, 0.3] swells in the middle). Add paint (only with brush) for gradient paint that changes colour along the stroke: { "type": "gradient", "stops": [...], "map": { "by": "distance", "length": 200 } } (length in canvas units), or by pressure, tilt, direction, across-tip, random. Returns the node id, its bounds and the stored brush (with paint normalised: defaults filled in, colours lowercased). |
canvas.selectBrush | Choose the brush the person draws with by hand (brushId from canvas.listBrushes, or null for a plain vector stroke), at its own size, taking its default colour unless the colour is locked or keepColour. Draws nothing. Returns { brush, color, colorScheme, colorLocked, colorFollowsScheme }. |
canvas.setColorLocked | Lock (locked: true) or unlock the hand-drawing colour ("Keep my colour"): while locked, canvas.selectBrush keeps it. Returns the same settings as canvas.selectBrush; call it (or canvas.selectBrush) to read whether the person locked it. |
canvas.setColorScheme | Set scheme light or dark: picks the side of light / dark default colours for canvas.selectBrush and canvas.drawFreehand; a colour taken from such a default follows, strokes already drawn keep theirs. Usually the host's job. Returns the same settings as canvas.selectBrush. |
canvas.listBrushes | Read-only: the brushes canvas.drawFreehand and the gestures accept as brush: the dry presets, then saved brushes from the brush library the canvas can paint (id, name, category, the brush's own size, which a stroke uses unless you pass width, and builtin). Call it before the first brushed stroke instead of guessing ids. |
Gestures draw one hand-drawn stroke node each (one undo step) through the same path as
canvas.drawFreehand, and the agent's cursor traces them. Targets are boxes
{ x, y, width, height } in canvas units, nodes { nodeId } (where the host can look nodes up)
or, where noted, points [x, y]. Every gesture also takes tool, color, width, opacity,
brush (brushed marks get an uneven hand pressure) and seed (default: derived from the
request). Each returns the node id, its bounds, the brush when brushed and the seed used;
pass the id to canvas_remove_nodes (or undo) to take a mark away.
| Command | Use it to |
|---|---|
canvas.circle | Circle the pick or the subject: around is a list of boxes or nodes, ringed together by a loose, slightly open ellipse that never cuts into them; padding (default 8) widens it. |
canvas.underline | Stress a line of text or a value: a stroke just under under's bottom edge (offset, default 4), a little longer or shorter than it; wave: true for a squiggle. |
canvas.arrow | Point from one thing to another (a swatch to where it is used, a note to its subject): from and to are boxes, nodes or points; boxes are reached at their nearest edge, gap (default 6) clear of it. curve −1..1 bends the shaft (0 straight). Ends that overlap are refused. |
canvas.bracket | Group items or a range of lines: a curly (default) or shape: "square" bracket on one side (left, right, top, bottom) of around, spanning them end to end, padding (default 8) away; depth sets how far it reaches out. Add a note beside it. |
canvas.strike | Strike a line through the middle of over (text or a value to drop). It only marks it; nothing is deleted. |
canvas.cross | Cross out a rejected option with an X over over. One stroke, so the pen runs back over part of the first diagonal. Nothing is deleted. |
canvas.check | Tick an approved option or a done item: at is a box or node (ticked in its middle) or a point; size is the tick's height (default from the box, 20 at a point). |
Components (command form)
The same operations as the component tools, for command palettes and run_commands batches.
Prefer the dedicated tool for a single operation: its schema is strict and its errors precise. Use
the command when you need it inside an atomic batch with arrange or animation steps, or when the
host registered only commands.
| Command | Use it to |
|---|---|
canvas.listComponents | Read-only; same result as list_components. |
canvas.inspectInstance | Read-only; same as inspect_instance (id). |
canvas.createComponent | Same as create_component (ids). |
canvas.combineAsVariants | Same as combine_as_variants (ids). |
canvas.createInstance | Same as create_instance (componentId, parentId, x, y, properties). |
canvas.setInstanceProperties | Same as set_instance_properties (properties, ids). |
canvas.swapInstance | Same as swap_instance (componentId, ids). |
canvas.resetOverrides | Same as reset_overrides (id, group). |
canvas.pushOverrides | Same as push_overrides (id). |
canvas.detachInstance | Same as detach_instance (ids). |
canvas.goToMainComponent | Same as go_to_main_component (id). |
canvas.restoreMainComponent | Same as restore_main_component (id). |
canvas.addVariant | Same as add_variant (setId, from, variant, x, y). |
canvas.removeVariant | Same as remove_variant (id). |
canvas.setVariantProperties | Same as set_variant_properties (values, id). |
canvas.renameVariantProperty | Same as rename_variant_property (name, newName, setId). |
canvas.renameVariantValue | Same as rename_variant_value (property, value, newValue, setId). |
Animation
A node's animation is a set of property tracks of keyframes { time (ms), value, easing }; the
easing applies to the segment leaving a keyframe. Animatable properties: x, y, width,
height, rotation (degrees), scale, opacity (0 to 1), fill, stroke, strokeWidth,
cornerRadius, motionPath (0 to 1). Easings: linear, ease, ease-in, ease-out,
ease-in-out, ease-in-back, ease-out-back, spring, hold, or cubic-bezier(x1, y1, x2, y2).
| Command | Use it to |
|---|---|
canvas.animation.animateNode | The main authoring command: animate nodeId from from (default: its current values) to to over duration ms (default 1000), starting at delay ms (default 0), with easing. Stagger several nodes with increasing delay in one atomic run_commands. |
canvas.animation.addKeyframe | Key nodes (ids, default selected) at time ms (default the playhead): the listed properties, else their animated ones, else x and y. Set the node's values first, then key. |
canvas.animation.setEasing | Set easing on the segments leaving the selected keyframes. Needs a keyframe selection. |
canvas.animation.deleteKeyframes | Delete the selected keyframes. Needs a keyframe selection. |
canvas.animation.seek | Move the playhead to time ms (required). Returns the time. |
canvas.animation.play | Start playback (refused when already playing). |
canvas.animation.pause | Pause playback (refused when not playing). |
canvas.animation.togglePlay | Play or pause; returns playing and time. |
canvas.animation.stop | Stop and return to the start. |
canvas.animation.toggleRecord | Toggle record mode, in which changing any property at the playhead keys it. Returns recording; turn it off when done. |
canvas.animation.nextKeyframe | Jump the playhead to the next keyframe. |
canvas.animation.previousKeyframe | Jump the playhead to the previous keyframe. |
canvas.animation.exportCss | Read-only: CSS @keyframes for nodeId's animation, as { css, exact }. exact: false means some tracks were sampled (OKLab colour tweens, x / y with different timing, motion paths). |
Provider effects
These tools come from createCanvasEffectsToolProvider. They work once the host registers an
effects provider (for example Flair); without one the read tools say so and the write tools refuse.
| Tool | Use it to |
|---|---|
search_effects | Find effects: use generator for fills, overlay, title or interactive for overlays, and transition for scene transitions. |
describe_effect | Read an effect's controls, ranges, defaults and presets before applying it. |
list_applied_effects | List the shape nodes that carry effects, with each effect's role (fill or overlay), opacity and values. available false with a reason means the registered provider cannot draw it (it was saved from another provider, or the provider lacks it): the node shows a hatch until you re-apply one from search_effects. |
apply_effect | Put an effect on a shape node: a generator fills the shape (clipped to its outline), anything else draws over its box. This replaces any effect already there. Returns the effect. |
update_effect | Change a node's effect (effect is "effect"): values (null resets a control), opacity, and enabled (false holds the first frame). |
remove_effect | Take the effect off a node. |
set_canvas_effect_mode | Switch a node's effect between fill and overlay. |
list_scene_transitions | List the Scenes in order, each with the transition played when moving to it. |
set_scene_transition | Set (or with effect null, remove) the transition effect played when moving to a scene, with its duration in ms. |
Pitfalls
-
Proposals are not on the canvas. Their ids are not in
get_editor_stateuntil accepted, andcanvas_connectorcanvas_update_nodeon them fails. Put the whole idea in one proposal (an edge between two proposed nodes goes in itsedges), or wait for the user to accept it. -
A shape node holds one effect.
apply_effectreplaces the effect that is there. Effects draw on shape nodes only (nodes withdata.shape). -
Effects belong to the provider they came from. Each saved effect keeps its provider id. After the provider changes (another effects library), check
list_applied_effectsforavailable: falseand re-apply those effects with ids from the new provider'ssearch_effects; the old effect ids mean nothing to it. -
Brushes are dry only. The canvas profile (
CANVAS_PROFILE, seepixerate-brushes) refuses wet and thick brushes (watercolour, gouache, wet oils, impasto), particle and sticker brushes, tools (mixer, blender, smudge, eraser) and light brushes, saying why and which ids to use: wet paint needs an area to flow in, tools need paint under them, light needs the paint editor's light channel. Saved brushes from the brush library work when they are dry (check_brushwithhost: "canvas"says, and gives adegradedbrush to save instead); a stroke keeps a copy of the saved brush, so it renders without the library.pressureand tilt without abrushare refused too. -
Gradient paint needs a brush, and no timing.
paintwithoutbrushis refused; usecolorfor a plain stroke. Canvas strokes record no timing, sovelocityandtimemappings are refused: usedistancefor colour that changes along the stroke. The strokecolorstill sets the alpha and fills"paint"stops ("secondary"is white). A distancelengthshorter than the stroke withrepeat: "mirror"(the default) runs back and forth; useclampand a length near the stroke's to go from one end to the other once. -
A canvas gradient in
colordoes not reach the brush: a brushed stroke with a gradientcolorand nopaintuses its first stop. Passpaintinstead. -
Gestures need a lookup for node ids. Where the host's draw target has no
nodeBounds, a{ nodeId }target is refused with that reason: pass the node's box fromget_editor_state. A point is refused where a box is needed (around,under,over). -
Gestures are marks, not edits. A strike or a cross does not delete or change the node under it, and a mark does not move with the node; take it away (
canvas_remove_nodeswith itsid) and draw it again after moving things. -
Shape operations drop the brush. Unite, subtract, offset path and outline stroke turn brushed strokes into plain filled or stroked paths.
-
canvas_*tools validate ids against the current state; an "Unknown node id" error means callget_editor_stateagain, not guess. -
canvas_add_nodewithnearNodeIdalso adds an edge from that node. Pass an explicitposition(and nonearNodeId) for a free-standing design element. -
With agent playback on, the cursor clicks where
canvas_add_nodewill place the node, dragscanvas_connectfrom source to target, and rings the nodes a layout or arrange command moved. Nothing about the calls changes: still read ids withget_editor_stateand pass them back. -
Exploded children are temporary. Do not edit, connect or lay out nodes that only exist while a node is exploded: they vanish on collapse. Collapse before
canvas_layout, and do not save positions read while a node is exploded (the moved nodes return to their old places). -
canvas_update_nodechangesdataonly. Move nodes with layout, align or distribute commands, or by the host's own means; do not expectpositioninsidedatato move anything. -
Align needs at least two nodes and distribute at least three; locked nodes never move. Shape booleans refuse locked shapes and empty results, and edges attached to consumed shapes are left dangling: reconnect or remove them.
-
Subtract uses stacking order (back-most is the base), not selection order; restack first.
-
Instances never gain or lose layers. Deleting a layer inside an instance hides it; nodes added inside an instance move out of it. Edit the main to change structure.
-
push_overrides,detach_instance,remove_variantandreset_overrideschange other instances or discard work; ask before using them on the user's design. -
Variant edits refuse a combination another variant already has, empty names, and names containing
,or=. -
An instance cannot be placed inside a component it contains (cycle); place it outside.
-
Animation times are milliseconds;
rotationis degrees andopacity0 to 1 (token bindings use percent for opacity, animation does not).