JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 991
  • Score
    100M100P100Q81496F
  • License MIT

Markdown for Motion Graphics. Text-in, video-out. A DSL for creating animations.

Package Exports

  • motionmark
  • motionmark/engine
  • motionmark/exporter
  • motionmark/renderer
  • motionmark/schema
  • motionmark/validator

Readme

MotionMark

Markdown for Motion Graphics. Text-in, video-out.

MotionMark is a domain-specific language (DSL) for creating motion graphics and animations using simple, readable text files. Write declarative code, preview and export in browser.

Quick Start

Requirements

  • Node.js

Installation

npm install
npm run build

Preview

npm run preview
# or with a specific file
npm run preview examples/showcase/trig-graphs.mmark

Open http://localhost:5175 in your browser. Edit code on the left, see live preview on the right. Export to WebM/MP4 directly from the browser.

File Format

MotionMark files use the .mmark extension.

Basic Structure

---
canvas: 1920x1080
bg: #0f0f1a
fps: 60
$accent: #e74c5c
$teal: #4ecdc4
---

rect r1 w:200 h:100 fill:#e74c5c | 0s - 3s
  x: 100 -> 500 over 2s ease-out
  y: 200
  opacity: 0 -> 1 over 0.5s

Syntax Reference

Header Block

Define canvas settings and variables at the top of your file:

---
canvas: 640x360
bg: #0f0f1a
fps: 24
$accent: #e74c5c
$teal: #4ecdc4
$speed: 80
---
Property Description Example
canvas Width x Height in pixels 1920x1080, 640x360
bg Background color (hex) #0f0f1a
fps Frames per second 24, 30, 60
$name Variable declaration $accent: #e74c5c

Elements

Rectangle

rect id w:WIDTH h:HEIGHT fill:COLOR
  x: VALUE
  y: VALUE

Circle

circle id r:RADIUS fill:COLOR
  x: VALUE
  y: VALUE

Ellipse

ellipse id rx:RX ry:RY fill:COLOR
  x: VALUE
  y: VALUE

Text

text id "Content" font:FONTNAME size:SIZE fill:COLOR
  x: VALUE
  y: VALUE

Line

line id from:(X1,Y1) to:(X2,Y2) stroke:COLOR strokeWidth:WIDTH

Gradients

Use gradients anywhere a fill or stroke paint is accepted. Stops are written as color, offset pairs, where offsets run from 0 to 1.

rect panel w:240 h:120 | 0s - 3s
  fill: linear(0, 0, 240, 0, #e74c5c, 0, #4ecdc4, 1)

circle glow r:80 | 0s - 3s
  fill: radial(0, 0, 0, 0, 0, 80, #fbbf24, 0, #ef4444, 1)

Path

Paths support 4 creation methods:

1. SVG d syntax (from design tools, bezier curves)

path arrow d:"M 10 50 L 90 50 L 70 30 M 90 50 L 70 70" stroke:#fff strokeWidth:2

2. Points array (manual coordinates, polygons)

path triangle [(100,200), (150,100), (200,200)] fill:#e74c5c closed:true

3. Function path fx (graphs, waves — y depends on x)

path sine fx:"200 - 50 * sin(x * 0.05)" xRange:"0-400" steps:50 stroke:#60a5fa strokeWidth:2

4. Parametric path xt/yt (circles, spirals, hearts)

path circle xt:"200 + 80 * cos(t)" yt:"200 + 80 * sin(t)" tRange:"0-6.28" steps:60 stroke:#4ade80 strokeWidth:2

When to use which:

Method Use when
d:"..." You have SVG path data from Figma/Illustrator
[(x,y), ...] You have exact coordinates for a simple shape
fx:"..." Drawing a graph/wave where y = f(x)
xt/yt:"..." Drawing shapes that loop back (circles, spirals, hearts)

Quick test: Can you draw it left-to-right without going backwards on x?

  • Yes → use fx
  • No → use xt + yt

Image

image id "filename.png" w:WIDTH h:HEIGHT
  x: VALUE
  y: VALUE

Element Lifetime

Control when elements appear and disappear:

rect r1 w:200 h:100 fill:#e74c5c | 0s - 3s    # visible from 0s to 3s
rect r2 w:200 h:100 fill:#4ecdc4 | 2s - 5s    # visible from 2s to 5s
rect r3 w:200 h:100 fill:#fff | persist        # visible entire duration

Animation

rect r1 w:200 h:100 fill:#e74c5c | 0s - 3s
  x: 100 -> 500 over 2s ease-out           # animate from 100 to 500
  opacity: 0 -> 1 over 0.5s                 # fade in
  scale: 1 -> 1.2 -> 1 over 1s ease-in-out  # scale up then down

Multi-step Keyframes

circle ball r:18 fill:#e74c5c | 0s - 4s
  y: 272 at 0s, 112 at 0.8s ease-out, 272 at 1.6s ease-in, 150 at 2.4s ease-out

Expression Mode (Advanced)

Use mathematical expressions with f(t) where t is time in seconds:

circle ball r:14 fill:#e74c5c | 0.8s - 4s
  x: f(t) = 74 + 80 * t
  y: f(t) = 260 - (120 * t - 35 * t^2)

Available math functions: sin, cos, tan, sqrt, abs, min, max, pow

Use degrees with the deg suffix: cos(45deg)

Easing Functions

Easing Description
linear Constant speed
ease-in Slow start
ease-out Slow end
ease-in-out Slow start and end
cubic-bezier(x1,y1,x2,y2) Custom bezier curve

Variables

Define in header, use with $:

---
$accent: #e74c5c
$speed: 80
---

circle ball r:20 fill:$accent
  x: f(t) = 200 + $speed * t

Scenes

Organize animations into logical sections:

= Setup | 0s - 5s =

rect title w:400 h:80 fill:#4ecdc4
  x: 100
  y: 100

= Launch | 5s - 10s =

circle ball r:20 fill:#e74c5c | 5s - 10s
  x: f(t) = 200 + 50 * t

Elements without explicit lifetime inherit their scene's time bounds.

Custom Motions (@motion)

Define reusable animation patterns:

@motion fade-in(dur=0.4s, ease=ease-out)
  opacity: 0 -> 1 over $dur $ease

@motion slide-up(from, to, dur=0.7s, ease=ease-out)
  y: $from -> $to over $dur $ease

@motion projectile(vx, vy, g, x0, y0)
  x: f(t) = $x0 + $vx * t
  y: f(t) = $y0 - ($vy * t - 0.5 * $g * t^2)

Apply motions to elements:

text title "Hello" font:Roboto size:48 fill:#fff | 0s - 4s
  x: 100
  fade-in()
  slide-up(from: 200, to: 150)

circle ball r:20 fill:#e74c5c | 2s - 8s
  projectile(vx: 160, vy: 330, g: 250, x0: 220, y0: 780)

Standard Library Motions

@motion fade-in(dur=0.5s, ease=ease-out)
@motion fade-out(dur=0.5s, ease=ease-in)
@motion slide-right(from, to, dur=1s, ease=ease-out)
@motion slide-left(from, to, dur=1s, ease=ease-out)
@motion slide-down(from, to, dur=1s, ease=ease-out)
@motion slide-up(from, to, dur=1s, ease=ease-out)
@motion scale-in(dur=0.5s, ease=ease-out)
@motion scale-out(dur=0.5s, ease=ease-in)
@motion pulse(dur=0.8s, ease=ease-in-out)
@motion drift-x(speed)

Audio Tracks

Add independent audio elements to your animation timeline:

audio "bg-music.mp3" | 0s - end volume:0.3 fade-in:2000 fade-out:1000
audio "whoosh.wav" | 1.5s - 2.5s volume:0.8
Property Description Default
volume Playback volume (0-1) 1
pan Stereo pan (-1 left, 1 right) 0
fade-in Fade-in duration in ms 0
fade-out Fade-out duration in ms 0
trim Skip this many ms from the start of the file 0
loop Loop the audio (true/false) false

Audio files are resolved relative to the .mmark file's directory.

Masking

Apply rectangular masks to elements:

rect panel w:440 h:150 fill:#e74c5c | 0s - 4s
  x: 100
  y: 112
  mask: rect(100, 112, 0, 150)

Examples

Bouncing Ball

---
canvas: 640x360
bg: #0f0f1a
fps: 24
$accent: #e74c5c
---

@motion fade-in(dur=0.3s, ease=ease-out)
  opacity: 0 -> 1 over $dur $ease

text title "Bouncing Ball" font:Roboto size:28 fill:#ffffff | 0s - 4s
  x: 36
  y: 54
  fade-in()

line floor from:(40,304) to:(600,304) stroke:#ffffff55 strokeWidth:3 | persist

circle ball r:18 fill:$accent | 0s - 4s
  x: 80 -> 520 over 4s linear
  y: 272 at 0s, 112 at 0.8s ease-out, 272 at 1.6s ease-in, 150 at 2.4s ease-out, 272 at 3.2s ease-in

Product Intro

---
canvas: 640x360
bg: #111827
fps: 24
$accent: #e74c5c
$teal: #4ecdc4
---

@motion fade-in(dur=0.4s, ease=ease-out)
  opacity: 0 -> 1 over $dur $ease

@motion slide-up(from, to, dur=0.7s, ease=ease-out)
  y: $from -> $to over $dur $ease

rect logo w:72 h:72 fill:$accent | 0s - 4s
  x: 70
  y: 110
  opacity: 0 -> 1 over 0.4s

text brand "MotionMark" font:Roboto size:42 fill:#ffffff | 0.25s - 4s
  x: 170
  slide-up(from: 164, to: 142)
  fade-in(0.5s)

text tagline "Markdown for motion graphics" font:Roboto size:22 fill:#cbd5e1 | 0.8s - 4s
  x: 174
  slide-up(from: 216, to: 194, dur: 0.55s)
  fade-in(0.45s)

Physics Simulation

@motion projectile(vx, vy, g, x0, y0)
  x: f(t) = $x0 + $vx * t
  y: f(t) = $y0 - ($vy * t - 0.5 * $g * t^2)

circle ball r:20 fill:#e74c5c | 2s - 8s
  projectile(vx: 160, vy: 330, g: 250, x0: 220, y0: 780)
  opacity: 0 -> 1 over 0.2s

Properties Reference

Common Properties

Property Description Animatable
x Horizontal position Yes
y Vertical position Yes
opacity Transparency (0-1) Yes
scale Size multiplier Yes
rotation Rotation (radians) Yes
draw Draw progress (0-1) Yes

Shape-specific Properties

Element Properties
rect w, h, fill, stroke, strokeWidth, cornerRadius
circle r (radius), fill, stroke, strokeWidth
ellipse rx, ry (radii), fill, stroke, strokeWidth
text font, size, fill
line from, to, stroke, strokeWidth, strokeCap
path d, points, fx+xRange, xt+yt+tRange, steps, fill, stroke, closed, strokeCap, strokeJoin
image w, h, source path

Architecture

.mmark file (DSL)
       |
       v
   PARSER (DSL -> Scene IR)
       |
       v
   SCENE IR (JSON) ─── audioTracks[]
       |                     |
       v                     v
   ENGINE (IR + time)    Web Audio API
       |                     |
       v                     v
   CANVAS 2D ──────────> MediaRecorder / WebCodecs
       |
       v
   WebM / MP4 (browser export)

Project Structure

motion-mark/
  packages/
    schema/     # IR JSON schema + TypeScript types
    parser/     # DSL tokenizer, AST, compiler
    engine/     # IR + time -> frame state
    renderer/   # Frame rendering
    exporter/   # Headless render -> MP4
    validator/  # DSL validation
    ai/         # NL -> DSL pipeline
  stdlib/
    motion/     # Standard @motion marks
  examples/     # Example .mmark files
  tests/        # Test suite

Draw Animation

Reveal elements progressively with the draw property (0-1):

line arrow x1:0 y1:100 x2:200 y2:100 stroke:#fff strokeWidth:2 | 0s - 3s
  draw: 0 -> 1 over 2s ease-out

circle ring r:50 fill:none stroke:#4ecdc4 strokeWidth:3 | 0s - 3s
  x: 300
  y: 100
  draw: 0 -> 1 over 1.5s ease-out

text title "Hello" font:Roboto size:32 fill:#fff | 0s - 3s
  x: 200
  y: 200
  draw: 0 -> 1 over 2s linear

path curve fx:"100 - 30 * sin(x * 0.05)" xRange:"0-300" steps:50 stroke:#e74c5c strokeWidth:2 | 0s - 3s
  draw: 0 -> 1 over 2s ease-out
Element Draw Effect
line Reveals from start to end
circle Arc reveals 0° → 360°
rect Outline draws clockwise
text Typewriter (char by char)
path Stroke reveals along curve
image Wipe reveal left → right

Stroke Controls

Fine-grained control over stroke rendering with drawStart, drawEnd, dashArray, and dashOffset:

circle spinner r:48 fill:none stroke:#60a5fa strokeWidth:6 | 0s - 6s
  x: 200
  y: 170
  drawStart: 0 -> 0.75 over 6s linear
  drawEnd: 0.25 -> 1 over 6s linear
  dashArray: [8, 6]
  dashOffset: 0 -> 120 over 6s linear

rect ants w:240 h:150 fill:none stroke:#34d399 strokeWidth:4 anchor:top-left | 0s - 6s
  x: 410
  y: 130
  dashArray: [14, 10]
  dashOffset: 0 -> 220 over 6s linear

path squiggle d:"M 120 380 C 210 300, 330 460, 420 380 S 630 300, 720 380" stroke:#f59e0b strokeWidth:6 fill:none | 0s - 6s
  drawStart: 0 -> 0.7 over 6s linear
  drawEnd: 0.3 -> 1 over 6s linear
Property Description Animatable
drawStart Start of visible stroke window (0-1) Yes
drawEnd End of visible stroke window (0-1) Yes
dashArray Dash pattern lengths [dash, gap, ...] No
dashOffset Offset into dash pattern Yes
strokeCap Line endpoint style: butt, round, square No
strokeJoin Corner join style: miter, round, bevel No

Shape Styling

Corner Radius

Round the corners of rectangles with a single value or per-corner array:

rect card w:200 h:120 fill:#1f2937 cornerRadius:12 | persist
rect pill w:200 h:60 fill:#4ecdc4 cornerRadius:30 | persist
rect mixed w:200 h:120 fill:#fff cornerRadius:[20, 0, 20, 0] | persist

Animatable — smoothly morph between sharp and rounded:

rect morph w:100 h:100 fill:#e74c5c | 0s - 2s
  cornerRadius: 0 -> 50 over 2s ease-in-out

Stroke Cap & Join

Control how line endpoints and corners render:

line beam x1:50 y1:100 x2:300 y2:100 stroke:#f59e0b strokeWidth:10 strokeCap:round | persist
path zigzag d:"M 50 200 L 150 250 L 250 200" stroke:#f472b6 strokeWidth:8 fill:none strokeJoin:round | persist
Value strokeCap strokeJoin
butt Flat end (default for paths)
round Rounded end (default for lines) Smooth arc corner
square Extended flat end
miter Sharp pointed corner (default)
bevel Flat diagonal cut

Filters

Apply visual effects to any element:

image photo "image.jpg" w:360 h:240 | 0s - 6s
  x: 220
  y: 245
  blur: 12 -> 0 over 1.2s ease-out
  saturate: 0 -> 1 over 1.6s ease-out
  contrast: 0.9 -> 1.15 over 2s ease-in-out
  brightness: 0.8 -> 1.1 over 2s ease-in-out
  hueRotate: 0 -> 25 over 6s linear
  shadow: 0 14 28 #00000066

text neon "NEON" font:Roboto size:56 fill:#22d3ee weight:bold | 0s - 6s
  x: 610
  y: 200
  shadow: 0 0 24 #22d3ee
  brightness: 1 -> 1.35 over 1.4s ease-in-out
Property Description Default Animatable
blur Gaussian blur in px 0 Yes
brightness Brightness multiplier 1 Yes
contrast Contrast multiplier 1 Yes
saturate Saturation multiplier 1 Yes
hueRotate Hue rotation in degrees 0 Yes
shadow Drop shadow offsetX offsetY blur color none No

Dynamics (Wiggle + Spring)

Spring Easing

Use spring() as an easing function for physically-based animations:

rect card w:220 h:120 fill:#1f2937 stroke:#22d3ee strokeWidth:2 | 0s - 6s
  x: 290 -> 400 over 1.2s spring(stiffness:180, damping:12)
  scale: 0.7 -> 1 over 1s spring(140, 11)

Spring parameters: spring(stiffness, damping, mass, velocity) — all optional with defaults 180, 12, 1, 0.

Wiggle Expression

Deterministic noise for organic floating motion:

circle float r:14 fill:#fb7185 | 0s - 6s
  x: wiggle(2, 44, base:180, seed:1)
  y: wiggle(3, 26, base:360, seed:2)

wiggle(frequency, amplitude, base, seed) — generates smooth pseudo-random motion around base.

Motion Path

Animate elements along an SVG path with arc-length parameterization:

path route d:"M 80 315 C 180 120, 360 120, 440 260 S 620 430, 720 210" stroke:#334155 strokeWidth:3 fill:none | 0s - 7s
  dashArray: [12, 10]
  dashOffset: 0 -> 160 over 7s linear

rect comet w:34 h:14 fill:#22d3ee anchor:center | 0s - 7s
  motionPath: "M 80 315 C 180 120, 360 120, 440 260 S 620 430, 720 210"
  motionProgress: 0 -> 1 over 5s ease-in-out
  motionRotate: auto
  shadow: 0 0 22 #22d3ee
Property Description Animatable
motionPath SVG path d string to follow No
motionProgress Position along path (0-1) Yes
motionRotate auto to orient along tangent No

Repeat

Clone an element with staggered offsets:

circle spark r:7 fill:#fb7185 | 1s - 7s
  repeat: 10
  repeatOffset: x:26 y:-10 opacity:-0.075 delay:0.06s
  x: 285
  y: 390
  scale: 0 -> 1 over 0.35s spring(stiffness:140, damping:10)
  opacity: 1 -> 0 over 1.2s ease-out
Property Description
repeat Number of clones
repeatOffset Per-clone offset: x, y, opacity, scale, rotation, delay

Tips

  1. Z-order: Elements are rendered in declaration order. Later elements appear on top.

  2. Time is relative: In expressions, t starts at 0 when the element appears, not global time.

  3. Use variables: Define colors and values once in the header for easy theming.

  4. Compose motions: Apply multiple @motion definitions to a single element.

  5. Start simple: Begin with static elements, then add animation progressively.

License

See LICENSE file for details.