motion-svg
Vector Motion

VectorMotion

Built on top of motion-svg
Export production-ready code.

Open the Editor →
motion-svg

motion-svg

The first declarative SVG animation engine built specifically for path-level control.

Parse, wrap, animate & export SVG paths with keyframes, easing curves, interaction triggers, path morphing, and a plugin system. Zero dependencies. TypeScript-first.

Parser

Actor

Animation

Orchestration

Triggers

hover · click · loop · scroll · appear · manual

Playback

createPlayback · rate · events · time-stretching

Bundle I/O

exportBundle · importBundle · validateBundle

Plugin System

PluginManager · hooks pipeline · zero-overhead

React Bindings

MotionSvgPlayer
MotionSvgActor
useMotionSvg / useActor

Vanilla Web Component

<motion-svg> custom element
Shadow DOM + attributes API
registerMotionSvg()

installation

React.jsVanilla.js
$npm install motion-svg

Installation

bash
npm install motion-svg

Entry Points

Import PathDescription
motion-svgCore engine (parser, actors, timelines, easing, bundles, plugins)
motion-svg/reactReact components and hooks
motion-svg/vanilla<motion-svg> Web Component (framework-free)
motion-svg/pluginsOfficial plugins (spring physics, custom easing)

Works in Node.js and browsers. Zero dependencies. TypeScript types included.

Quick Start

Minimal flow from SVG string to animated, exportable bundle:

Core
ts
import { parseSvg, createActor, timeline, trigger, exportBundle } from 'motion-svg';

// 1. Parse an SVG
const scene = parseSvg('<svg viewBox="0 0 200 200"><path id="star" d="M100,10 L..." fill="#f00"/></svg>');

// 2. Create an Actor from a path
const star = createActor({
  id: 'star',
  paths: [scene.paths[0]],
  origin: { x: 100, y: 100 },
});

// 3. Define keyframes
const anim = timeline(star, {
  keyframes: [
    { at: 0, scale: 1, rotation: 0 },
    { at: 600, scale: 1.3, rotation: 180, curve: 'easeInOutBack' },
    { at: 1200, scale: 1, rotation: 360, curve: 'easeOutCubic' },
  ],
});

// 4. Set a trigger
const triggerBinding = trigger(anim, { type: 'hover', reverse: true });

// 5. Export as a self-contained JSON bundle
const json = exportBundle({
  scene,
  actors: [star],
  timelines: [anim],
  triggers: [triggerBinding],
});
import { MotionSvgPlayer } from 'motion-svg/react';
import bundle from './animation.motionsvg.json';

function App() {
  return <MotionSvgPlayer data={bundle} width={400} height={300} />;
}

Architecture

┌──────────────────────────────────────────────────────────────────┐
motion-svg
├────────────┬──────────────┬──────────────────┬───────────────────┤
Parser Actor Animation Orchestration
│ parseSvg │ createActor │ timeline │ sequence
│ shapes │ interpolation │ stagger
│ easing curves │ parallelDuration
│ path morphing
├────────────┴──────────────┴──────────────────┴───────────────────┤
│ Triggers: hover · click · loop · scroll · appear · manual
├──────────────────────────────────────────────────────────────────┤
│ Playback: createPlayback · rate · events · time-stretching
├──────────────────────────────────────────────────────────────────┤
│ Bundle I/O: exportBundle · importBundle · validateBundle
├──────────────────────────────────────────────────────────────────┤
│ Plugin System: PluginManager · hooks pipeline · zero-overhead
├────────────────────────────┬─────────────────────────────────────┤
React Bindings Vanilla Web Component
│ MotionSvgPlayer <motion-svg> custom element
│ MotionSvgActor │ Shadow DOM + attributes API
│ useMotionSvg / useActor │ registerMotionSvg()
└────────────────────────────┴─────────────────────────────────────┘

Parser

parseSvg(svgString): Scene

Parse an SVG string into a structured Scene object. Regex-based — works in Node.js and browsers, no DOM required.

ts
import { parseSvg } from 'motion-svg';

const scene = parseSvg(svgString);
// scene.viewBox   → { x, y, w, h }
// scene.paths     → SvgPath[] (all <path>, <rect>, <circle>, etc.)
// scene.groups    → SvgGroup[] (all <g> with children)
// scene.colors    → Record<id, color>
// scene.gradients → GradientDef[] (linear + radial)
// scene.metadata  → { xmlns, width, height, originalSvg }
FieldTypeDescription
pathsSvgPath[]All <path> elements with id, d, fill, stroke, transform
groupsSvgGroup[]All <g> elements with child hierarchy
viewBoxViewBoxCanvas dimensions { x, y, w, h }
colorsColorMapMap of element id → color value
gradientsGradientDef[]Linear and radial gradient definitions
metadataSvgMetadataOriginal xmlns, width, height, raw SVG

Actor

createActor(config): Actor

Create an Actor — a controllable wrapper around one or more SVG paths with independent transform properties.

ts
import { createActor } from 'motion-svg';

const actor = createActor({
  id: 'my-actor',
  paths: [scene.paths[0], scene.paths[1]],
  origin: { x: 100, y: 100 },
  z: 1,  // optional z-order
});
FieldTypeDescription
idstringUnique identifier
pathsSvgPath[]SVG paths to include
originPointTransform origin { x, y }
znumber?Z-order for stacking (default: 0)

generateShapePath(type, width, height, opts?): string

Generate an SVG path d string for a geometric shape. Bounding box is always [0, 0, width, height].

ts
import { generateShapePath } from 'motion-svg';

const rect = generateShapePath('rect', 100, 50);
const hex  = generateShapePath('polygon', 80, 80, { sides: 6 });

Supported shapes: 'rect' | 'ellipse' | 'line' | 'arrow' | 'polygon'

Timeline

timeline(actor, config): Timeline

Create a keyframe timeline for an actor. Keyframes are sorted by at time and the first keyframe is auto-filled with actor defaults.

ts
import { timeline } from 'motion-svg';

const tl = timeline(actor, {
  keyframes: [
    { at: 0, opacity: 0, position: { x: 0, y: 20 } },
    { at: 400, opacity: 1, position: { x: 0, y: 0 }, curve: 'easeOutCubic' },
  ],
});
// tl.id, tl.actorId, tl.keyframes, tl.duration

Keyframe Properties

