Figma Motion → real code

Animate in Figma.
Ship it everywhere.

The runtime for Figma Motion. Export a timeline to a tinyMotionDoc, play it with a pure-JS engine — the same motion, frame for frame, on every platform. No video bake. No rebuild in CSS.

renders identically on
DOMCanvasReactVueSvelteAngularLitRN
card.motion60 fpsLIVE
0.00s
springcubic-bézierloop
The pipeline

From Figma timeline
to a tree of pure numbers

No black box in the middle. Your animation becomes an open document, then a deterministic render tree — the same maths everywhere it plays.

01
Figma Motion

Animate on the timeline

Keyframes, easing curves and springs live in node.animations — raw data, never rasterized.

02
@blinn-motion/figma-plugin

Export, don't bake

The plugin reads the timeline and emits a tiny MotionDoc — no PNGs, no video, just your motion.

03
@blinn-motion/core

sample(doc, t)

The pure render method walks the doc, solves every track at time t, returns a resolved tree of final numbers.

one resolved tree · eight thin adapters
DOMdivs · CSS · SVG
Canvaspure-JS 2D
React<BlinnMotion />
Vuecomponent + composable
Svelteuse:blinnMotion
Angularstandalone component
Lit<blinn-motion>
React NativeExpo · native Views
Adapters

One document.
Every platform, frame for frame.

Every adapter paints the same resolved render tree, so an animation looks and times identically wherever it runs. The adapters are thin; the engine does the thinking.

DOM

@blinn-motion/dom
full fidelity

Full-fidelity CSS adapter — nested divs, gradients, SVG vector paths with arrowheads, clip-path stars, masks and procedural shaders.

import { create } from "@blinn-motion/dom";
create(el, doc, { loop: true }).play();

Canvas

@blinn-motion/canvas
zero DOM

A pure-JS 2D canvas painter for the very same resolved tree — one immediate-mode draw per frame, no DOM nodes.

import { create } from "@blinn-motion/canvas";
create(el, doc, { loop: true }).play();

React

@blinn-motion/react
component + hook

A declarative component and hook. Switch the painter with one prop; play, pause and seek through a ref.

<BlinnMotion doc={doc} renderer="canvas" loop autoplay />

React Native

@blinn-motion/react-native
native views

The same timing on native <View> / <Text>. No native module, no Skia dependency — just the shared core maths.

<BlinnMotionView doc={doc} loop />

Vue

@blinn-motion/vue
component + composable

Vue 3 component and useBlinnMotion composable — same DOM/Canvas backends and controlled progress surface.

<BlinnMotion :doc="doc" renderer="dom" :loop="true" />

Svelte

@blinn-motion/svelte
use:action

Idiomatic Svelte action (use:blinnMotion) plus attachBlinnMotion for full imperative control.

<div use:blinnMotion={{ doc, renderer: "canvas" }} />

Angular

@blinn-motion/angular
standalone component

Standalone <blinn-motion> with inputs for doc, renderer, progress and a frame output.

<blinn-motion [doc]="doc" renderer="dom" />

Lit

@blinn-motion/lit
custom element

<blinn-motion> web component — works in any stack that can host custom elements.

<blinn-motion .doc=${doc} renderer="canvas"></blinn-motion>
The render engine

One pure method does the work

@blinn-motion/core is time-based and DOM-free. The whole engine hangs off a single deterministic function — everything else is a thin painter.

core/sample.ts
// THE render method — pure, DOM-free
export function sample(doc: MotionDoc, t: number) return doc.layers.map((layer) =>
    resolve(layer, t)   // walk + sample every track
  );


// each track, eased + composed over the base
const v = ease(key.easing, local);
node[p] = op === "offset"
  ? base + lerp(a, b, v)   // additive
  : lerp(a, b, v);          // replace

// → resolved RenderNode tree (final numbers)
easing solvers
linearconstant rate
holdstep / no tween
cubicBezierNewton-Raphson
springdamped approx.
Time-based, in seconds

The core speaks one language: t in seconds. The playback clock is shared, so play / pause / seek / loop behave the same everywhere.

Progress-driven, too

Map any 0…1 signal — scroll, drag, state — with setProgress or sample(doc, progress × duration). No black-box player required.

Composes stacked tracks

Multiple tracks per property compose over the base value — set replaces, offset adds — so springs and curves layer cleanly.

Returns final numbers

Every transform, RGBA color and shape vertex comes out resolved. Adapters never re-interpret intent; they just paint.

Why Blinn Motion

Figma Motion, shipped as code

Keep motion where product teams already work — then resolve it to numbers and paint it the same way on every surface you ship to.

Figma is the source of truth

Select a Motion timeline, preview live, download MotionDoc — no rebuild in CSS, no After Effects detour.

Pure-JS render core

sample(doc, t) is DOM-free and unit-tested. Only adapters touch a platform — the engine runs anywhere JS does.

Springs & curves, solved

Linear, hold, cubic-bezier (Newton-Raphson) and damped springs — sampled per frame, not pre-baked.

MotionDoc you can own

Small, readable JSON. Inspectable, git-diffable, versionable — designed for product handoff, not a black box.

Identical timing everywhere

Same resolved tree, same shared clock. An animation times the same on DOM, Canvas, React, Vue, Svelte, Angular, Lit & Native.

Clock or progress-driven

Play on a shared clock — or drive with setProgress(0…1) / a progress prop for scroll- and gesture-linked motion on every binding.

Built for product UI motion

Nested transforms, gradients, vectors, masks, path trim, shaders — real Figma frames, not only illustration loops.

Tiny & framework-agnostic

No runtime deps in core. First-class packages for React, Vue, Svelte, Angular, Lit, RN — plus Next, Astro & Expo via those adapters.

Use it in code

Three lines to motion

Hand the engine a MotionDoc and a host. Play on a clock, or drive with setProgress(0…1) / a controlled progress prop. Same surface on React, Vue, Svelte, Angular, Lit, DOM, Canvas and RN.

install
npm i @blinn-motion/react

also: vue · svelte · angular · lit · dom · canvas · react-native

import { create } from "@blinn-motion/dom";
import doc from "./card.motion.json";

const player = create(
  document.getElementById("stage")!,
  doc,
  { loop: true }
);

player.play();
player.seek(0.8);          // seconds
player.setProgress(0.5);   // 0…1 — scroll / gesture
import { create } from "@blinn-motion/canvas";
import doc from "./card.motion.json";

const player = create(canvasHost, doc, { loop: true });
player.play();
// drive from scroll instead of the clock:
// player.setProgress(scrollY / range);
import { BlinnMotion } from "@blinn-motion/react";
import doc from "./card.motion.json";

// clock-driven
<BlinnMotion doc={doc} renderer="canvas" loop autoplay />

// progress-driven (scroll, drag, state)
<BlinnMotion doc={doc} progress={scrollP} />
import { BlinnMotion } from "@blinn-motion/vue";
import doc from "./card.motion.json";

// clock-driven
<BlinnMotion :doc="doc" renderer="dom" :loop="true" :autoplay="true" />

// progress-driven
// <BlinnMotion :doc="doc" :progress="scrollP" />
import { BlinnMotionView } from "@blinn-motion/react-native";
import doc from "./card.motion.json";

// clock
<BlinnMotionView doc={doc} loop autoplay />

// or progress-driven
<BlinnMotionView doc={doc} progress={gestureP} />
When motion lives in Figma

Ship the timeline.
Skip the detour.

Product teams already animate in Figma — then rebuild in CSS, export a video, or leave for another tool. Blinn is the runtime for motion that already lives in Figma.

Hand CSS / JSVideo / GIFBlinn Motion
Source of truthRebuilt from handoffBaked exportFigma Motion timeline
Timing & springsApproximate in CSS/JSLocked in pixelsSame keyframes & easings
InteractiveYes — if you code itPlay-only mediaPlay · seek · scrub · rate
PlatformsUsually web-onlyAnywhere as a fileDOM · Canvas · React · Vue · Svelte · Angular · Lit · RN
Git / PR reviewDiff the hand-written codeBinary blobDiffable MotionDoc JSON
Designer → engSpec + rebuildDrop a fileSame file ships

Blinn Motion is the runtime for Figma Motion — not a rebuild of another motion format. MIT-licensed and independent.

Open source · MIT

Animate in Figma.
Ship the same file.

Export from the Motion timeline, pick your stack in the Figma plugin, drop theMotionDoc into your app — DOM, React, Vue, Svelte, Angular, Lit, or native.

$ npm i @blinn-motion/react  # or vue · svelte · lit · dom