CommunityCoding & Developmentgithub.com

Pixerate/pixerate-canvas

How to build and edit a Pixerate design canvas or node graph through its agent tools - drawing freehand pen, highlighter and dry-brush strokes (canvas.drawFreehand), annotating with hand-drawn circles, underlines, arrows, brackets, strikes, crosses and ticks (canvas.circle, canvas.arrow), adding, connecting, updating and laying out nodes (canvas_add_node, canvas_layout), reading state with get_editor_state and looking at it with view_editor, reusable components with variants, instances and overrides (create_component, add_variant, set_instance_properties), align, distribute, lock and layer order, shape boolean operations (unite, subtract, offset path, outline stroke), auto layout frames, and keyframe animation with easing and CSS export. Use when working on a canvas, whiteboard, flow chart, node graph, frame, design system, component library, button variants, layout, alignment, z-order or a canvas animation in a Pixerate editor.

What is pixerate-canvas?

pixerate-canvas is a Cursor agent skill that how to build and edit a Pixerate design canvas or node graph through its agent tools - drawing freehand pen, highlighter and dry-brush strokes (canvas.drawFreehand), annotating with hand-drawn circles, underlines, arrows, brackets, strikes, crosses and ticks (canvas.circle, canvas.arrow), adding, connecting, updating and laying out nodes (canvas_add_node, canvas_layout), reading state with get_editor_state and looking at it with view_editor, reusable components with variants, instances and overrides (create_component, add_variant, set_instance_properties), align, distribute, lock and layer order, shape boolean operations (unite, subtract, offset path, outline stroke), auto layout frames, and keyframe animation with easing and CSS export. Use when working on a canvas, whiteboard, flow chart, node graph, frame, design system, component library, button variants, layout, alignment, z-order or a canvas animation in a Pixerate editor.

Works with~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/Pixerate/editor/tree/HEAD/packages/core/skills/pixerate-canvas

Installed? Explore more Coding & Development skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation

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 (mode horizontal / vertical / grid, gap, padding, alignMain, alignCross, sizing); its children are the nodes whose parentId is 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 }) and data.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

  1. Read. get_editor_state with includeDocument: false returns canvas.nodes and canvas.edges. For a design system also call list_components, and inspect_instance before changing an instance.
  2. 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.
  3. Write. Use the dedicated tools for graph edits (canvas_add_node, canvas_connect, canvas_update_node) and components (create_component, create_instance, ...). Use run_commands with atomic: true for 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).
  4. Tidy. canvas_layout for graphs; canvas.align* / canvas.distribute* for designs; layer order with canvas.bringToFront and friends; lock finished backgrounds with canvas.lock.
  5. Look. view_editor (default viewport, JPEG, 1024 px) once a batch is done; use scope: "canvas" for the whole drawing. Fix overlaps, misalignment and clipped text, then show the user with canvas_select or canvas_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_variants for states and sizes (State=Default, State=Hover, Size=Small), then create_instance everywhere. 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 to data.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_instance for 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 zIndex per sibling group and never reorder the array.

  • Graph layout. Lay flows out in one direction (LR for pipelines, TB for hierarchies), keep edges short, label edges only when the relationship is not obvious.

  • Motion. Use ease-out (or ease-out-back for a little overshoot) for things arriving, ease-in for things leaving, ease-in-out for moves between two resting places, and linear only 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 with delay rather than moving everything at once. Animate opacity, x, y, scale and rotation; 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 pressure profile (tapered ends: [0.2, 0.9, 1, 0.9, 0.2]) rather than many points of different widths. Leave width out to use the preset's own size (canvas.listBrushes gives 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; pass color to 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.selectBrush chooses 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 pass keepColour. 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 affect canvas.drawFreehand, which takes its own brush and color.

  • 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 for canvas.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 (marker or ink-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 another seed for 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

ToolUse it to
get_editor_stateRead 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_editorGet 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_viewScreenshot 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_componentsList 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_instanceDescribe 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

ToolUse it to
canvas_add_nodeAdd 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_connectAdd an edge from source to target (two different existing nodes), with an optional label. Returns the edge id.
canvas_update_nodeMerge 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_nodesDelete nodes and their edges; every id must exist. Destructive: confirm with the user before removing content they made.
canvas_layoutArrange 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_selectReplace 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_highlightFlash 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.

ToolUse it to
list_explodable_nodesList 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_nodeExplode 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_nodeCollapse 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.

ToolUse it to
propose_nodesSuggest 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_proposalsList 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_proposalAdd 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_proposalDismiss 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).

ToolUse it to
create_componentTurn 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_variantsWrap 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_instancePlace 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_propertiesSet 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_instanceMake instances show another component (componentId), keeping overrides on layers whose names match.
reset_overridesReset 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_overridesCopy 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_instanceTurn instances into plain frames that keep their look and stop following the main. Destructive; prefer properties or swap_instance.
go_to_main_componentSelect an instance's main component and get its id, to edit the main itself.
restore_main_componentRecreate a deleted main component from one of its instances (its old id comes back, so other instances re-link).
add_variantAdd 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_variantRemove variant id from its set (never the last one); its instances switch to the closest remaining variant, keeping overrides. Destructive.
set_variant_propertiesMerge values into variant id's property values. A combination another variant has is refused.
rename_variant_propertyRename property name to newName across a set; instances keep their variant.
rename_variant_valueRename 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.

ToolUse
get_canvas_documentTake 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_documentPut 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_canvasUndo 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_canvasRedo the last undone canvas change.
save_canvas_documentSave 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_documentsList saved canvases (key, name, savedAt), newest first.
open_saved_canvas_documentReplace 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

CommandUse it to
canvas.alignLeftAlign the left edges of two or more nodes to the selection bounds.
canvas.alignCenterAlign horizontal centres (a vertical centre line).
canvas.alignRightAlign right edges.
canvas.alignTopAlign top edges.
canvas.alignMiddleAlign vertical centres (a horizontal centre line).
canvas.alignBottomAlign bottom edges.
canvas.distributeHorizontalGive three or more nodes equal horizontal gaps; the outermost stay put.
canvas.distributeVerticalGive three or more nodes equal vertical gaps.
canvas.lockLock nodes: they cannot be moved, resized, restacked, deleted or combined, but stay selectable and act as alignment anchors.
canvas.unlockUnlock nodes, restoring their previous drag and delete flags.
canvas.bringForwardMove nodes one step up among their siblings (past the next overlapping sibling).
canvas.sendBackwardMove nodes one step down among their siblings.
canvas.bringToFrontPut nodes on top of their siblings.
canvas.sendToBackPut 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.

CommandUse it to
canvas.uniteMerge two or more shapes into one outline.
canvas.intersectKeep only the area all shapes share; refused when they do not overlap.
canvas.subtractCut every other shape out of the back-most one (stacking order decides which is the base).
canvas.excludeKeep the areas covered an odd number of times (XOR).
canvas.divideSplit overlapping shapes into one shape per region, each painted like the top shape covering it.
canvas.offsetPathGrow (distance > 0) or shrink (< 0) shapes by distance canvas units (default 10), with join miter (default), round or bevel and miterLimit (default 4).
canvas.outlineStrokeTurn 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.