PropertyTypeDefaultDescription
atnumberrequiredTime in milliseconds
positionPointactor origin{ x, y } position
scalenumber | Point1Uniform or { x, y } scale
rotationnumber0Rotation in degrees
opacitynumber1Opacity 0..1
fillstringoriginalFill color or url(#gradientId)
strokestringoriginalStroke color or gradient ref
strokeWidthnumberoriginalStroke width
strokeAlignStrokeAlign'center''center' | 'inside' | 'outside'
blurRadiusnumber0Gaussian blur in px
backdropBlurnumber0Backdrop blur in px
widthnumberShape width (shape actors)
heightnumberShape height (shape actors)
pathDstringSVG path d for morphing
curveEasingCurve'linear'Easing to reach this keyframe

Easing Curves

20+ built-in easing functions plus custom cubic bezier. Every keyframe can specify a curve that controls the transition from the previous keyframe.

CategoryFunctions
Linearlinear
SineeaseIn, easeOut, easeInOut
QuadeaseInQuad, easeOutQuad, easeInOutQuad
CubiceaseInCubic, easeOutCubic, easeInOutCubic
QuarteaseInQuart, easeOutQuart, easeInOutQuart
BackeaseInBack, easeOutBack, easeInOutBack
ElasticeaseInElastic, easeOutElastic, easeInOutElastic
BounceeaseInBounce, easeOutBounce, easeInOutBounce
ts
import { cubicBezier, getEasingFunction } from 'motion-svg';

// Custom cubic bezier (like CSS cubic-bezier)
const myEasing = cubicBezier(0.25, 0.1, 0.25, 1.0);

// Resolve by name
const fn = getEasingFunction('easeOutBack'); // (t: number) => number

Custom cubic bezier in keyframes:

ts
{ at: 500, curve: { type: 'cubicBezier', x1: 0.4, y1: 0, x2: 0.2, y2: 1 } }

Orchestration

sequence(actor, config): Timeline

Chain multiple timelines sequentially into a single Timeline.

ts
import { sequence } from 'motion-svg';

const combined = sequence(actor, {
  items: [
    { timeline: fadeIn },
    { timeline: slideUp, delay: 100 },
    { timeline: bounce, offset: 800 },
  ],
});

stagger(config): Timeline[]

Create staggered timelines for multiple actors with the same animation.

ts
import { stagger } from 'motion-svg';

const timelines = stagger({
  actors: [card1, card2, card3, card4],
  keyframes: [
    { at: 0, opacity: 0, position: { x: 0, y: 20 } },
    { at: 400, opacity: 1, position: { x: 0, y: 0 }, curve: 'easeOutCubic' },
  ],
  stagger: 100,       // 100ms between each actor
  from: 'start',      // 'start' | 'end' | 'center' | 'edges'
});

parallelDuration(timelines): number

Return the total duration of timelines running in parallel (max duration).

Path Morphing

Interpolate SVG path d attributes between shapes. Automatically normalizes all commands to cubic beziers and balances segment counts.

lerpPath(pathA, pathB, t): string

ts
import { lerpPath } from 'motion-svg';

const mid = lerpPath(
  'M0,0 L10,0 L10,10 Z',        // triangle
  'M0,0 L10,0 L10,10 L0,10 Z',  // square
  0.5
);

Use pathD in keyframes for animated morphing:

ts
const morph = timeline(actor, {
  keyframes: [
    { at: 0, pathD: circlePath },
    { at: 1000, pathD: starPath, curve: 'easeInOutCubic' },
  ],
});

Helpers: parsePathD(d), normalizeToCubic(commands), balanceCommands(a, b). Supports all SVG path commands: M, L, H, V, C, S, Q, T, A, Z.

Triggers

trigger(timeline, config): TriggerBinding

Bind a trigger to a timeline. The actual event wiring is handled by the renderer.

ts
import { trigger } from 'motion-svg';

trigger(tl, { type: 'hover', reverse: true });
trigger(tl, { type: 'click', toggle: true });
trigger(tl, { type: 'loop', iterations: Infinity, direction: 'alternate', delay: 200 });
trigger(tl, { type: 'scroll', start: 0.2, end: 0.8 });
trigger(tl, { type: 'appear', threshold: 0.5, once: true });
trigger(tl, { type: 'manual' });
TriggerKey Options
hoverreverse?: boolean
clicktoggle?: boolean
loopiterations?, direction?: 'normal'|'reverse'|'alternate', delay?
scrollstart?: number, end?: number (0..1 viewport %)
appearthreshold?: number, once?: boolean
manual— (controlled via PlaybackController)

Playback

createPlayback(options): PlaybackController

Runtime playback controller that drives animations frame-by-frame using requestAnimationFrame.

ts
import { createPlayback } from 'motion-svg';

const ctrl = createPlayback({
  timeline: tl,
  trigger: triggerBinding,
  gradients: scene.gradients,
  onUpdate: (state, timeMs) => { /* apply state to DOM */ },
  onComplete: () => { /* all done */ },
});

ctrl.play();
ctrl.pause();
ctrl.stop();
ctrl.seek(500);
ctrl.reverse();
PropertyTypeDescription
statePlaybackState'idle' | 'playing' | 'paused' | 'finished'
currentTimenumberCurrent time in ms
durationnumberTimeline duration in ms
progressnumberNormalized progress 0..1
playbackRatenumber1=normal, 0.5=half, 2=double, negative=reverse

Events

ts
const unsub = ctrl.on('complete', (event) => {
  console.log(event.progress, event.currentTime);
});
// Event types: 'start' | 'play' | 'pause' | 'stop' | 'seek'
//            | 'reverse' | 'frame' | 'complete' | 'repeat'

Interpolation

interpolateKeyframes(keyframes, timeMs, opts?): ActorState

Compute interpolated state at a given time. Each property is interpolated independently — a fill-only keyframe won't create a "hold" segment for position or scale.

getActorStateAtTime(timeline, timeMs, opts?): ActorState

ts
import { getActorStateAtTime } from 'motion-svg';

const state = getActorStateAtTime(tl, 250, { gradients: scene.gradients });
// state.position → { x: 50, y: 0 }
// state.fill     → '#807f00' (smooth color lerp)

lerpGradientDef(a, b, t): GradientDef

Interpolate between two gradient definitions (linear/radial, cross-type).

ActorState Interface

ts
interface ActorState {
  position: Point;
  scale: number | Point;
  rotation: number;
  opacity: number;
  fill?: string;
  stroke?: string;
  strokeWidth?: number;
  strokeAlign?: StrokeAlign;
  blurRadius?: number;
  backdropBlur?: number;
  width?: number;
  height?: number;
  fillGradient?: GradientDef;
  strokeGradient?: GradientDef;
  pathD?: string;
}

Colors & Gradient Animation

Smoothly interpolates colors and gradients between keyframes. Supports hex-to-hex, gradient-to-gradient, and color-to-gradient transitions.

ts
// Hex → Hex
{ at: 0, fill: '#ff0000' }
{ at: 1000, fill: '#0000ff', curve: 'easeInOut' }
// At t=500 → '#800080'

// Gradient → Gradient
{ at: 0, fill: 'url(#sunrise)' }
{ at: 1000, fill: 'url(#sunset)', curve: 'easeInOut' }

// Color ↔ Gradient
{ at: 0, fill: '#ff6b6b' }
{ at: 1000, fill: 'url(#sunset)', curve: 'easeOut' }

Stroke Properties

Keyframes can animate stroke alongside all other properties. Three alignment modes are supported:

AlignmentDescription
centerDefault — centered on path edge
insideInside path, using clip-path
outsideOutside path, using paint-order
ts
{ at: 0, stroke: '#ff0000', strokeWidth: 2, strokeAlign: 'center' }
{ at: 500, stroke: '#00ff00', strokeWidth: 6, strokeAlign: 'outside', curve: 'easeOut' }

Bundle I/O

exportBundle(config): string

Export a complete animation as a self-contained .motionsvg.json string.

ts
import { exportBundle } from 'motion-svg';

const json = exportBundle({
  scene,
  actors: [actor1, actor2],
  timelines: [tl1, tl2],
  triggers: [trigger1],
  variants: [{ name: 'idle', actorIds: ['a1'], timelineIndices: [0], triggerIndices: [0] }],
});

importBundle(jsonString): ImportedBundle

Import a bundle and reconstruct all objects.

ts
import { importBundle, getVariant } from 'motion-svg';

const imported = importBundle(jsonString);
// imported.scene, imported.actors, imported.timelines, imported.triggers, imported.variants

const variant = getVariant(imported, 'idle');
// variant.actors, variant.timelines, variant.triggers — or null

validateBundle(jsonString): ValidationResult

Validate a bundle JSON string without importing it.

Bundle Format

json
{
  "version": "1.1",
  "scene": { "viewBox": {...}, "paths": [...], "gradients": [...] },
  "actors": [{ "id": "logo", "pathIds": [...], "origin": {...} }],
  "timelines": [{ "actorId": "logo", "keyframes": [...] }],
  "triggers": [{ "timelineIdx": 0, "type": "hover", "reverse": true }],
  "variants": [{ "name": "idle", "actorIds": [...], "timelineIndices": [...] }]
}

Variants

A variant is a named configuration of the animation scene. Each variant specifies which actors are visible, which timelines are active, and which triggers fire. Use variants for multiple states in a single bundle — e.g. idle, loading, success.

FieldTypeDescription
namestringUnique display name (used as variant prop)
actorIdsstring[]?Actor IDs visible. Omit = all actors.
timelineIndicesnumber[]Indices into timelines[] array
triggerIndicesnumber[]Indices into triggers[] array
ts
import { importBundle, getVariant } from 'motion-svg';

const imported = importBundle(jsonString);
const names = imported.variants.map(v => v.name);

const idle = getVariant(imported, 'idle');
if (idle) {
  idle.actors;    // Actor[]
  idle.timelines; // Timeline[]
  idle.triggers;  // TriggerBinding[]
}

Bundles without variants (v1.0) work unchanged. The variants field is optional and defaults to an empty array.

AnimationStore

High-performance external store for batching actor state updates. Uses queueMicrotask to notify subscribers once per frame instead of per-actor.

ts
import { AnimationStore } from 'motion-svg';

const store = new AnimationStore();
store.set('actor-1', state);
store.setMany({ 'actor-1': state1, 'actor-2': state2 });
const snapshot = store.getSnapshot();
const actorState = store.getActorState('actor-1');
const unsub = store.subscribe(() => { /* re-render */ });
store.reset();

Plugin System

Zero-overhead hook-based plugin system. Plugins compose in a pipeline where each receives the output of the previous one.

ts
import { plugins } from 'motion-svg';

plugins.register(myPlugin);
plugins.unregister('my-plugin');
plugins.listPlugins();  // [{ name, version }]
plugins.clear();

Available Hooks

HookSignatureCalled When
afterParse(scene) => SceneAfter SVG parsing
afterCreateActor(actor, scene) => ActorAfter actor creation
beforeInterpolate(keyframes, timeMs) => Keyframe[]Before each frame interpolation
afterInterpolate(state, actorId, timeMs) => ActorStateAfter each frame interpolation
beforeExport(bundle) => BundleBefore bundle export
afterImport(scene, actors, timelines, triggers) => {...}After bundle import
interpolateProperty(propName, from, to, t) => unknownCustom property interpolation

Creating a plugin

ts
import type { MotionSvgPlugin } from 'motion-svg';

const myPlugin: MotionSvgPlugin = {
  name: 'my-plugin',
  version: '1.0.0',
  hooks: {
    afterInterpolate(state, actorId, timeMs) {
      return { ...state, opacity: state.opacity * 0.8 };
    },
  },
  destroy() { /* cleanup */ },
};

Spring Plugin

Damped harmonic oscillator that adds natural overshoot and settling to scale, position, and rotation.

ts
import { springPlugin } from 'motion-svg/plugins';
import { plugins } from 'motion-svg';

plugins.register(springPlugin({
  stiffness: 120,    // higher = snappier (default: 100)
  damping: 8,        // higher = less oscillation (default: 10)
  mass: 1,           // higher = more inertia (default: 1)
  properties: ['scale', 'position'],
}));

springResponse(t, stiffness, damping, mass): number — low-level spring response function for direct use.

Custom Easing Plugin

Register named easing functions usable in keyframes.

ts
import { customEasingPlugin, steppedEasing, elasticEasing } from 'motion-svg/plugins';
import { plugins } from 'motion-svg';

plugins.register(customEasingPlugin({
  easings: {
    stepped5: steppedEasing(5),
    superElastic: elasticEasing(1.5, 0.3),
    myCustom: (t) => t * t * (3 - 2 * t), // smoothstep
  },
}));

// Then use in keyframes:
// { at: 500, scale: 2, curve: 'stepped5' }
FactoryDescription
steppedEasing(steps)Discrete step animation
slowMotionEasing(factor?)Slow in the middle
elasticEasing(amplitude?, period?)Configurable elastic
springEasing(overshoot?)Spring-like back easing

React Bindings

Import from motion-svg/react:

ts
import {
  MotionSvgPlayer,   // Full player component
  MotionSvgActor,    // Single actor renderer
  MotionSvgCanvas,   // SVG canvas wrapper
  useMotionSvg,      // Control hook
  useActorState,     // Granular actor state hook
} from 'motion-svg/react';

useMotionSvg(bundle, options?): MotionSvgInstance

React hook for controlling animations. Uses useSyncExternalStore for optimal batched rendering.

ts
const {
  data,           // ImportedBundle | null
  actors,         // Actor[]
  timelines,      // Timeline[]
  triggers,       // TriggerBinding[]
  actorStates,    // Record<string, ActorState>
  controllers,    // PlaybackController[]
  play, pause, stop, seek,
  playing,        // boolean
  variantNames,   // string[]
} = useMotionSvg(bundleJson, { variant: 'idle' });

useActorState(store, actorId): ActorState | undefined

Granular hook that re-renders only when a specific actor's state changes.

<MotionSvgPlayer />

tsx
<MotionSvgPlayer data={bundle} width={400} height={300} variant="idle" />

<MotionSvgActor />

Renders a single actor with its current interpolated state. Supports stroke alignment, inline gradients, blur filters, and path morphing.

Vanilla Web Component

registerMotionSvg(tagName?)

Register the <motion-svg> custom element. Call once before using in HTML.

ts
import { registerMotionSvg } from 'motion-svg/vanilla';
registerMotionSvg(); // or registerMotionSvg('my-player')

<motion-svg> Attributes

AttributeDescription
srcURL to a .motionsvg.json file (fetched automatically)
dataInline JSON string of the bundle
variantName of the variant to activate
autoplayStart animation on load (boolean attribute)
widthCSS width (default: '100%')
heightCSS height (default: '100%')

JS API

js
const el = document.querySelector('motion-svg');
el.bundle = bundleObject;     // set bundle programmatically
el.variant = 'dark';          // switch variant
el.play();
el.pause();
el.stop();
el.seek(500);
el.controllers;               // PlaybackController[]

Events: motionsvg:ready, motionsvg:play, motionsvg:complete

Using .motionsvg.json in Code

After exporting a bundle, you can import and play animations in any JavaScript/TypeScript project.

1. Load the bundle

ts
import { importBundle } from 'motion-svg';

const json = await fetch('/my-animation.motionsvg.json').then(r => r.text());
const bundle = importBundle(json);

2. Render & animate

import { useState, useEffect, useRef, useMemo } from 'react';
import { importBundle, getActorStateAtTime } from 'motion-svg';

function AnimatedSvg({ bundleJson }) {
  const bundle = useMemo(() => importBundle(bundleJson), [bundleJson]);
  const [time, setTime] = useState(0);
  const rafRef = useRef();

  useEffect(() => {
    const start = performance.now();
    const tick = () => {
      setTime((performance.now() - start) % bundle.timelines[0].duration);
      rafRef.current = requestAnimationFrame(tick);
    };
    rafRef.current = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(rafRef.current);
  }, [bundle]);

  const { scene, actors, timelines } = bundle;

  return (
    <svg viewBox={`${scene.viewBox.x} ${scene.viewBox.y} ${scene.viewBox.w} ${scene.viewBox.h}`}>
      {actors.map(actor => {
        const tl = timelines.find(t => t.actorId === actor.id);
        const state = tl ? getActorStateAtTime(tl, time, { gradients: scene.gradients }) : null;
        const ox = actor.origin.x, oy = actor.origin.y;
        const transform = state
          ? `translate(${state.position.x},${state.position.y}) translate(${ox},${oy}) rotate(${state.rotation}) scale(${typeof state.scale === 'number' ? state.scale : state.scale.x}) translate(${-ox},${-oy})`
          : '';
        return (
          <g key={actor.id} transform={transform} opacity={state?.opacity ?? 1}>
            {actor.paths.map(p => <path key={p.id} d={p.d} fill={state?.fill ?? p.fill} />)}
          </g>
        );
      })}
    </svg>
  );
}

Manual animation loop (alternative)

ts
import { getActorStateAtTime } from 'motion-svg';

const tl = bundle.timelines[0];
let startTime = performance.now();

function animate() {
  const elapsed = performance.now() - startTime;
  const timeMs = elapsed % tl.duration;
  const state = getActorStateAtTime(tl, timeMs, {
    gradients: bundle.scene.gradients,
  });
  // Apply state to DOM / canvas / WebGL / React state...
  requestAnimationFrame(animate);
}
animate();

Type Exports

All types are exported from the main entry point:

ts
import type {
  // Core
  Point, ViewBox, SvgPath, SvgGroup, Scene, SvgMetadata,
  // Color & Gradients
  ColorMap, ColorEntry, GradientStop, GradientDef,
  LinearGradientDef, RadialGradientDef,
  // Actor
  Actor, ActorConfig, ShapeType,
  // Animation
  Keyframe, TimelineConfig, Timeline, EasingName,
  EasingCurve, CubicBezierCurve,
  // Trigger
  TriggerType, TriggerConfig, TriggerBinding, LoopDirection,
  HoverTrigger, ClickTrigger, LoopTrigger, ScrollTrigger,
  AppearTrigger, ManualTrigger,
  // Bundle
  Bundle, BundleScene, BundleActor, BundleTimeline,
  BundleTrigger, BundleVariant, ExportConfig,
  // Playback
  PlaybackState, PlaybackController, PlaybackEventType,
  PlaybackEvent, PlaybackEventHandler,
  // Interpolation
  ActorState, InterpolateOptions,
  // Bundle I/O
  ImportedBundle, ValidationResult,
  // React
  UseMotionSvgOptions, MotionSvgInstance,
  // Path Morphing
  PathCommand, CubicSegment, NormalizedPath,
  // Orchestration
  SequenceConfig, SequenceItem, StaggerConfig, StaggerFrom,
  // Plugins
  MotionSvgPlugin, MotionSvgHooks, SpringConfig,
  SpringProperty, CustomEasingConfig,
} from 'motion-svg';

motion-svg-kit

motion-svg-kit is the visual editor and developer toolkit built on top of the motion-svg core. It provides a full GUI for designing, previewing and exporting SVG animations — no code required.

Developer Access

Contribute to the core, get access to the kit

motion-svg-kit will be available to developers who contribute to the motion-svg core library. Contributors get early access to the visual editor, shape tools, timeline UI, and export pipeline.

What's included

FeatureDescription
Visual TimelineDrag-and-drop keyframe editor with curve preview
Canvas EditorDirect manipulation of actors, origins, and paths on an SVG canvas
Shape ToolsBuilt-in shape generator (rect, ellipse, polygon, arrow, line)
Live PreviewReal-time animation preview with trigger simulation
Export PipelineOne-click export to .motionsvg.json bundles
Plugin PanelVisual configuration for spring physics, custom easing, and more
Variant ManagerCreate and switch between animation variants visually

How to get access

Contribute to the motion-svg core — bug fixes, features, documentation, or tests. Once your PR is merged, you'll receive access to the motion-svg-kit editor.

bash
# 1. Fork & clone
git clone https://github.com/anthropics/motion-svg.git
cd motion-svg && pnpm install

# 2. Run tests
pnpm test

# 3. Make your contribution & open a PR
Vector Motionmotion-svg — Declarative SVG Animation Engine · MIT
v1.1
⌘I