Motion Graphics logo

Motion Graphics

Organization
acelera-agency
motion-graphics

Build frame-exact motion graphics — animated logos, kinetic typography, brand videos, animated titles and social clips — as a single HTML file driven by a paused Web Animations timeline, then export them to MP4 in 1:1, 4:5 and 9:16. Use this whenever someone wants to animate a wordmark or logo, make a video from text, build a title sequence or lower third, produce a clip for LinkedIn / Instagram / Reels / TikTok, "turn this design into a video", animate a landing-page hero, or export a web animation to a video file — and also when an existing animation of this kind needs fixing, retiming, restyling or re-exporting. Reach for it even if the person only says "animate our logo", "make this move", or "I need a short video for the launch", without naming any of the above.

Overview

Publisheracelera-agency
Repositoryhtml-animation
Skill namemotion-graphics
Stars
10
Forks
1
Bundled files
12
LicenseMIT
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • 12 bundled files

    Scripts, templates, and references the model can read while it works. Files are read-only and never executed.

  • Open source

    Published by acelera-agency on GitHub. Read the source before you install it.

Installation

Install the Motion Graphics AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/acelera-agency/html-animation/tree/main
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/acelera-agency/html-animation.git \
  .claude/skills/motion-graphics
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Motion Graphics in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Motion Graphics on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Motion Graphics is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Motion graphics as code

Everything here is built the same way: one HTML file, one paused timeline, deterministic export. No video editor, no After Effects, no timeline GUI. The animation is code, so it is diffable, reviewable, re-renderable at any size, and — crucially — measurable.

That last word is the heart of this skill. Motion work invites eyeballing, and eyeballing lies. Nearly every real defect you will hit here is invisible to a glance and obvious to a measurement, and several things that look broken turn out to be the measuring script's fault. Build the habit early: when something looks wrong, get a number before you get an opinion.

The shape of the thing

project/
├── video.html      # canvas, scenes, timeline — the whole animation
├── export.js       # frame-by-frame capture → MP4, all aspect ratios
└── verify.js       # whatever you need to assert about this piece

Copy assets/starter.html to begin. It is a complete, working animation with the engine already wired — type that enters, a recurring mark that highlights a word, and a scene that exits. Its palette and motif are placeholders; the structure is the point. Read it before writing anything; it is faster than assembling the pieces from the references.

scripts/timeline.js is the engine and scripts/physics.js the simulator — inline both into the HTML, since a single self-contained file is the deliverable. scripts/export.js and scripts/verify.js run in Node and work unchanged for any project that exposes window.__seek and window.__duration.

Why a paused timeline

CSS animations and libraries run on wall-clock time. That is fine for a website and useless for a video: to capture frame 847 you must be able to put the animation at frame 847, exactly, on demand.

So: every animation is created through the Web Animations API with fill: 'both', its absolute start time baked into the timing as delay, and immediately paused. Then

js
function seek(s){ A.forEach(a => { a.pause(); a.currentTime = s * 1000; }); }

positions the entire piece at any instant. The exporter walks 0 → duration in 1/fps steps and screenshots. The result is identical on any machine and does not care whether the browser can hit 60fps in real time.

This buys you something else: you can interrogate any moment. seek(7.2) then read a getBoundingClientRect(). That is what makes the verification below possible.

Workflow

1. Lay out the canvas. Fixed internal coordinates (1080 wide is a good default), scenes absolutely positioned and stacked, only the height varying per aspect ratio. Read references/craft.md before placing type — optical centering is not the same as centering the box, and getting it wrong is the single most common reason a composition "feels off" without anyone being able to say why.

2. Build the timeline. Scene by scene. Give each element one animation covering its whole life. Keep an eye on the four failure modes below; they account for most of the time lost on work like this.

3. Verify by measuring. Before showing anything, write the small script that checks the thing you are unsure about. references/verify.md has the recipes and, more importantly, the traps in the measuring scripts themselves.

4. Export. node export.js → the MP4s. references/export.md covers the encoder flags that decide whether a platform accepts your file.

The four things that will bite you

These are not style preferences. Each one produced a bug that survived visual review.

One animation per element

Entrance, hold and exit go in a single keyframe list. Two animations with fill: 'both' touching the same property collide, and the later-created one wins from time zero — so an element that should appear at 14s is visible from the first frame, with its "before" state applied backwards through the whole piece.

The symptom is bizarre and misleading: content from a later scene bleeding through an earlier one. If you see that, count the animations on the element.

seek() must pause first

A preview loop keeps playing. Set currentTime on a running animation and it advances a few milliseconds before the screenshot lands. This is not a preview annoyance — it silently corrupts every frame of the export, and the result looks plausible enough that you will ship it.

Pause, then set the time, and cancel the preview loop the first time an external seek arrives.

Keyframe times must be monotonic

When you chain moves — "then it hops here, then there" — it is easy to schedule one before the previous finished. WAAPI rejects the whole animation with Offsets must be monotonically non-decreasing, which tells you nothing about which move overlapped.

Clamp on the way in, so the times in your script are intentions rather than fragile arithmetic:

js
const push = (t, s, ease) => { t = Math.max(t, lastT); lastT = t; /* ... */ };

The same hazard reappears in anything assembled from several sequences (a shared camera track, for instance). Clamp there too.

Measure before you animate

fill: 'both' applies the first keyframe immediately, so the moment you create the animations, every getBoundingClientRect() reflects a transform. Do all layout measurement first, then build the timeline. Ordering: build DOM → optically centre → measure targets → create animations.

Pick a motion language, then hold to it

The most common way a piece looks amateur is that different elements arrive in different ways. Choose one way things enter and one way they leave, and apply it everywhere. Which one is a tone decision, not a technical one:

LanguageSaysMG.ENTER / MG.EXIT
Speed — arrives fast with a directional smear and brakesurgency, sport, news, "we move fast"speed
Pop — grows from nothing and overshootsplayful, product, consumer, toypop
Wipe — a hard edge uncovers it, nothing moveseditorial, architectural, confidentwipe
Drift — barely moves, the fade does the workpremium, calm, luxury, seriousdrift
Rise — comes up out of its own baselinecinematic titles, creditsrise
Focus — comes into focus from blurredphotographic, dreamy, memoryfocus

They return keyframe steps, so an entrance and an exit concatenate into one list:

js
tl(el, [...MG.ENTER.pop(tIn, {size: fs}), ...MG.EXIT.drift(tOut, {size: fs})]);

Nothing about the rest of this skill assumes a particular one. If a brief does not suggest a tone, ask — it is a bigger decision than any timing.

What holds regardless of language

Nothing should ever be perfectly still. A frame that holds motionless for two seconds reads as stuck, even at a flawless 60fps. Every scene gets a slow continuous push — settle over ~1.5s, then keep drifting a fraction of a percent. Around 0.7% total; at 1.4% people notice things "levitating".

Arrivals decelerate, departures accelerate. Whatever the language, matching the two curves makes everything feel rubbery.

Chained ease-outs read as stop-start. Each segment decelerates to zero and the next starts fast again. Use ease-in-out for anything continuous, and reserve the braking curve for actual arrivals.

Stagger is a lever. The same language feels completely different per-letter, per-word or per-line. Per-letter is energetic and gets tiring past a few words; per-line is calm and reads better for sentences.

Scale the exit down for large type. Exit distance is usually proportional to size, so at 112px a default sweep crosses half the screen and collides with the next scene.

A recurring element can carry the whole piece. One shape that persists across scenes gives a video a spine that cuts alone cannot — a rule that underlines the key word, a dot, a cursor, a colour block that reframes each card, a mask edge. Treat it as a character rather than a graphic: anticipation before it moves, overshoot on arrival, weight when it lands, a small idle vocabulary for when it waits. Drive any randomness from a seeded generator, because Math.random() makes the preview and the export different films.

An element that crosses text reads as a rendering bug, even when the copy explicitly calls for a strikethrough. Keep it behind the type so letters cut through it.

Dark backgrounds change things. A trail's colour, flat-area banding, and the fact that two stacked vignettes draw a visible ellipse edge. MG.trail() takes a colour for exactly this reason — hardcoded black on a dark ground is an invisible smear.

Physics, when you need it

For anything thrown, dropped or bounced, integrate the motion rather than reaching for an easing curve. An ease-out is not a fall — it decelerates, and falls accelerate. The same goes for departures, which start slow and build.

scripts/physics.js has this solved. PHYS.drop() does projectile, bounce and topple for boxes; PHYS.roll() does the same for anything round, which is a genuinely different solver; PHYS.departure() does something pulling away; PHYS.keyframes() turns any of them into keyframes. Use them rather than rewriting — the two bugs that make hand-rolled rigid bodies look wrong are already handled, and references/physics.md explains what they were.

Verify by measuring

The habit, concretely: before you claim something is fixed, write six lines that check it and print a number.

  • Reproducing a reference exactly? Diff the rendered pixels, then — because pixel counts explode from sub-pixel antialiasing — also compare geometry, element by element, at matching instants. Geometry is the real claim; the pixel count is a coarse net.
  • Something "feels janky"? Measure frame times before touching the design. It is often not dropped frames.
  • Element flying off-frame? Sample its rect across the window and report the worst overflow.

And a warning that will save you an hour: when a check suddenly fails, suspect the check. In practice most alarms were stale measurement windows, a fixed timestamp that drifted after a retime, a comparison of elements that were invisible at that moment, or a race in the harness. Confirm the failure independently before you "fix" working code.

scripts/verify.js has each of these as a function you can import; references/verify.md explains what each one is really testing.

Export

node export.js renders 1:1, 4:5 and 9:16. Three things decide whether the file is usable:

  • yuv420p — without it, platforms and QuickTime refuse to decode.
  • +faststart — without it, playback waits for the whole download.
  • A silent audio track — some platforms reject video-only uploads outright.

Change aspect ratio by making the canvas taller, never by padding a square with flat colour. If the design has any vignette or gradient reaching the edge, flat padding leaves a visible seam.

One trap specific to Playwright: screenshot({ animations: 'disabled' }) does not freeze anything — it fast-forwards animations to their end state, and every exported frame comes out identical. The timeline is already paused; do not pass it.

When the copy is the problem

If you are writing the words too and two rounds of alternatives all feel wrong, stop generating options. The words are usually fine — the slot has no defined job. Ask what that beat is for (name the problem? answer the objection? show proof?) and the line tends to write itself. A defensive beat dropped into the middle of a persuasive sequence will feel wrong no matter how well it is phrased.

Two related instincts: consistency between scenes beats cleverness in one of them — a scene that is twice as long as its neighbours reads as a mistake even when its content earns the time. And when someone says a section is "too slow", scale its whole sequence by a factor rather than trimming one hold; the internal rhythm is usually right.

References

Read the one you need, when you need it.

FileWhat is in it
references/engine.mdTimeline internals, the failure modes in depth, camera and shared tracks
references/craft.mdOptical centering, trails, easing, recurring elements, idle vocabulary
references/physics.mdWhy the solver in scripts/physics.js is built the way it is
references/verify.mdMeasurement recipes, and the traps in the measuring scripts
references/export.mdffmpeg flags, aspect ratios, platform requirements

Bundled files

The model reads these on demand while the skill is loaded. They are exposed as readable files and are never executed.

Frequently asked questions

What does the Motion Graphics AI skill do?

Build frame-exact motion graphics — animated logos, kinetic typography, brand videos, animated titles and social clips — as a single HTML file driven by a paused Web Animations timeline, then export them to MP4 in 1:1, 4:5 and 9:16. Use this whenever someone wants to animate a wordmark or logo, make a video from text, build a title sequence or lower third, produce a clip for LinkedIn / Instagram / Reels / TikTok, "turn this design into a video", animate a landing-page hero, or export a web animation to a video file — and also when an existing animation of this kind needs fixing, retiming, r...

Why use Motion Graphics on TypingMind?

Because you install it once and use it with any model. Motion Graphics is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Motion Graphics in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/acelera-agency/html-animation/tree/main. TypingMind reads its SKILL.md and bundles its files and installs it as a skill you can enable per chat.

Which AI models can use Motion Graphics?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Motion Graphics?

As many as you like. As long as a model supports skills, you can use Motion Graphics with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Motion Graphics AI skill free?

Yes. It is published on GitHub by acelera-agency under the MIT license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