Skip to content

Khizerhussain02/motion-code

Repository files navigation

Motion Code

The video is a program, not a picture.

Motion Code turns a one-line brief into an editable, on-brand promo video, rendered entirely as code (an HTML document with a GSAP timeline), driven by a single clock, and exported to a byte-identical MP4. Because the video is code, you can click any element and change it, or ask a model to rewrite just that element, and fixing one word never re-renders the whole minute.

▶ Live case study (interactive) · Open the app · LinkedIn

video-as-code · gsap · typescript · deterministic-rendering · next.js


The one-line version: most AI video tools generate pixels you cannot edit. Motion Code generates an editable program, drives it from one clock, and draws a hard line between what the model decides and what code guarantees. That line is the product.

Why video-as-code

An LLM is, at its core, a code engine. Pixel generation fights that: it is expensive, non-deterministic, and the output is a flat frame you cannot touch. So Motion Code does the opposite. Every scene is a self-contained HTML document with a GSAP timeline. The code on the left literally is the video on the right. Nothing is streamed.

That single reframe changes what an edit is. With pixels, an edit means re-render. Here, an edit is a text change to a live program: click an element and change its text, color or size, drag it, or say "make this calmer" and have the model rewrite only that element. None of that is possible with pixels.

The keystone: exactly one clock

This is the idea the whole architecture leans on. GSAP automatically aggregates every tween created anywhere on the page into a single globalTimeline. Motion Code makes that one object the source of truth for everything that needs a sense of time.

flowchart TB
    G(("globalTimeline<br/>the one clock<br/>paused for render"))
    G --- P["Preview<br/>pauses and resumes it"]
    G --- E["Editor / drag<br/>freezes it so the<br/>animation cannot fight your cursor"]
    G --- C["Captions<br/>their tweens auto-join it<br/>the caption code never calls the renderer"]
    G --- R["Renderer<br/>registers it, pauses it,<br/>and drives time by seeking"]
Loading

And here is what the one clock buys: determinism, for free. The renderer pauses the timeline and drives time by seeking to each frame. Nothing runs on Date.now() or requestAnimationFrame. Seek to t = 2.0s, screenshot, repeat. The same scene renders byte-for-byte identically on any machine. A stochastic, model-generated artifact becomes a deterministic function of time.

The whole pipeline

From a line of intent to a deterministic MP4. Each stage sees the whole story: scenes are generated with the full ordered script in context, like an artist who reads the script before drawing a frame.

flowchart LR
    A["brief<br/>one line"] --> B["split<br/>ordered scenes"]
    B --> C["generate<br/>scene = code"]
    C --> D["edit<br/>click, or ask the model"]
    D --> E["voice + captions"]
    E --> F["render<br/>seek then screenshot"]
    F --> G[("MP4")]
Loading

Architecture, the parts that were hard

1. Editing a document that is rewriting itself

The scene's timeline overwrites each element's transform sixty times a second. So when you drag an element, you cannot write to transform: the next frame stomps it. The fix is to move elements through channels the animation never touches, and let the browser compose them:

Channel Owned by Purpose
transform the animation the scene's own motion, untouched
left / top (layout) your drag move the layer
scale / rotate (individual CSS props) your resize size and turn it, live

SVG elements get a dedicated <g> wrapper carrying the offset as data-attributes. The result is the Figma editing model, rebuilt on an adversarial substrate: the float never stops while you edit, because your edit and the animation write to orthogonal channels. The AI-edit path is just as disciplined: a hand-written balanced-tag scanner finds the exact character span of the selected element and replaces only that span, so the GSAP <script> that drives everything stays byte-for-byte intact. Regenerating would be the easy path; it would also sever the animation wiring, so it is refused, by both a parser and a prompt.

2. The line between what the model decides and what code guarantees

The single most important lesson in the codebase is a boundary, found the hard way and written into a comment:

"A mechanical per-item timing job belongs in a pure, unit-tested function, not a prompt."

Models are unreliable at "apply this to every one of N items." So that work was moved across the boundary into deterministic code. The pattern recurs:

The model is unreliable at... ...so a pure function guarantees it
Per-word caption timing across N words Character timing to words to lines, unit-tested, one shared source for preview and export so they cannot drift
Leaving no element accidentally static An orphan-revival pass grafts ambient motion onto any element the model forgot to animate, no regeneration needed
Avoiding render-breaking infinite loops Every "forever" loop is mechanically rewritten to a large-but-finite one before render

The model brings the judgment (what the scene should look like, which style to call). Code brings the guarantees (timing, determinism, safety). Knowing exactly where that line sits, from experiment rather than theory, is most of what makes this reliable.

3. Determinism, earned the hard way

Frame-seeking a browser sounds simple until the scene refuses to hold still. AI scenes love "forever" loops for ambient motion. They come in two forms, one of which is a genuine trap:

Loop form The trap
GSAP repeat:-1 On the global clock, so pausing the clock stops it. Rewritten to a finite repeat anyway, so a seek never lands on an infinite tween.
Native SVG SMIL repeatCount="indefinite" Pausing the GSAP clock does NOT stop it. SMIL runs on its own native clock, so the page never settles and the render hangs. Confirmed empirically: every scene with this pattern hung.

The counter-measure is belt-and-suspenders: the craft prompt teaches the model to avoid these, and the renderer mechanically rewrites both forms to finite loops regardless. You defend a determinism boundary at the layer where the mistake is made and at the layer where it would bite.

4. Style is a document, not a model

Every look (clean, bold, cinematic, neon, hand-drawn, cartoon, and more) is a skill.md: a page of expert direction on palette, line weight, lighting and motion personality, injected into generation. A new style is not a retrain, it is a new file. Hand Motion Code a reference image or a link, and it reads that into a fresh skill.md that drives generation exactly like the built-in styles. Your brand becomes a skill, and every future video inherits it.

Because the craft skill does the quality work, the model's own hidden thinking budget is set to zero. That roughly halves the cost at full quality: move the intelligence into a curated, versioned prompt asset, and stop paying the model to re-derive it.

5. Crash-consistency, on a laptop

The render pipeline runs on one machine, but it is engineered like a small distributed system, because the failure modes are the same:

What goes wrong What the system does
Laptop dies mid-render A content-hashed cache lives beside the script, never in temp. Already-rendered scenes are reused on restart. Publish is atomic: render to a temp file, then same-directory rename, so a crash cannot leave a half-written clip.
A render logic change ships CACHE_VERSION bumps and invalidates every entry at once, so a code change can never silently reuse a stale clip.
One scene hangs forever A timeout race: the render is raced against a timer, because a cancellation signal alone cannot force a stuck await to return. After one retry, a simple placeholder card renders for that scene only (never cached), so one bad scene cannot sink a 52-scene render.
An interrupted render orphans a headless Chrome A reaper garbage-collects leftover processes on startup and before each export, gated on live-PID probes, so it can never kill a render that is actually running.

What a minute costs

Photoreal models bill by the second, and you pay the whole thing again on every re-render. Motion Code produces the same minute for about ₹70, a little over ₹1 a second, because it draws code instead of sampling pixels. An edit is a text change, not a new render, so fixing one word never buys the minute twice.

Tool Per second Per minute
Motion Code ₹1.2 ₹70
Kling (cheapest photoreal) ₹6.7 ₹399
Sora 2 (720p) ₹9.5 ₹570
Veo 3 (quality tier) ₹38 ₹2,280

Roughly 8x cheaper than Sora, up to 33x under Veo. Providers' own per-second rates, verified Jul 2026.

What it can and cannot do

Cheap and editable comes from one constraint: everything is drawn in code. That is also the honest limit.

✓ Draws it in code ✕ Out of scope, by design
UI, product and dashboard mockups Photoreal human faces
Diagrams, flows and state machines Live-action or real camera footage
Kinetic type and data-viz Literal photography of a place or product
Hand-drawn, isometric, cinematic, neon looks Hyper-real 3D and heavy VFX

If a brief needs a real camera shot, those photoreal tools are the right call. Motion Code is for editable, on-brand motion.

Repository layout

promo-studio/     the Next.js app: prompt → generate → click-to-edit → export
render-worker/    the deterministic renderer (local seek-and-screenshot → MP4)
design/           the design system and theme explorations
audit/            architecture audits (editing model, daemon, render, infra)
tracker/          roadmap + build tracker
docs/             this project's public case-study site (GitHub Pages)

Where the honesty is

Good architecture means naming its edges:

  • Export is deliberately local. The cloud render worker was built and measured. It was not faster at 2 vCPU and it lost the durable cache, so rendering stays on the machine, on purpose. A measured choice, not a missing feature.
  • Captions "for free" is an ElevenLabs property, not a universal one. ElevenLabs returns character-level timing with the audio, so captions need no transcription. The Google voice path does transcribe: it runs speech-to-text on its own synthesized audio, then rebuilds a character grid to match ElevenLabs' shape, so the caption code downstream never knows the difference. Same interface, a round-trip behind it.

Read the full story

The interactive case study runs real Motion Code scenes live in your browser: the code-is-the-video panel, a draggable element that keeps animating while you edit it, the one-clock diagram, and the full style showcase. It is the best way to see the architecture actually move.


Built by Khizer Hussain. Motion Code is a working product; the app is live at getmotioncode.vercel.app.

About

Video as code: one GSAP clock drives preview, editing, captions, and byte-identical MP4 render. The video is a program, not a picture.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors