Skip to content

MarkdyScript: The Mermaid of Motion

MarkdyScript is a declarative text DSL that turns 30 lines of plain code into Web Animations API scenes, git-diffable, LLM-friendly, and zero GSAP.

Hoang Yell
Hoang Yell
9 min read
Tiếng Việt
MarkdyScript: The Mermaid of Motion

TL;DR

  • What it solves: Authoring browser animations without leaving your text editor, recording a screen, or opening a design tool.
  • What it is: A declarative DSL where one plain text file produces a seekable, Web Animations API scene complete with stick figures, speech bubbles, and sprite throws.
  • Best for: Text-brain developers writing MDX documentation, solo builders making product landing pages, and anyone who wants the animation source to live next to the code it describes.
  • Best use case: Embedding an animated CLI walkthrough in a .mdx file that ships in the same PR as the code it documents.
  • Main tradeoff: Version 0.5.7, no LSP, straight-line actor motion only, and no per-actor keyframe loops. Great for demos and docs; not yet for complex production UI animation.

I spent half an hour writing a GSAP timeline to animate three words fading in. The result was a useEffect with a cleanup function, a ref with a null check, and twenty-two lines of imperative JavaScript describing what should have been four words: “fade in, then slide.” The animation played once. I refactored the parent component, the ref broke silently, and nobody noticed until a pull request comment said “is this intentionally static?”

The problem was not GSAP. GSAP is fine. The problem is that I was writing animation as code when I wanted to write animation as a description, and those are not the same activity. Mermaid codified the difference for diagrams years ago: nobody writes flowcharts in D3 anymore, they type A --> B and get an arrow. The same philosophy should exist for motion. For a long time it did not.

MarkdyScript is that philosophy, shipped.

The Pitch

MarkdyScript is a line-based DSL that compiles to Web Animations API calls. Write a text file organized like a screenplay: declare your actors, place events on a timeline with @time: markers, and the browser plays it. The whole scene is a plain string you can commit, git diff, and hand to an LLM.

The parser (@markdy/core) is a zero-dependency TypeScript pure function at approximately 12 KB; it produces a typed AST with line-number errors on bad input. The renderer (@markdy/renderer-dom) drives the browser’s Web Animations API via a manual requestAnimationFrame loop and weighs approximately 22 KB. Together: 34 KB, lighter than a single medium-resolution Lottie animation file, with no canvas, no GSAP, and no build plugin beyond the one-line Astro integration.

Writing GSAP is like directing a play by sending the actors raw muscle instructions frame by frame, instead of a script they can read and rehearse. MarkdyScript is the script. The timing is not accidental: the Web Animations API finally has reliable broad-browser support, and post-Mermaid developer expectations have normalized the idea that structured visuals should live in text files. The question is whether 30 lines of that text can actually replace the record-export-embed-expire cycle in a real documentation workflow.

Real-World Use Cases

Five places where a .markdy file beats every alternative:

  1. MDX documentation: The animation ships in the same PR as the code it explains. No separate assets/ folder, no re-recording when the API changes, no GIF growing stale in public/.
  2. Product landing pages: A stick figure enters, looks confident, says your tagline. The whole thing is a text block. Nothing ever needs re-recording because nothing was ever recorded.
  3. Tutorial step demos: Show “before state” transitioning to “after state” with enter, move, and say. Seekable, so a reader can pause at the exact moment they need.
  4. Team wikis and RFCs: Sequence diagrams that actually animate. Unlike Mermaid, the actors can speak, react, and get punched when they say something wrong.
  5. AI-generated animations: The DSL is explicitly designed for LLM generation. The repo ships docs/AGENT.md, a prompt-ready grammar reference that an LLM can use to write and iterate on scenes without understanding a single line of WAAPI internals.

The case that reveals the most is case one. You are documenting a CLI tool. You want to show mytool init running, scaffolding a project, then mytool verify confirming success. The old workflow: record a GIF against a specific terminal theme, 400 KB, already outdated the day you rename the command. The new workflow is text:

scene width=800 height=400 bg=#1a1a2e

var skin = #c68642

actor dev = figure(\${skin}, m, 😎) at (200, 200)
actor cli = figure(#5b9bd5, m, 😐) at (600, 200)

@0.0: dev.enter(from=left, dur=0.8)
@0.4: cli.enter(from=right, dur=0.8)
@1.5: dev.say("mytool init", dur=1.2)
@2.5: cli.face("🤔")
@3.0: cli.say("Scaffolding...", dur=1.5)
@4.5: cli.face("✅")
@5.0: dev.say("mytool verify", dur=1.2)
@6.0: cli.say("All checks passed", dur=2.0)

Before: a 400 KB recording tied to a specific Tuesday. After: 18 lines of plain text, diffable on GitHub, regenerable by any tool that reads the grammar reference. When the command is renamed, the fix is one line, in the same PR, with a readable diff.

The scene below runs that philosophy through a four-actor drama: junior submits a PR, senior reviewer reacts badly, PM mediates, and the whole arc from submission to approval lives in fifty lines of plain text:

That entire scene is 50 lines of plain text. One def template (guy) eliminates the repeated figure declaration. Three seq blocks (celebrate, rage, nod) define the choreography once and play it on any figure across the timeline. The four-actor arc from enthusiasm to conflict to resolution is encoded in the source; no JavaScript was authored. How that source turns into a running browser animation is the installation question.

How to Use It

Three installation paths, depending on your stack.

Astro or MDX (recommended):

pnpm add @markdy/astro

In any .astro file:

---
import { Markdy } from "@markdy/astro";

const code = `
  scene width=800 height=400 bg=#fff5f9
  actor hero = figure(#c68642, m, 😎) at (300, 200)
  @0.5: hero.enter(from=left, dur=0.8)
  @1.5: hero.say("Hello!", dur=1.0)
`;
---
  1. Run pnpm dev. The scene plays on viewport entry.
  2. For a non-Astro project, install @markdy/core + @markdy/renderer-dom and use the createPlayer path from the previous section.

The StackBlitz starter at https://stackblitz.com/github/HoangYell/markdy-com/tree/main/examples/astro-starter requires zero local install. The full grammar reference is docs/AGENT.md in the repo: one page, every valid statement, every parameter. The five concepts to internalize first are scene, actor, @time:, def, and seq. After those five, everything else is just action names and acceptable parameter values, and whether the alternatives section changes any of those conclusions is the next honest question.

How It Compares: Alternatives

Tool What it is The gap
GSAP / Framer Motion Imperative JS animation libraries You write code, not descriptions. Timeline chains are hard to read, hard to generate, and hard to diff in a pull request.
Mermaid Declarative diagram DSL Diagrams only. No timeline, no motion, no speech bubbles, no Web Animations API.
LottieFiles JSON animation exported from After Effects Design-tool pipeline. Files are large, unreadable JSON blobs. Requires After Effects or Rive to author anything.
Screen recording / GIF Record and export Goes stale. Cannot be meaningfully version-controlled. Requires a real running UI.
CSS @keyframes Pure CSS transforms No actors, no speech bubbles, no shared timeline orchestration. Composing three actors across the same timeline is manual and fragile.
MarkdyScript Declarative text DSL, Web Animations API Text file, git-diffable, LLM-friendly, approximately 34 KB, zero canvas, zero GSAP.

MarkdyScript wins for text-brain developers who prefer 30 lines of script over opening Figma, and who want the animation source to live in the same commit as the feature it describes. It loses for teams that need curved motion paths, design-tool collaboration, or a production animation system that will outlive a pre-1.0 API. Whether MarkdyScript reaches 1.0 before your next project needs it is the open question.

FAQ

What is MarkdyScript and how does it differ from Mermaid?

MarkdyScript is a text-based animation DSL for the browser. Mermaid generates static SVG diagrams (flowcharts, sequence diagrams, Gantt charts) from text. MarkdyScript generates animated scenes that play using the Web Animations API: actors enter, move, speak, and react over a timeline. The two tools cover completely different territory with the same declarative philosophy.

Do I need to know JavaScript to write MarkdyScript?

No. The DSL is self-contained: scene, actor, @time:, def, and seq are the five statement types you need. There is no JavaScript in a .markdy file. The Web Animations API is fully abstracted by the renderer. The same syntax works whether you write in Astro MDX, plain HTML, or a CI validation pipeline.

Can I use MarkdyScript outside Astro?

Yes, via @markdy/core + @markdy/renderer-dom. The createPlayer({ container, code }) function works in any project that can import an ESM npm package. The parser also runs on Node 18/20/22, Deno, and Cloudflare Workers as a pure function. The @markdy/astro package is a convenience island wrapper, not a requirement.

Is MarkdyScript production-ready?

The current release is v0.5.7. The parser and the def/seq systems feel stable; the createPlayer option names are not yet locked before 1.0. For documentation, product pages, and tutorial demos, the risk is low. For a long-term production animation system with strict API stability requirements, waiting for 1.0 is the more cautious call.

How do I animate multiple actors at the same time?

Multiple @time: events with the same timestamp play simultaneously. To reuse a sequence of coordinated actions, define a seq block and call actor.play(seqName) from any @time: event. The sequence expands inline at parse time with zero runtime overhead. See the seq punch_combo(side) pattern in docs/AGENT.md for the exact syntax with parameterized sequences.

The Text File Wins

I opened a GSAP project I wrote fourteen months ago. The timeline had nine chained .to() calls, a ScrollTrigger plugin, and three refs that had to be alive simultaneously. It animated a loading screen that was replaced eight months later. The animation file is still in src/animations/. Nobody wants to delete it; nobody knows if it is still referenced anywhere.

A .markdy file would have been twelve lines. It would have said exactly what it did. When the loading screen was replaced, so would the file, in the same commit, with a diff you could actually read.

MarkdyScript is v0.5.7 with zero stars, no LSP, no bezier paths, and no keyframe loops. For the narrow job it is built for, which is describing a simple animated scene in committed text, it does that job with zero runtime dependencies and a grammar that fits on one page. Mermaid has 76,000 GitHub stars today. The idea that diagrams should be text turned out to be correct. The idea that motion should be text has the same shape, and someone had to build it first.

HoangYell/markdy-com · MIT · 0★ · markdy.com

Related posts