Skip to main content
Glama

Create a .riv file from a scene spec

riv_create

Create a working .riv animation file from a scene spec without the Rive editor, validate it with the official runtime, and return a rendered preview frame.

Instructions

Create a working .riv animation file from scratch (no Rive editor needed) and validate it with the official runtime. Returns a rendered preview frame. For polished results (gradients, organic bezier curves, easing, springy motion), read the "rive-design-guidelines" prompt this server exposes before designing a non-trivial scene. Scene spec example: { "artboard": {"name":"Demo","width":400,"height":300}, "backgroundColor": "#1a1a2e", "shapes": [ {"id":"box","type":"rect","x":120,"y":150,"width":80,"height":80,"cornerRadius":12,"rotation":0,"opacity":1, "fill":{"color":"#e94560"},"stroke":{"color":"#fff","thickness":3}}, {"id":"ball","type":"ellipse","x":280,"y":150,"width":70,"height":70, "fill":{"gradient":{"type":"linear","stops":[{"color":"#00d9ff"},{"color":"#0066ff"}]}}}, {"id":"tri","type":"polygon","x":200,"y":100,"points":[{"x":0,"y":-40},{"x":35,"y":20},{"x":-35,"y":20}],"fill":{"color":"#ffd700"}} ], "animations": [ {"name":"spin","fps":60,"duration":60,"loop":"loop","tracks":[ {"target":"box","property":"rotation","keyframes":[{"frame":0,"value":0},{"frame":60,"value":360,"easing":"linear"}]}, {"target":"ball","property":"y","keyframes":[{"frame":0,"value":150},{"frame":30,"value":80,"easing":"ease-out"},{"frame":60,"value":150,"easing":"ease-in"}]} ]} ], "stateMachine": {"name":"SM","inputs":[{"name":"go","type":"bool"}], "states":[{"name":"spinning","animation":"spin"}], "transitions":[{"from":"entry","to":"spinning","condition":{"input":"go"}}]} } Character animation (images/groups/mesh): { "groups": [{"id":"rig","x":300,"y":200}], "images": [{"id":"chara","pngPath":"./cat.png","x":0,"y":0,"scale":0.25,"parent":"rig", "mesh":{"columns":6,"rows":6}}], "animations": [{"name":"idle","duration":240,"loop":"loop","tracks":[ {"target":"rig","property":"y","keyframes":[{"frame":0,"value":200},{"frame":120,"value":195,"easing":"ease-in-out"},{"frame":240,"value":200,"easing":"ease-in-out"}]}, {"target":"chara#v0_3","property":"x","keyframes":[{"frame":0,"value":0},{"frame":120,"value":40,"easing":"ease-in-out"},{"frame":240,"value":0,"easing":"ease-in-out"}]} ]}] }

  • layouts[]: Flex nodes {id,parent?,width?,height?,widthMode?,heightMode?,widthWeight?,heightWeight?,direction?,justifyContent?,alignItems?,gap?,padding?,margin?,wrap?,fillColor?}. Modes: fixed (default), percent (width/height is 0-100 percentage), auto (hug contents), fill (remaining space; optional positive weight per axis). gap: number or {horizontal?,vertical?}; padding/margin: number or {left?,right?,top?,bottom?}. Parent must be an earlier layout id. Shapes themselves do not become Flex items.

  • images[].pngPath: PNG file embedded into the .riv. mesh enables vertex deformation; vertices addressed as "#v_" (row 0 = top), coordinates in the image's natural pixel space centered at origin. Mesh vertex tracks support x/y only.

  • texts[].followPath {"path":"<shapes[].id>","start":0,"end":1,"offset":0,"strength":1,"orient":true} bends the text along that shape's path (text on path; start/end/offset are 0-1 fractions of the path length). Needs fonts[] like any text.

  • groups are Nodes usable as parents (parent) of shapes/images for rig hierarchies and pivots; animatable like shapes.

  • transitions support exitTimeMs (play source animation this long before transitioning). Motion presets (tuned amplitudes and easings; about 10x fewer tokens than hand-authored keyframes): "animations":[{"name":"intro","duration":90,"presets":[ {"preset":"pop-in","target":"logo"}, {"preset":"rise-in","targets":["c1","c2","c3"],"at":12,"stagger":4}, {"preset":"float","target":"logo"} ],"tracks":[]}] Available: fade-in rise-in drop-in slide-in pop-in bounce-in | fade-out sink-out slide-out pop-out | pulse heartbeat tada shake wobble | breathing float sway spin glow-pulse blink(for eyelid overlays). Options: at(start frame), stagger(frames between targets), intensity(0.25-3), direction(left|right|up|down), cycleSeconds. Ambient presets (breathing..blink) span the whole animation seamlessly. A preset and a manual track must not drive the same target+property. Pro features: stroke.trim {start,end,mode} + trimStart/trimEnd tracks (draw-on effect), shapes[].clipBy (mask via an invisible shape), groups[].solo+active + soloActive track with keyframes[].ref (pose/mouth switching), constraints [{type:"followPath",item,path}] + followDistance track 0-1 (motion along a path), open paths (closed:false), multi-contour shapes (subpaths), stroke cap/join. "imports":[{"spec":"logo.scene.json","x":200,"y":150,"scale":0.8}] places riv_import_svg / riv_asset_search fragments under a wrapper group (id = file basename) — animate the wrapper or individual shape ids. PREFER imported real vector art over drawing with primitives for anything illustrative. Recommended flow: riv_design_tokens → (riv_import_svg / riv_asset_search for artwork) → riv_create (token values + presets + imports) → riv_critique → fix → re-critique. Shape z-order: later in array = on top; images render above shapes. properties for tracks: x,y,rotation(deg),scaleX,scaleY,opacity(0-1),width,height,fillColor(needs "color" in keyframes). Colors: #RRGGBB or #AARRGGBB. rotation in degrees. Easings include emphasized-decel (enters) / emphasized-accel (exits). Audio: "audio":[{"id":"beep","path":"./beep.wav"}] embeds a WAV/MP3/FLAC file (path resolved relative to cwd, or pass bytes directly). "events":[{"id":"beepEvent","type":"audio","audio":"beep"}] declares an AudioEvent bound to that clip. Trigger it either from a state machine state ("states":[{"name":"s1","fireEvent":"beepEvent"}]) or at specific frames inside a timeline via animations[].events: {"name":"anim1","duration":60,"tracks":[...],"events":[{"event":"beepEvent","frame":0},{"event":"beepEvent","frame":30}]}. NOTE: this server's preview (Canvas2D) does not play audio, so PNG/GIF/video previews and riv_studio stay silent. The AudioAsset/AudioEvent are still written correctly and play in a GPU-backed Rive runtime (WebGL/Skia).

Tool call template

Replace each placeholder with a value matching the input schema. Only required parameters are included; add optional parameters as needed.

{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "arguments": {
      "outPath": "<outPath>",
      "scene": "<scene>"
    },
    "name": "riv_create"
  }
}

Input Schema

NameRequiredDescriptionDefault
sceneYesScene spec with optional Flex layouts (see tool description)
outPathYesOutput .riv path
previewTimeNoSeconds into first animation for the preview frame (default 0.4)

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed3 schema fields changedv0.12.0
    • changedInput schema / properties / scene / additionalProperties
      Previous value: -{}New value: +true
    • changedInput schema / properties / scene / description
      Previous value: -"Scene spec (see tool description for schema)"New value: +"Scene spec with optional Flex layouts (see tool description)"
    • addedInput schema / properties / scene / properties
      Added value: +{
      +  "layouts": {
      +    "items": {
      +      "additionalProperties": true,
      +      "properties": {
      +        "alignItems": {
      +          "enum": [
      +            "start",
      +            "center",
      +            "end"
      +          ],
      +          "type": "string"
      +        },
      +        "direction": {
      +          "enum": [
      +            "row",
      +            "column"
      +          ],
      +          "type": "string"
      +        },
      +        "fillColor": {
      +          "type": "string"
      +        },
      +        "gap": {
      +          "anyOf": [
      +            {
      +              "minimum": 0,
      +              "type": "number"
      +            },
      +            {
      +              "additionalProperties": false,
      +              "properties": {
      +                "horizontal": {
      +                  "minimum": 0,
      +                  "type": "number"
      +                },
      +                "vertical": {
      +                  "minimum": 0,
      +                  "type": "number"
      +                }
      +              },
      +              "type": "object"
      +            }
      +          ]
      +        },
      +        "height": {
      +          "minimum": 0,
      +          "type": "number"
      +        },
      +        "heightMode": {
      +          "enum": [
      +            "fixed",
      +            "percent",
      +            "auto",
      +            "fill"
      +          ],
      +          "type": "string"
      +        },
      +        "heightWeight": {
      +          "exclusiveMinimum": 0,
      +          "type": "number"
      +        },
      +        "id": {
      +          "type": "string"
      +        },
      +        "justifyContent": {
      +          "enum": [
      +            "start",
      +            "center",
      +            "end",
      +            "space-between"
      +          ],
      +          "type": "string"
      +        },
      +        "margin": {
      +          "anyOf": [
      +            {
      +              "minimum": 0,
      +              "type": "number"
      +            },
      +            {
      +              "additionalProperties": false,
      +              "properties": {
      +                "bottom": {
      +                  "minimum": 0,
      +                  "type": "number"
      +                },
      +                "left": {
      +                  "minimum": 0,
      +                  "type": "number"
      +                },
      +                "right": {
      +                  "minimum": 0,
      +                  "type": "number"
      +                },
      +                "top": {
      +                  "minimum": 0,
      +                  "type": "number"
      +                }
      +              },
      +              "type": "object"
      +            }
      +          ]
      +        },
      +        "padding": {
      +          "anyOf": [
      +            {
      +              "minimum": 0,
      +              "type": "number"
      +            },
      +            {
      +              "additionalProperties": false,
      +              "properties": {
      +                "bottom": {
      +                  "minimum": 0,
      +                  "type": "number"
      +                },
      +                "left": {
      +                  "minimum": 0,
      +                  "type": "number"
      +                },
      +                "right": {
      +                  "minimum": 0,
      +                  "type": "number"
      +                },
      +                "top": {
      +                  "minimum": 0,
      +                  "type": "number"
      +                }
      +              },
      +              "type": "object"
      +            }
      +          ]
      +        },
      +        "parent": {
      +          "type": "string"
      +        },
      +        "width": {
      +          "minimum": 0,
      +          "type": "number"
      +        },
      +        "widthMode": {
      +          "enum": [
      +            "fixed",
      +            "percent",
      +            "auto",
      +            "fill"
      +          ],
      +          "type": "string"
      +        },
      +        "widthWeight": {
      +          "exclusiveMinimum": 0,
      +          "type": "number"
      +        },
      +        "wrap": {
      +          "type": "boolean"
      +        }
      +      },
      +      "required": [
      +        "id"
      +      ],
      +      "type": "object"
      +    },
      +    "type": "array"
      +  }
      +}
  2. First observedv0.5.0

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does well: it discloses that the file is validated against the official runtime, that a rendered preview frame is returned, that PNG paths resolve relative to cwd, that z-order favors later array entries and images over shapes, and that the Canvas2D preview will not play audio even though the AudioAsset is written correctly. It never states what happens when outPath already exists (overwrite vs error) or how invalid specs fail, which keeps it short of a 5 for a file-writing mutation tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

It is long, but the complexity of the scene spec justifies most of the volume, and the core purpose and return value are front-loaded in the first line before any examples. Minor waste remains — rotation units are stated twice, and the trailing bullet sprawl (layouts, mesh, followPath, groups, imports, pro features, audio) is appended rather than organized into a scan-friendly outline.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 3-parameter tool with no output schema and a 100%-covered top level but wide-open nested scene object, the description supplies everything an agent needs: two contrasting worked specs (graphics and character rig), preset catalog, pro-feature syntax, import behavior, z-order rules, and the audio limitation. No material invocation detail is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Top-level schema coverage is 100%, but the scene object is declared with additionalProperties:true and only layouts is actually documented, so the description must supply the real semantics — and it does, in depth, for shapes, animations, tracks, presets, groups, mesh vertices, followPath, imports, and audio events. It falls short of 5 because much of the meaning is conveyed by worked examples rather than enumerated fields, leaving some sub-keys (e.g. keyframe field options, state machine inputs) inferential.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence states a specific verb and resource ('Create a working .riv animation file from scratch') plus the distinguishing value proposition ('no Rive editor needed') and the side effect of runtime validation returning a preview frame. This clearly separates it from riv_edit, riv_import_svg, and other siblings that operate on existing files.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives an explicit recommended pipeline ('riv_design_tokens → riv_import_svg / riv_asset_search → riv_create → riv_critique → fix → re-critique') that tells the agent both when to use this tool and which siblings precede and follow it. It also states a preference rule ('PREFER imported real vector art over drawing with primitives') and directs the agent to the rive-design-guidelines prompt for non-trivial scenes.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.