CommandUse it to
canvas.drawFreehandDraw 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.selectBrushChoose 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.setColorLockedLock (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.setColorSchemeSet 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.listBrushesRead-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.

CommandUse it to
canvas.circleCircle 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.underlineStress 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.arrowPoint 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.bracketGroup 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.strikeStrike a line through the middle of over (text or a value to drop). It only marks it; nothing is deleted.
canvas.crossCross 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.checkTick 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.

CommandUse it to
canvas.listComponentsRead-only; same result as list_components.
canvas.inspectInstanceRead-only; same as inspect_instance (id).
canvas.createComponentSame as create_component (ids).
canvas.combineAsVariantsSame as combine_as_variants (ids).
canvas.createInstanceSame as create_instance (componentId, parentId, x, y, properties).
canvas.setInstancePropertiesSame as set_instance_properties (properties, ids).
canvas.swapInstanceSame as swap_instance (componentId, ids).
canvas.resetOverridesSame as reset_overrides (id, group).
canvas.pushOverridesSame as push_overrides (id).
canvas.detachInstanceSame as detach_instance (ids).
canvas.goToMainComponentSame as go_to_main_component (id).
canvas.restoreMainComponentSame as restore_main_component (id).
canvas.addVariantSame as add_variant (setId, from, variant, x, y).
canvas.removeVariantSame as remove_variant (id).
canvas.setVariantPropertiesSame as set_variant_properties (values, id).
canvas.renameVariantPropertySame as rename_variant_property (name, newName, setId).
canvas.renameVariantValueSame 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).

CommandUse it to
canvas.animation.animateNodeThe 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.addKeyframeKey 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.setEasingSet easing on the segments leaving the selected keyframes. Needs a keyframe selection.
canvas.animation.deleteKeyframesDelete the selected keyframes. Needs a keyframe selection.
canvas.animation.seekMove the playhead to time ms (required). Returns the time.
canvas.animation.playStart playback (refused when already playing).
canvas.animation.pausePause playback (refused when not playing).
canvas.animation.togglePlayPlay or pause; returns playing and time.
canvas.animation.stopStop and return to the start.
canvas.animation.toggleRecordToggle record mode, in which changing any property at the playhead keys it. Returns recording; turn it off when done.
canvas.animation.nextKeyframeJump the playhead to the next keyframe.
canvas.animation.previousKeyframeJump the playhead to the previous keyframe.
canvas.animation.exportCssRead-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.

ToolUse it to
search_effectsFind effects: use generator for fills, overlay, title or interactive for overlays, and transition for scene transitions.
describe_effectRead an effect's controls, ranges, defaults and presets before applying it.
list_applied_effectsList 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_effectPut 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_effectChange a node's effect (effect is "effect"): values (null resets a control), opacity, and enabled (false holds the first frame).
remove_effectTake the effect off a node.
set_canvas_effect_modeSwitch a node's effect between fill and overlay.
list_scene_transitionsList the Scenes in order, each with the transition played when moving to it.
set_scene_transitionSet (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_state until accepted, and canvas_connect or canvas_update_node on them fails. Put the whole idea in one proposal (an edge between two proposed nodes goes in its edges), or wait for the user to accept it.

  • A shape node holds one effect. apply_effect replaces the effect that is there. Effects draw on shape nodes only (nodes with data.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_effects for available: false and re-apply those effects with ids from the new provider's search_effects; the old effect ids mean nothing to it.

  • Brushes are dry only. The canvas profile (CANVAS_PROFILE, see pixerate-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_brush with host: "canvas" says, and gives a degraded brush to save instead); a stroke keeps a copy of the saved brush, so it renders without the library. pressure and tilt without a brush are refused too.

  • Gradient paint needs a brush, and no timing. paint without brush is refused; use color for a plain stroke. Canvas strokes record no timing, so velocity and time mappings are refused: use distance for colour that changes along the stroke. The stroke color still sets the alpha and fills "paint" stops ("secondary" is white). A distance length shorter than the stroke with repeat: "mirror" (the default) runs back and forth; use clamp and a length near the stroke's to go from one end to the other once.

  • A canvas gradient in color does not reach the brush: a brushed stroke with a gradient color and no paint uses its first stop. Pass paint instead.

  • 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 from get_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_nodes with its id) 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 call get_editor_state again, not guess.

  • canvas_add_node with nearNodeId also adds an edge from that node. Pass an explicit position (and no nearNodeId) for a free-standing design element.

  • With agent playback on, the cursor clicks where canvas_add_node will place the node, drags canvas_connect from source to target, and rings the nodes a layout or arrange command moved. Nothing about the calls changes: still read ids with get_editor_state and 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_node changes data only. Move nodes with layout, align or distribute commands, or by the host's own means; do not expect position inside data to 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_variant and reset_overrides change 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; rotation is degrees and opacity 0 to 1 (token bindings use percent for opacity, animation does not).

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

Pixerate/pixerate-animation-editor

How to author and edit vector animations (motion graphics, logo reveals, animated icons, explainer scenes, UI micro-interactions) in the Pixerate animation editor through its agent tools - artwork SVG with ids, shots of commands (enter, exit, draw, move, rotate, scale, fade, morph, Smart Animate match, follow a path, pulse, shake, wiggle), easing and springs, stagger, camera, beat and voiceover cues, drivers, reusable clips, checking, rendering frames and contact sheets, and exporting to Flair or Lottie. Use when animation tools such as get_animation, insert_shots or edit_animation are available, or when asked to animate, time, ease, stagger or export vector motion, and provider motion and vector effects (for example Flair) applied to nodes.

Pixerate/pixerate-brushes

How to find, describe, create, change, check and share brushes in the Pixerate brush library through list_brush_presets, describe_brush, create_brush, update_brush, duplicate_brush, remove_brush, check_brush, export_brushes, import_brushes, import_brush_pack, preview_brush, add_brush_source and list_brush_sources - brushes made of facets (kind, medium and variety or tool, tip, marks, dynamics, behaviour, light, motion, colour), image tips and tip grain made from image sources, the built-in presets, and the host profiles that say what the paint editor, image editor, canvas and vector strokes can render. Use when asked to make, design, tweak, save, copy, delete, export, import or check a brush, make a brush from an image (a scanned mark, a leaf, a texture), give a brush grain, bring in a Photoshop .abr or Procreate .brush / .brushset pack, find a brush for a medium (pencil, ink, watercolour, oil, airbrush, highlighter), or make a brush work in another editor.

Pixerate/pixerate-vector

How to draw and edit vector artwork in the Pixerate vector editor through its vector.* commands - adding shapes and SVG paths, text (point, area, on a path, styles, Create Outlines), symbols and instances, CAD constraints, artboards and frames with resizing constraints, connectors, stacked fills and strokes with blend modes, brushstrokes with brush character and paper grain, drop shadows, glows and blurs, clipping and opacity masks, gradients (linear, radial, conic, mesh, freeform), stroke alignment, arrowheads, variable-width and tapered strokes, live corner radii, boolean operations and Pathfinder (unite, subtract, divide, trim), Shape Builder, paint bucket, path effects (trim, repeater, zig zag, offset), envelope warps and free distort, blends and morphing, anchor editing (join, smooth, simplify, knife, eraser), align, distribute, group, and SVG import and export. Use when creating an icon, logo, illustration, glyph, badge or Bezier path artwork, or editing an SVG in a Pixerate vector editor.

Pixerate/pixerate-video-editor

How to edit video projects in the Pixerate non-linear video editor through its agent tools - adding video, audio and image assets, building a rough cut on video and audio tracks, inserting, overwriting, moving, trimming, splitting and ripple-deleting clips, slip, slide and roll edits, lift and extract of a time range, razor cuts across tracks, sync-locked tracks, keyframed opacity, volume and transform, closing gaps, titles and colour clips, fades, volume and speed, crossfade, dip and wipe transitions with handles, J and L cuts, markers, checking the project, adjustment clips over tracks, clip masks, skin-tone protected LUTs, edit plans proposed for review before they apply, provider effects (clip effects, effect clips, effect transitions), rendering frames and contact sheets, and exporting an ffmpeg command or a CMX 3600 EDL. Use when video tools such as get_video_project, add_video_clip or edit_video are available, or when asked to cut, trim, assemble, sequence or export footage on a timeline.

Related Skills