A Python-native, code-first video generation framework
Project description
PyMotion
Code-first video generation for Python.
Build professional motion graphics, animated explainers, social ads, and product videos — entirely in Python.
Why PyMotion?
Most video tools force you into a GUI timeline. PyMotion doesn't. You write Python, you get broadcast-quality video. No templates to fight, no drag-and-drop constraints — just code that renders frames.
It ships with a Cairo 2D backend, a ModernGL 3D pipeline, FreeType+HarfBuzz typography, a full audio DSP chain, and 15 FFmpeg export presets out of the box. One pip install, one comp.render() call, done.
from pymotion import Composition, ColorClip, TextClip, Track
comp = Composition(1920, 1080, fps=30, duration=150)
track = Track(name="main")
bg = ColorClip(color="#1a1a2e")
bg.set_duration(150)
track.add(bg)
title = TextClip("Hello, PyMotion!", font="Arial", size=72.0, color="#FFFFFF")
title.set_duration(150).set_position(480.0, 500.0)
track.add(title)
comp.add_track(track)
comp.render("output.mp4", preset="h264_1080p")
That's a full 1080p video in 12 lines.
Features
| 2D Rendering | Color, image, shape, gradient, and text clips — Cairo backend |
| 3D Rendering | PBR materials, point/spot/directional/ambient lights, SSAO, bloom, DOF (ModernGL headless) |
| Animation | Keyframe tracks, 30+ easing functions, spring physics, cubic bezier curves |
| Typography | FreeType + HarfBuzz shaping, variable fonts, 9 animated text presets (Typewriter, CountUp, Scramble, ...) |
| Audio | Mixing, EQ, compressor, limiter, reverb, delay, pitch shift, beat detection, waveform analysis |
| Effects | 30+ visual/color/distortion/light effects (blur, grain, glow, LUT, wave warp, god rays, ...) |
| Keying | ChromaKey, LumaKey, ColorKey, DifferenceKey — with feathering, choking, despill |
| Editing | Split, join, subclip, repeat, freeze frame, concatenate with transitions |
| Speed/Time | Uniform speed, speed ramp, reverse, time remap, optical flow slow-motion |
| Layout | Picture-in-picture, grid, split screen, stack — named anchor positioning |
| Tracking | Motion tracking, video stabilization, follow-tracker binding |
| Proxy | Low-res proxy generation with disk cache for fast preview |
| Transitions | 39 built-in (fade, slide, wipe, zoom, glitch, film burn, shatter, vortex, ...) |
| Particles | 9 presets — fire, sparkles, confetti, rain, smoke, stars, dust, explosion, bubbles |
| Export | 15 presets — H.264, H.265, ProRes, AV1, WebM, GIF, PNG/EXR frame sequences |
| Batch | Template system with field validation for data-driven video generation |
| Color | .cube LUT loading, lift/gamma/gain grading, ACES/Reinhard/Filmic tone mapping |
| CLI | render, preview, benchmark, validate, doctor, new |
Installation
Prerequisites: Python 3.12+, FFmpeg, Cairo
# macOS
brew install ffmpeg cairo pkg-config
# Ubuntu / Debian
sudo apt-get install ffmpeg libcairo2-dev pkg-config libfreetype6-dev
# Windows (via chocolatey)
choco install ffmpeg cairo
Install from PyPI:
pip install pymotion-studio
With extras:
pip install "pymotion-studio[3d-extras]" # GLTF model loading
pip install "pymotion-studio[gpu-compute]" # wgpu acceleration
pip install "pymotion-studio[jit]" # Numba JIT compilation
pip install "pymotion-studio[dev]" # Development tools
Note: The Python import name is
pymotion(no hyphen):from pymotion import Composition, ColorClip, Track
Quick Start
Animated Text with Particles
from pymotion import Composition, ColorClip, Track
from pymotion.text.animated import Typewriter
from pymotion.particle.system import sparkles
from pymotion.utils.color import Color
comp = Composition(1920, 1080, fps=30, duration=150)
# Background
bg_track = Track(name="bg")
bg = ColorClip(color="#0D1B2A")
bg.set_duration(150)
bg_track.add(bg)
# Typewriter text
text_track = Track(name="text")
tw = Typewriter(
text="Welcome to PyMotion",
font_size=64.0,
color=Color(1.0, 1.0, 1.0, 1.0),
chars_per_frame=1.5,
)
tw.set_duration(150).at(10)
text_track.add(tw)
# Sparkle particles
fx_track = Track(name="fx")
sparks = sparkles(1920, 1080).to_clip(150)
sparks.set_duration(150)
fx_track.add(sparks)
comp.add_track(bg_track)
comp.add_track(text_track)
comp.add_track(fx_track)
comp.render("intro.mp4", preset="h264_1080p")
Batch Rendering with Templates
from pymotion import Template, Composition, ColorClip, TextClip, Track
class ProductVideo(Template):
product_name: str
brand_color: str = "#FF5500"
def build(self) -> Composition:
comp = Composition(1920, 1080, fps=30, duration=90)
track = Track(name="main")
bg = ColorClip(color=self.brand_color)
bg.set_duration(90)
track.add(bg)
label = TextClip(self.product_name, font="Arial", size=80.0, color="#FFFFFF")
label.set_duration(90).set_position(600.0, 480.0)
track.add(label)
comp.add_track(track)
return comp
for name in ["Widget Pro", "Gadget X", "Tool Kit"]:
ProductVideo(product_name=name).render(f"{name.lower().replace(' ', '_')}.mp4")
Examples
Five production-ready scripts ship with the repo, each targeting a real-world use case:
| # | Script | Niche | What It Demonstrates |
|---|---|---|---|
| 01 | real_estate_tour.py |
Property listings | ImageClip slideshow, Typewriter text, sparkle particles, 7-track composition |
| 02 | tech_review_intro.py |
YouTube intros | CountUp stats, radial/conic gradients, fire particles |
| 03 | fitness_social_ad.py |
Instagram/TikTok | Vertical 1080x1920, CountDown timer, confetti, LetterByLetter |
| 04 | restaurant_menu_promo.py |
Menu promotions | WordByWord reveals, stars particles, ShapeClip polygons |
| 05 | educational_explainer.py |
E-learning | LetterByLetter titles, CountUp counters, diagram shapes |
python examples/download_assets.py # grab stock images (~5 MB)
python examples/01_real_estate_tour.py # render
CLI
pymotion render scene.py -o out.mp4 -p h264_1080p # render a composition
pymotion export-frame scene.py -f 30 -o thumb.png # export single frame
pymotion benchmark scene.py -n 100 # measure frame throughput
pymotion doctor # verify system dependencies
pymotion validate scene.py # check composition integrity
pymotion new my-project # scaffold a new project
Architecture
pymotion/
├── animation/ Keyframe tracks, 30+ easings, spring, bezier, interpolation
├── audio/ Mixer, DSP effects (EQ, compressor, reverb, ...), beat detection
├── clip/ ColorClip, ImageClip, ShapeClip, TextClip, VideoClip, Scene3DClip
├── effects/ Visual, color, distortion, light effect processors
├── export/ FFmpeg encoder, 15 output presets
├── particle/ Vectorized particle system, 9 preset generators
├── render/ Cairo 2D, ModernGL 3D, compositor, color pipeline
├── security/ Path traversal, color, asset magic-byte, text sanitization validators
├── template/ Template ABC with field validation for batch rendering
├── text/ FreeType/HarfBuzz renderer, 9 animated text presets
├── transition/ 39 transition implementations
├── utils/ Color (OKLCH), Vec2/Vec3, logging (structlog)
└── cli/ Click-based CLI (render, preview, benchmark, doctor, ...)
Internal frame format: BGRA uint8 NumPy arrays (H, W, 4) — matches Cairo ARGB32 on little-endian. The compositor uses bounding-box sparse blending with uint16 fixed-point fast paths for opaque layers and alpha-info caching for transparency detection.
Performance
Benchmarked on a typical 7-layer 1080p composition:
| Metric | Value |
|---|---|
| Frame render throughput | ~75 fps (13 ms/frame) |
| 12s video end-to-end | ~7s wall time (2.5x realtime) |
| Static layer caching | Single render, reused across frames |
| Particle simulation | Vectorized NumPy — no per-particle Python loops |
| FFmpeg encoding | Multi-threaded, contiguous frame pipe, zero-copy |
Development
git clone https://github.com/Ohswedd/pymotion.git
cd pymotion
pip install -e ".[dev]"
make lint # ruff format + ruff check + mypy --strict
make test # pytest with coverage (85% minimum)
make clean # remove caches and build artifacts
Running Tests
pytest -v # full suite (1285 tests)
pytest tests/unit/ -v # unit tests only
pytest tests/integration/ -v # integration tests
pytest --cov=pymotion --cov-report=html # coverage report
Docker
docker build -t pymotion .
docker run --rm -v $(pwd)/output:/app/output pymotion render scene.py -o output/video.mp4
System Dependencies
| Dependency | Purpose | Bundled? |
|---|---|---|
| FFmpeg | Video/audio encoding | No — install separately |
| Cairo | 2D vector rendering | No — install separately |
| FreeType | Font rasterization | Yes (via freetype-py) |
| HarfBuzz | Text shaping | Yes (via uharfbuzz) |
| ModernGL | 3D PBR rendering | Yes (via pip) |
Run pymotion doctor to verify your environment.
License
PyMotion is released under the PyMotion Source Available License 1.0.
What you can do:
- Use PyMotion in any project, including commercial products
- Fork the repo and modify the code for your own use
- Contribute back via pull requests
What you cannot do:
- Redistribute, rebrand, or republish PyMotion as a standalone library
- Sell, sublicense, or commercially exploit the library itself
- Publish modified versions to any package registry
Read the full LICENSE for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pymotion_studio-1.2.1.tar.gz.
File metadata
- Download URL: pymotion_studio-1.2.1.tar.gz
- Upload date:
- Size: 5.9 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e4530d3e8c3fbad170db58bac961ec234643f4f87faf406372dd278e64b47a12
|
|
| MD5 |
ff5477257a898ff7c73a9d69927f424e
|
|
| BLAKE2b-256 |
0f518b6b0ff2af2751479980dd1e85734aacf2730c200da0f5328e72d4382118
|
Provenance
The following attestation bundles were made for pymotion_studio-1.2.1.tar.gz:
Publisher:
publish.yml on Ohswedd/pymotion
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pymotion_studio-1.2.1.tar.gz -
Subject digest:
e4530d3e8c3fbad170db58bac961ec234643f4f87faf406372dd278e64b47a12 - Sigstore transparency entry: 1075798817
- Sigstore integration time:
-
Permalink:
Ohswedd/pymotion@f31ea52b7af4e6f62dd2553714b3a13280803694 -
Branch / Tag:
refs/tags/v1.2.1 - Owner: https://github.com/Ohswedd
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f31ea52b7af4e6f62dd2553714b3a13280803694 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pymotion_studio-1.2.1-py3-none-any.whl.
File metadata
- Download URL: pymotion_studio-1.2.1-py3-none-any.whl
- Upload date:
- Size: 155.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d3cb44f00614cb3b856924e800f501a3fa392e78679ba594c37e35e2ff21f9ef
|
|
| MD5 |
9b874dd76b4281e78198c412edfdb4fc
|
|
| BLAKE2b-256 |
441dcc8baf1d2cf3c92db42457079109c64e5685e9e0c7eaf5868702e4293761
|
Provenance
The following attestation bundles were made for pymotion_studio-1.2.1-py3-none-any.whl:
Publisher:
publish.yml on Ohswedd/pymotion
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pymotion_studio-1.2.1-py3-none-any.whl -
Subject digest:
d3cb44f00614cb3b856924e800f501a3fa392e78679ba594c37e35e2ff21f9ef - Sigstore transparency entry: 1075798848
- Sigstore integration time:
-
Permalink:
Ohswedd/pymotion@f31ea52b7af4e6f62dd2553714b3a13280803694 -
Branch / Tag:
refs/tags/v1.2.1 - Owner: https://github.com/Ohswedd
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f31ea52b7af4e6f62dd2553714b3a13280803694 -
Trigger Event:
push
-
Statement type: