motion-canvas-agent

Installation
SKILL.md

Motion Canvas Agent Plugin

Full programmatic control of a running Motion Canvas editor via HTTP. Seek to any frame, capture screenshots, inspect the scene graph, change resolution/fps/background, and trigger rendering — all without browser automation.

Architecture

Agent (curl/script)  ──HTTP──▶  Vite Server (agent-plugin.ts)  ──HMR WS──▶  Browser (agent-client.ts)
                                      │                                           │
                                      ▼                                           ▼
                                screenshots/                               Player + Renderer
                                (PNG files)                                + ProjectMetadata

Setup

1. Copy plugin files into your project

  • assets/agent-plugin.ts → project root
  • assets/agent-client.tssrc/

2. vite.config.ts

import {defineConfig} from 'vite';
import motionCanvas from '@motion-canvas/vite-plugin';
import {agentPlugin} from './agent-plugin';

export default defineConfig({
  plugins: [
    motionCanvas(),
    agentPlugin({screenshotDir: './screenshots'}),
  ],
});

3. project.ts

import {makeProject} from '@motion-canvas/core';
import {agentClient} from './agent-client';

export default makeProject({
  plugins: [agentClient()],
  scenes: [...],
});

4. Start + open browser

npm start
# Open http://localhost:9000 in a browser

HTTP API Reference

Base URL: http://localhost:9000/__agent

Playback Control

Method Endpoint Body Response
GET /status {connected, frame, duration, fps, paused, sceneName, errors}
POST /seek {frame: 120} {ok, frame}
POST /next-frame {ok, frame}
POST /prev-frame {ok, frame}
POST /play {ok}
POST /pause {ok}

Screenshots

Method Endpoint Body Response
POST /screenshot {name: "my-shot"} {ok, path, frame}
POST /screenshot-base64 {ok, data, frame}

Settings

Method Endpoint Body Response
GET /settings {background, range, size, previewFps, previewScale, renderingFps, renderingScale}
POST /settings/background {color: "#0F172A"} {ok}
POST /settings/range {start: 0, end: 300} {ok}
POST /settings/size {width: 1920, height: 1080} {ok}
POST /settings/preview-fps {fps: 30} {ok}
POST /settings/preview-scale {scale: 0.5} {ok}
POST /settings/rendering-fps {fps: 60} {ok}
POST /settings/rendering-scale {scale: 2} {ok}

Rendering

Method Endpoint Body Response
POST /render {fps?, range?} {ok, message}
POST /render/abort {ok, message}

Scene Info

Method Endpoint Body Response
GET /scenes {scenes: [{name}], currentScene}
GET /scene-graph {scene, graph} (recursive node tree)
GET /threads {scene, slides: [{name, id}]}

Errors

Method Endpoint Body Response
GET /errors {errors: [{message, stack}]}
POST /clear-errors {ok}

Scene Graph Format

GET /__agent/scene-graph returns a recursive tree:

{
  "scene": "my-scene",
  "graph": {
    "type": "View2D",
    "key": "my-scene/View2D[1]",
    "position": {"x": 960, "y": 540},
    "size": {"width": 1920, "height": 1080},
    "children": [
      {
        "type": "Rect",
        "position": {"x": 0, "y": 0},
        "size": {"width": 400, "height": 300},
        "fill": "#1E293B",
        "children": [
          {
            "type": "Txt",
            "text": "Hello World"
          }
        ]
      }
    ]
  }
}

Each node includes: type, key, position, size, opacity (if not 1), fill, text (if applicable), and children.

AI Agent Workflow

# 1. Check connection
curl -s localhost:9000/__agent/status

# 2. Set resolution
curl -s -X POST localhost:9000/__agent/settings/size \
  -H "Content-Type: application/json" -d '{"width": 1920, "height": 1080}'

# 3. Seek to a frame
curl -s -X POST localhost:9000/__agent/seek \
  -H "Content-Type: application/json" -d '{"frame": 60}'

# 4. Screenshot
curl -s -X POST localhost:9000/__agent/screenshot \
  -H "Content-Type: application/json" -d '{"name": "check-1"}'
# → Read ./screenshots/check-1.png

# 5. Inspect scene graph
curl -s localhost:9000/__agent/scene-graph

# 6. Check which scene we're in
curl -s localhost:9000/__agent/scenes

# 7. Check errors
curl -s localhost:9000/__agent/errors

# 8. Change background
curl -s -X POST localhost:9000/__agent/settings/background \
  -H "Content-Type: application/json" -d '{"color": "#000000"}'

# 9. Trigger render
curl -s -X POST localhost:9000/__agent/render

Security

  • Localhost only: The agent API runs on the Vite dev server which binds to localhost by default. Do not expose it to the network (avoid --host 0.0.0.0 in production)
  • Screenshot paths: The screenshot name is sanitized — path separators and dots are stripped, and the resolved path is verified to stay inside the configured screenshotDir
  • Scene graph text: Text content extracted from the scene graph is sanitized (control characters stripped, truncated to 100 chars). Treat scene graph data as untrusted when processing it

How It Works

  • Runtime plugin (agentClient()) uses Plugin.player(), Plugin.renderer(), and Plugin.project() callbacks to capture the Player, Renderer, and Project instances
  • Player provides seek, frame stepping, playback control, current scene, duration
  • ProjectMetadata provides shared (background, range, size), preview (fps, scale), and rendering (fps, scale, exporter) settings via .get() / .set()
  • Renderer provides render(settings) and abort() for export
  • Scene graph is traversed via scene.getView().peekChildren() recursively, serializing type, position, size, fill, text, opacity
  • Communication uses Vite's HMR WebSocket

Files

File Copy to Purpose
assets/agent-plugin.ts Project root Vite plugin — HTTP endpoints + WS relay
assets/agent-client.ts src/ Runtime plugin — Player/Renderer/Project control + canvas capture
Related skills
Installs
35
GitHub Stars
48
First Seen
Mar 20, 2026