npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@nitesh-tyagi/vectorflow

v1.0.3

Published

A minimal, JSON-driven SVG pose animation library

Readme

VectorFlow

A minimal, JSON-driven SVG pose animation library.

npm version license Try the Demo


What is VectorFlow?

VectorFlow loads an SVG containing multiple pose states and a JSON config defining routes and actions, then animates smoothly between poses using attribute interpolation. No path morphing, no crossfades — just clean, transform-and-attribute-based transitions.

Features:

  • Declarative JSON config — define states, routes, and actions
  • Smooth SVG attribute interpolation (position, size, color, transform)
  • Sequence and loop action types with cancellation support
  • Clean public API with event system
  • Zero-config playback — just load SVG + JSON and go
  • ~29 KB UMD bundle (including anime.js)

Table of Contents


Quick Start

Install

npm install @nitesh-tyagi/vectorflow

Browser (UMD)

<script src="https://unpkg.com/@nitesh-tyagi/vectorflow/dist/vectorflow.umd.js"></script>
<script>
  const vf = new VectorFlow({
    svgElement: document.querySelector('svg'),
    json: config,
  });
  vf.play('shuffle');
</script>

ES Module

import VectorFlow from '@nitesh-tyagi/vectorflow';

const vf = new VectorFlow({
  svgElement: document.querySelector('#my-svg'),
  json: configObject, // or a JSON string
});

vf.play('left');       // play an action
vf.play('shuffle');    // auto-cancels previous, starts new
vf.stop();             // cancel current playback

Live Demo

Try VectorFlow instantly — no install required:

vectorflow.webmuffin.io

  1. Download the sample SVG and sample JSON
  2. Open the Demo Player
  3. Upload both files and click Load & Play
  4. Use the action buttons to trigger animations

The demo player source is at Nitesh-WebMuffin/SVG-Animator.


Preparing Your SVG

VectorFlow discovers animation states and parts directly from your SVG markup. Follow these rules when creating your SVG:

1. Define State Groups

Wrap each pose/state in a <g> (group) element with an id of the form state_{name}:

<g id="state_left">   <!-- state name: "left" -->
  ...
</g>
<g id="state_center"> <!-- state name: "center" -->
  ...
</g>
<g id="state_right">  <!-- state name: "right" -->
  ...
</g>
  • The state_ prefix is required. Everything after it becomes the state name used in your JSON config.
  • You can have as many states as you need.

2. Tag Parts with data-part

Inside each state group, mark the elements that should be animated with a data-part attribute:

<g id="state_left">
  <rect data-part="body" x="20" y="80" width="40" height="40" fill="red" />
  <circle data-part="head" cx="40" cy="60" r="15" fill="orange" />
</g>
  • VectorFlow matches parts by name across states. A part called "body" in state_left will animate to the "body" in state_right.
  • Part names must be identical across all states.

3. Keep Part Sets Consistent

Every state group must contain the exact same set of data-part names. If state_left has parts body and head, then state_center and state_right must also have body and head.

VectorFlow will throw an error if part sets don't match:

VectorFlow: Part mismatch between states. State "left" has parts [body,head]
but state "right" has parts [body].

4. Animatable Attributes

VectorFlow interpolates the following SVG attributes and properties between states:

