Open source · 0BSD

Motion FX Lab

Code-made motion graphics → MP4 with sound.

Write the animation as a web page, yourself or with an AI coding assistant, and render it frame by frame to a checked MP4 with sound. Browse the use cases, demo films and effects below, then adapt one.

Use cases

Start from the video you need

Each use case is one editable page: change its words, numbers or brand near the top of the file and render a new MP4. Other ratios and brands come from the same page. Click a preview or “Play with sound” for the full film; “Scrub timeline” opens the page with a scrub bar.

This weekend only12 s · 9:16

One data file, three shop promos, safe for every app’s buttons.

Use for: Reels, Shorts and TikTok promos for a shop or venue.

The tea bar, bakery and yoga studio versions come from one data block; product drawings are code.

Also rendered: bakery, 9:16 · yoga studio, 9:16

Explore: 3-second headline hook · Word-synced captions · Beat sync · Light sweep · Sticker labels · Red flash + shake + stamp · Whip-in letters + motion blur · Counting number · Countdown ring

Feature launch in 15 seconds15 s · 16:9

One new feature, from problem to result, in 15 seconds.

Use for: Feature launches, release notes and app-store previews.

The same page renders 16:9 and a 1:1 cut; every word stays at least 28 px at 1080p.

Also rendered: 1:1

Explore: Counting number · Sticker labels · Camera move over real UI · Light sweep · Typewriter · Highlighter · Scan and check · Frosted glass card · Red flash + shake + stamp

Year in numbers14.4 s · 16:9

Your figures, each page ending on its point.

Use for: Annual reviews, reports and data explainers.

Replace the data block and the counters, charts and closing sentences follow; a 9:16 cut uses the same data.

Also rendered: 9:16

Explore: Whip-in letters + motion blur · Staggered letter pop · Highlighter · Counting number · Countdown ring · Stroke reveal · Sticker labels

Background loops in place12 s · 16:9

Seamless loops behind a title, an interview and a “starting soon” screen.

Use for: Title backgrounds, interview backdrops and stream holding screens.

Each loop is checked for a seamless join between its last and first frame; the film shows a loop caption in place of the check.

Explore: Soft colour fields · Staggered letter pop · Noise ridgelines · Whip-in letters + motion blur

Titles & logo stings18 s · 16:9

Type a name, pick a preset, render a 6-second opener.

Use for: Channel intros, podcast openers and logo stings.

All three presets play in 18 s; ?preset= exports one alone, in 16:9 or 9:16.

Also rendered: 9:16

Explore: Solid lettering · Light through a stencil · Particle flow into type · Organic closed contours · Signal breakup

Event countdown15 s · 16:9

Change the name and details, get a beat-locked countdown and logo reveal.

Use for: Launch, livestream and event countdowns.

The countdown, chips and logo come from one data block; a 9:16 cut uses the same page.

Also rendered: 9:16

Explore: Counting number · Countdown ring · Beat sync · Whip-in letters + motion blur · Red flash + shake + stamp · Particle flow into type · Chrome monogram · Light through a stencil · Sticker labels · Film grain

Product spotlight10 s · 4:5

One product, three ratios: drop, callouts, colourways, price.

Use for: Shop listings, product ads and social posts.

Renders 4:5, 9:16 and 1:1 from the same page; text never covers the product.

Also rendered: 9:16 · 1:1

Explore: Light sweep · Staggered letter pop · Frosted glass card · 3D card flip grid · Red flash + shake + stamp · Sticker labels · Film grain

Clear15 s · 16:9

A drop becomes a crown splash, rises into a bottle that refracts the brand, dives underwater and surfaces on the end card.

Use for: Product adverts and material-led openers.

Four water effects each carry one chapter; the brand is set in one place and a 9:16 cut uses the same page.

Also rendered: 9:16

Explore: Drop, crown and rebound · 3D shape blending · Water and glass · Below the surface

Unique effects

Effects to reuse

Each card is one distinct effect, drawn live in your browser from code and ordered by visual design craft and motion feel. Type custom copy below, choose a variant in a card’s selector, or filter by kind and renderer; where an effect has its own full render, the card links to it. How effects are grouped.

Kind
Drawn with

Live scene and treatment presets · expand to preview

Reusable multi-treatment scenes behind the demo films: one object in five shader styles, twenty art directions, the gallery journey, the underwater scene and the eight-era room. They reuse effects above rather than adding new ones.

Rendering notes

Browser effects run from code; the Blender effects replay baked frames and keep their editable source recipes. The colour-grade technique uses CSS filters and a separate glow; it is not a LUT implementation. The original 3D and shader tiles share one WebGL canvas; the three optical shaders share a second context. Reaction–diffusion draws the same pixels every time on one GPU, but not the same pattern on every GPU.

Make your own

Write it as a web page. Render it to MP4.

  1. Set up. Clone the repository, then run npm install, npx playwright install chromium and npm run setup:hooks. Rendering also needs Python 3 and ffmpeg; see the quick start.
  2. Edit. Copy a use case or demo film page and change its words, data, timeline or effects. Preview it with node video/serve.cjs 8000 and scrub to any frame: each frame is a function of time.
  3. Render. One command renders, checks and delivers the MP4:
bash video/build.sh examples/product.html

The build renders every frame twice in independent browsers and compares them, masters the sound to −14 LUFS, and writes an MP4, a high-quality copy and a poster; a failed check keeps the previous files. Page contract and engine reference.

For developers: the stack, a render benchmark and authoring choices

How the films are built

The library combines drawing APIs, renderers, simulation libraries and export tools. Original effects use three.js r128; the newer 3D and compute modules pin r180.

StackWhat it isWhyWhat it does here

A recorded rendering benchmark

Measured on a 56-second, 1,693-frame test video with heavy 3D, which is not included in this repository. Timed from launching the browsers to a finished lossless master. The first pipeline, default headless Chromium with one browser and page.screenshot, took 399.6 s. Rendering the whole video twice and comparing every frame took 52.9 s: 7.5× faster than the first pipeline. Before that, with only an 8-frame spot check, it took 26.4 s. Encoding both deliverables with every check took another 33.6 s. All measured on 2026-09-28 on an Apple M4 laptop.

Past 4 browsers there was no gain: the bottleneck moved to the single ffmpeg process that decoded the PNGs and wrote the master. The table describes that benchmark, not the current intro file.

StepFirst pipelineVerified recorder in that testHow it is proven
3D renderingSwiftShader (GPU emulated on the CPU)ANGLE Metal (the Apple M4 GPU)The browser's WebGL renderer string is read at start; an unexpected SwiftShader fallback stops a GPU run
Screenshotspage.screenshotCDP captureScreenshot with optimizeForSpeedAt 8 fast-moving moments the decoded pixels matched the old method 8/8, with no stale frame
Parallelism1 browser4 independent browsers, frames dealt round-robinThe whole video is rendered a second time with every frame on another browser, and all frames are compared; a frame fails if over 0.01% of plane samples differ by more than 8 levels, or over 20 samples differ by more than 48 levels
Masterx264 CRF 17 (lossy)x264 losslessDeliverables are compressed once; the master must hold every frame
LookMean SSIM 0.989 between old and new; the 71 frames under 0.95 are dense particle and dust frames that look identical side by sideffmpeg ssim, frame by frame
Sound−21.2 LUFS · −2.1 dBTP · AAC 160 kbps−14.1 LUFS · −1.6 dBTP · AAC 127 kbpsLimit, then two-pass loudnorm that must report “linear”; the file is measured with ebur128. 160 kbps was over Meta's 128 kbps ceiling
High-quality filenone24.9 MB · 3.5 Mbps mean · 17.2 Mbps peak secondMeta Reels allows 25 Mbps and 300 MB; moov first, no edit list
Web preview13.8 MB13.9 MB · two-pass 1870 kbpsOver 15 MB, it retries once at a lower rate, then stops

What the checks caught during development

  • A writer-loop bug: too few frames, stop.
  • Re-rendered frames differed: GPU edge noise on rotated text. Pixel tolerance handles small GPU edge differences; it does not judge the composition or motion quality. Later, a spot check of 8 frames missed a browser that drew a label wrongly, so every frame is now rendered twice. The label had kept a stale size after a scale animation; engine.js now prevents that.
  • A decoder deadlock: the frame comparison, the frame counts and every delivery step now have a time limit, so a hang there fails loudly.
  • A bad argument: refused before starting.

Left out, and why

  • HEVC: the high-quality file has no size pressure and H.264 is enough; H.264 is used for broad browser compatibility.
  • GPU benchmarks were measured on macOS. WebGL-only films can use --cpu; films with WebGPU compute require a compatible GPU adapter.

Tools and authoring choices

Current rendering choices

  • Full three.js migration: the original effects keep r128; newer film modules pin r180 without migrating every existing shader.
  • Timelines: each frame is calculated directly from time. These examples use the local timeline engine rather than a GSAP runtime.
  • HyperFrames: another HTML-to-video framework; the current films use this repository’s existing recorder.

Other workflows and assets

  • Remotion: videos use React components; free eligibility and Company License terms depend on the user or organization.
  • lygia: its default Prosperity licence covers noncommercial use and a 30-day commercial trial; it is not bundled here.
  • Generated speech: the dialogue films use offline Kokoro voices and Rhubarb mouth analysis. Character geometry, performance, scenery and music are authored in code; no generated footage or depth maps are used.