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
DOMCanvasReactReact Native
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 · four thin adapters
DOMdivs · CSS · SVG
Canvaspure-JS 2D
React<BlinnMotion /> component
React Nativenative <View> / <Text>
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 />
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 & Native.

Clock or progress-driven

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

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 dependencies in the core. Drop it into vanilla JS, React or React Native without ceremony.

Use it in code

Three lines to motion

Hand the engine a MotionDoc and a host element. Play on a clock, or drive with setProgress(0…1) / a React progress prop for scroll- and gesture-linked motion. Same surface on every adapter.

install
npm i @blinn-motion/dom
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 { 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 · 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, drop the MotionDoc into your app, and play it identically — on the web, in React, on native.

$ npm i @blinn-motion/dom @blinn-motion/core