JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 991
  • Score
    100M100P100Q81568F
  • 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

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
circle r (radius), fill, stroke, strokeWidth
text font, size, fill
line from, to, stroke, strokeWidth
path d, points, fx+xRange, xt+yt+tRange, steps, fill, stroke, closed
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

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.