| Type | Attributes / Properties | |------|------------------------| | Numeric | x, y, width, height, rx, ry, cx, cy, r, opacity | | Colors | fill, stroke (hex #RGB/#RRGGBB or rgb()) | | Transforms | translate(x,y), scale(x,y), rotate(angle) |

Design your states so that the visual differences are expressed through these attributes. For example, to move a shape left, change its x; to recolor it, change its fill.

5. Minimal SVG Template

<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
  <!-- State: left -->
  <g id="state_left">
    <rect data-part="shape" x="20" y="80" width="40" height="40" fill="#e74c3c" />
  </g>

  <!-- State: center -->
  <g id="state_center">
    <rect data-part="shape" x="80" y="80" width="40" height="40" fill="#3498db" />
  </g>

  <!-- State: right -->
  <g id="state_right">
    <rect data-part="shape" x="140" y="80" width="40" height="40" fill="#2ecc71" />
  </g>
</svg>

Tip: At runtime, VectorFlow hides all state groups and creates a single <g id="live_character"> clone to animate. Your original state groups serve as snapshots defining each pose.


Writing the JSON Config

The JSON config tells VectorFlow which state to start in, how to transition between states, and what actions are available.

Top-Level Structure

{
  "name": "my-animation",
  "initial_state": "center",
  "ms": 120,
  "routes": { ... },
  "actions": { ... }
}

| Field | Type | Required | Description | |-------|------|----------|-------------| | name | string | No | A label for this config (for your own reference). | | initial_state | string | Yes | The state name to start in (must match an id="state_..." in your SVG). | | ms | number | No | Default animation duration in milliseconds (default: 120). | | routes | object | Yes | Defines how to travel between states. | | actions | object | Yes | Named animation sequences/loops that users can trigger. |

Routes

Routes define the path the animation takes when transitioning to a target state. Each route key is a target and its value maps source states (or "*" for any source) to an array of intermediate + destination states.

"routes": {
  "left": {
    "*": ["left"],
    "right": ["center", "left"]
  },
  "right": {
    "*": ["right"],
    "left": ["center", "right"]
  },
  "center": {
    "*": ["center"],
    "ms": 60
  }
}

How route resolution works:

  1. When the engine needs to go to "left" from the current state, it looks up routes["left"].
  2. If there's a specific entry for the current state (e.g. "right": ["center", "left"]), it follows that path — first animate to center, then to left.
  3. If no specific entry exists, it uses the wildcard "*" path.
  4. If no route exists at all, VectorFlow jumps directly to the target state.

Route-level ms: You can set a per-route ms to override the global duration for transitions to that target state.

Route keys vs state names: Route keys don't have to match a state name. You can create alias routes that resolve to real states:

"routes": {
  "direct-left": { "*": ["left"] },
  "direct-right": { "*": ["right"] }
}

Here "direct-left" is a route key (not a state) that resolves to state "left". This is useful for defining multiple paths to the same state.

Actions

Actions are the named animations you trigger via vf.play('actionName'). Each action has a type — either "sequence" (play once) or "loop" (repeat).

"actions": {
  "left":    { "type": "sequence", "order": ["left"] },
  "right":   { "type": "sequence", "order": ["right"] },
  "center":  { "type": "sequence", "order": ["center"] },
  "shuffle": {
    "type": "loop",
    "mode": "direct",
    "order": ["left", "center", "right", "center"],
    "ms": 80,
    "count": 99999
  }
}

| Field | Type | Default | Description | |-------|------|---------|-------------| | type | "sequence" \| "loop" | "sequence" | sequence plays once; loop repeats count times. | | order | string[] | — | Array of route keys or state names to visit in order. | | ms | number | — | Override animation duration for this action (takes highest priority). | | mode | "direct" | — | If "direct", skips intermediate route steps — jumps straight to the final resolved state. | | count | number | 99999 | For loops: how many times to repeat the order cycle. |

order items can be route keys or state names. The engine resolves each entry through the routes table. If an entry matches a route key, VectorFlow follows that route's path. If it matches a state name directly, it goes there.

Duration Precedence

When determining how long a step takes, VectorFlow checks in this order:

action.ms  >  routes[target].ms  >  json.ms  >  120ms (fallback)

Full JSON Example

{
  "name": "character",
  "initial_state": "idle",
  "ms": 150,
  "routes": {
    "idle":       { "*": ["idle"] },
    "walk-left":  { "*": ["lean-left", "walk-left"], "walk-right": ["idle", "lean-left", "walk-left"] },
    "walk-right": { "*": ["lean-right", "walk-right"], "walk-left": ["idle", "lean-right", "walk-right"] },
    "lean-left":  { "*": ["lean-left"], "ms": 80 },
    "lean-right": { "*": ["lean-right"], "ms": 80 }
  },
  "actions": {
    "go-left":    { "type": "sequence", "order": ["walk-left"] },
    "go-right":   { "type": "sequence", "order": ["walk-right"] },
    "reset":      { "type": "sequence", "order": ["idle"], "ms": 200 },
    "patrol":     { "type": "loop", "order": ["walk-left", "idle", "walk-right", "idle"], "count": 10 }
  }
}

Sample Files

Ready-to-use sample files are available in the demo-files folder:

| File | Description | |------|-------------| | Shapes.svg | SVG with 3 pose states: left, center, right | | Shapes.json | JSON config with routes, actions, and a shuffle loop |

Download both and pass them to VectorFlow, or upload them to the Live Demo Player.


API Reference

Constructor

const vf = new VectorFlow({ svgElement, json });

| Param | Type | Description | |-------|------|-------------| | svgElement | SVGElement \| string | An SVG DOM element or raw SVG string. | | json | object \| string | VectorFlow JSON config (object or JSON string). |

Properties

| Property | Type | Description | |----------|------|-------------| | vf.states | string[] | All discovered state names from the SVG. | | vf.currentState | string | The current active state. | | vf.actions | object | Actions config from the JSON. | | vf.isPlaying | boolean | Whether an action is currently playing. | | vf.currentAction | string \| null | Name of the playing action, or null. | | vf.svgElement | SVGElement | The SVG element being animated. |

Methods

| Method | Returns | Description | |--------|---------|-------------| | vf.play(actionName) | Promise<void> | Play a named action. Auto-cancels any running action. | | vf.stop() | void | Cancel current playback immediately. | | vf.on(event, callback) | VectorFlow | Register an event listener (chainable). | | vf.off(event, callback) | VectorFlow | Remove an event listener (chainable). | | vf.destroy() | void | Stop playback, restore original SVG state groups, remove listeners. |

Events

| Event | Callback Arg | Description | |-------|-------------|-------------| | stateChange | string | Fired after each intermediate step completes (receives the new state name). | | actionStart | string | Fired when an action begins playing. | | actionEnd | string | Fired when an action finishes naturally or is cancelled. | | error | Error | Fired on runtime errors during playback. |

vf.on('stateChange', (state) => {
  console.log('Now in state:', state);
});

vf.on('actionEnd', (actionName) => {
  console.log(`Action "${actionName}" finished`);
});

Full Example

SVG (character.svg)

<svg viewBox="0 0 300 200" xmlns="http://www.w3.org/2000/svg">
  <g id="state_left">
    <rect data-part="body" x="30" y="60" width="50" height="80" rx="8" fill="#e74c3c" />
    <circle data-part="head" cx="55" cy="45" r="18" fill="#f5b041" />
  </g>
  <g id="state_center">
    <rect data-part="body" x="125" y="60" width="50" height="80" rx="8" fill="#3498db" />
    <circle data-part="head" cx="150" cy="45" r="18" fill="#f5b041" />
  </g>
  <g id="state_right">
    <rect data-part="body" x="220" y="60" width="50" height="80" rx="8" fill="#2ecc71" />
    <circle data-part="head" cx="245" cy="45" r="18" fill="#f5b041" />
  </g>
</svg>

JSON (character.json)

{
  "name": "character",
  "initial_state": "center",
  "ms": 150,
  "routes": {
    "left":   { "*": ["left"], "right": ["center", "left"] },
    "right":  { "*": ["right"], "left": ["center", "right"] },
    "center": { "*": ["center"], "ms": 80 }
  },
  "actions": {
    "go-left":  { "type": "sequence", "order": ["left"] },
    "go-right": { "type": "sequence", "order": ["right"] },
    "reset":    { "type": "sequence", "order": ["center"] },
    "patrol":   {
      "type": "loop",
      "order": ["left", "center", "right", "center"],
      "ms": 100,
      "count": 5
    }
  }
}

JavaScript

import VectorFlow from '@nitesh-tyagi/vectorflow';

// Load SVG (already in the DOM) and JSON config
const vf = new VectorFlow({
  svgElement: document.querySelector('#character-svg'),
  json: characterConfig,
});

// Wire up buttons
document.querySelector('#btn-left').onclick = () => vf.play('go-left');
document.querySelector('#btn-right').onclick = () => vf.play('go-right');
document.querySelector('#btn-reset').onclick = () => vf.play('reset');
document.querySelector('#btn-patrol').onclick = () => vf.play('patrol');
document.querySelector('#btn-stop').onclick = () => vf.stop();

// Listen for state changes
vf.on('stateChange', (state) => {
  document.querySelector('#current-state').textContent = state;
});

// Clean up when done
window.addEventListener('beforeunload', () => vf.destroy());

Links

| Resource | URL | |----------|-----| | npm | @nitesh-tyagi/vectorflow | | Live Demo | vectorflow.webmuffin.io | | Demo Source | Nitesh-WebMuffin/SVG-Animator | | Sample Files | demo-files/ | | Docs | docs/ |


License

MIT