120 named text overlay effects for video. The text-effects engine of the Trollfabriken stack.
Project description
text-fx
120 named text overlay effects for Python video pipelines.
What it is
text-fx exposes 120 named text overlay effects through 25 hand-written base engines with parametric variants. It was built at Trollfabriken AITrix AB to give programmatic text overlays the visual vocabulary of consumer video editors — needed for AIMOS Insight municipal report video subtitles, civic-education explainer captions, and CineForge pipeline trailer titles.
The design follows the same principle as cut-fx's named-transition catalog: consumer editors
expose hundreds of named text effects that are, mechanically, the same handful of base engines
parameterized differently. text-fx publishes that catalog mapping (effects_catalog.json,
effect_mapping.json) alongside a stable API that caption-cast and title-fx build on.
The library does not manage subtitle timing, lyric synchronization, or beat-matching — that is
caption-cast's responsibility. text-fx renders one text overlay at a time: text in, animated
transparent WebM out, or text composited directly onto a base video.
What it solves
| Pain point | Resolution |
|---|---|
| Named effects from consumer editors are not reproducible in Python | 120 slugs, each resolved to a deterministic (engine, params) pair from a shipped JSON catalog |
| Every render library has a different text layout API | Single TextEffectConfig Pydantic model covers font, position, color, easing, and animation in one place |
| LLM output needs to select text effects by name | text_effect_schema() returns a JSON Schema with the full 120-slug enum; parse_llm_response() validates and coerces the result |
| Downstream packages need transparent overlay frames, not a re-encoded video | render_overlay() returns a transparent WebM with alpha channel; sibling packages composite it without re-rendering the text |
Installation
pip install text-fx
Requires ffmpeg >= 6.0 on PATH. Install it with your OS package manager.
Extras:
pip install "text-fx[overlay]" # HTML/CSS rendering for light_neon effects (~220 MB)
pip install "text-fx[fonts]" # system font discovery + variable font support
pip install "text-fx[all]" # overlay + fonts (~250 MB)
The overlay extra installs web-overlay and Playwright Chromium. On headless servers run
playwright install chromium after pip install. See docs/RUNBOOK.md.
Quick start
Apply a named effect to a video:
from text_fx import apply_text_effect
apply_text_effect(
video="background.mp4",
text="HELLO WORLD",
effect="neon_glow_text",
output="titled.mp4",
)
Override defaults via TextEffectConfig:
from text_fx import apply_text_effect
from text_fx.config import TextEffectConfig
config = TextEffectConfig(
text="CHAPTER 1",
effect="kinetic_pop",
start_time=1.5,
duration=2.0,
font_family="Inter",
font_size=120,
color="#FFFFFF",
easing="ease-out-back",
intensity=1.2,
position=("center", "bottom"),
margin_y=80,
)
apply_text_effect(video="clip.mp4", config=config, output="out.mp4")
Render a transparent overlay (no video input):
from text_fx import render_overlay
render_overlay(
text="HELLO",
effect="neon_glow_text",
width=1920,
height=1080,
duration=2.0,
fps=30,
output="hello_neon.webm",
)
# Produces a WebM with alpha channel — composite it onto any video downstream
The catalog
120 named effects across 6 categories, each resolved to (base engine, parameter set) via
effects_catalog.json and effect_mapping.json — both ship inside the wheel.
| Category | Named effects | Base engines | Engines |
|---|---|---|---|
basic_editing |
18 | 3 | alpha, typewriter, cascade |
kinetic_typography |
25 | 5 | slide, spring, bounce, spin, oscillate |
mask_reveal |
22 | 4 | linear_wipe, radial, shaped, organic |
glitch_digital |
20 | 4 | rgb_shift, slice_corrupt, decode, scanline |
distortion_warp |
20 | 5 | sine_warp, radial_warp, displacement, motion_blur, particle |
light_neon |
15 | 4 | glow, sweep, shimmer, sparkle |
| Total | 120 | 25 |
Left Wipe, Right Wipe, Top Wipe, Bottom Wipe are four named effects backed by one linear_wipe
engine with different direction parameters. That pattern — multiple catalog entries, single base
engine — is how the catalog reaches 120 from 25 engines.
Catalog inspection:
from text_fx import list_effects, list_categories, get_effect_info
names = list_effects() # all 120 slugs
names = list_effects(category="mask_reveal") # filtered
cats = list_categories() # 6 names
info = get_effect_info("neon_glow_text")
# {
# "name": "Neon Glow Text", "slug": "neon_glow_text",
# "categories": ["light_neon"], "base_engine": "glow",
# "params": {"color": "#FF00FF", "blur_radius": 24, "intensity": 1.5},
# "duration_default": 2.0
# }
Render engines
Engine selection is automatic based on the effect's catalog entry. prefer_engine on
TextEffectConfig overrides it.
Pillow (default) — handles all 25 base engines. Text renders to an RGBA image; the engine applies per-frame transforms (alpha curves, translations, rotations, mask animations). Frames are assembled by ffmpeg into a transparent WebM.
OpenCV — used automatically for displacement-map effects: sine_warp, radial_warp,
displacement, motion_blur, particle. OpenCV's remap and filter2D handle pixel
displacement more efficiently than Pillow for large frames.
web_overlay (optional) — some light_neon effects (gold_shine, chrome_shine,
holographic_text, bokeh_text) and glitch_digital effects (cyberpunk_neon_glitch,
hud_text) look meaningfully better rendered as HTML/CSS animations through Chromium. If
text-fx[overlay] is not installed, these effects fall back to a PIL approximation with a
one-time UserWarning.
Render pipeline (three passes):
- Typography pass —
typography/renderer.pyrenders text to a high-quality RGBA Pillow Image. - Animation pass — the engine applies per-frame transformations to produce RGBA frames.
- Composite pass —
compositor.pyoverlays frames onto the base video via ffmpegfilter_complexwith theoverlayfilter. Audio passes through unchanged.
Typography
The library ships no fonts to keep the wheel slim. Resolution order:
- Explicit path —
font_family="/path/to/Inter-Bold.ttf"loads that file directly. - System fonts — if
text-fx[fonts]is installed, fonttools discovers the named font on the host OS. - Fallback — PIL's built-in bitmap font. A warning is logged. The render still proceeds, but the output is not suitable for production.
Variable fonts are supported when fonttools >= 4.43 is installed; font_weight maps to the
wght axis automatically. Multi-line text uses \n in the text field.
LLM integration
from text_fx.schema import text_effect_schema, parse_llm_response
schema = text_effect_schema()
# JSON Schema dict; the "effect" field's enum contains all 120 slugs
# Pass as a tool definition or structured-output spec to any LLM
config = parse_llm_response(llm_text)
# Returns a validated TextEffectConfig, or raises pydantic.ValidationError
Composition with other Trollfabriken packages
text-fx is the leaf library. caption-cast and title-fx depend on it; it depends on neither.
caption-cast calls render_overlay() for each timed caption and composites the resulting
transparent WebMs onto the base video. It adds lyric sync and beat alignment on top.
title-fx uses apply_text_effect() and render_overlay() for its 44 cinematic title recipes,
optionally composing with cut-fx for cut-triggered title transitions.
CLI
# Apply one effect
text-fx apply --video bg.mp4 --text "HELLO" --effect neon_glow_text --out titled.mp4
# Catalog inspection
text-fx list # all 120 slugs, one per line
text-fx list --category light_neon # filtered
text-fx categories # 6 category names, one per line
text-fx info neon_glow_text # JSON metadata
# Engine availability
text-fx engines
# LLM schema
text-fx schema --pretty
# Render transparent overlay (no input video)
text-fx render --text "HELLO" --effect neon_glow_text \
--width 1920 --height 1080 --duration 2 --out overlay.webm
# Multi-effect sequence from YAML
text-fx sequence --config sequence.yaml --video bg.mp4 --out out.mp4
# Preview over a solid background
text-fx preview --effect neon_glow_text --text "Sample" --out preview.mp4
Package structure
text-fx/
├── pyproject.toml
├── LICENSE
└── src/text_fx/
├── __init__.py # apply_text_effect, render_overlay, list_effects, …
├── api.py
├── config.py # TextEffectConfig
├── resolver.py # slug → (engine, params)
├── compositor.py # ffmpeg overlay compositing
├── cli.py
├── exceptions.py
├── catalog/
│ ├── loader.py
│ └── mapping.py
├── data/
│ ├── effects_catalog.json # ships in wheel
│ └── effect_mapping.json # ships in wheel
├── typography/
│ ├── fonts.py # font loading, system discovery
│ ├── renderer.py # PIL text → RGBA
│ └── layout.py # multi-line, word/char splitting
├── easing/
│ └── curves.py # linear, cubic, back, elastic, spring, bounce
├── engines/
│ ├── base.py # Engine ABC
│ ├── alpha/ # basic_editing
│ ├── kinetic/ # kinetic_typography
│ ├── mask/ # mask_reveal
│ ├── glitch/ # glitch_digital
│ ├── distortion/ # distortion_warp
│ └── neon/ # light_neon
└── schema/
├── generator.py # text_effect_schema()
└── parser.py # parse_llm_response()
License
MIT. See LICENSE.
Author: Trollfabriken AITrix AB — https://github.com/tomastimelock
GitHub: https://github.com/tomastimelock/text-fx | PyPI: https://pypi.org/project/text-fx/ | Issues: https://github.com/tomastimelock/text-fx/issues
Sibling packages: caption-cast — title-fx — cut-fx
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 text_fx_engine-0.1.0.tar.gz.
File metadata
- Download URL: text_fx_engine-0.1.0.tar.gz
- Upload date:
- Size: 100.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ce17b11b895c6d7622b2187b3c0b6dd092f356e7ccf717829070c223941abe72
|
|
| MD5 |
5180a2417f36f47e141a2d6a7fa7b6f1
|
|
| BLAKE2b-256 |
6ce11f8efe3cc420eb54a9a36bb82a765514c74c3cd2be6ae5c3ee047ac0041d
|
Provenance
The following attestation bundles were made for text_fx_engine-0.1.0.tar.gz:
Publisher:
release.yml on tomastimelock/text-fx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
text_fx_engine-0.1.0.tar.gz -
Subject digest:
ce17b11b895c6d7622b2187b3c0b6dd092f356e7ccf717829070c223941abe72 - Sigstore transparency entry: 1616132764
- Sigstore integration time:
-
Permalink:
tomastimelock/text-fx@4f7c6255acba63046a8998ebe7110fb34e4e86b2 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/tomastimelock
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4f7c6255acba63046a8998ebe7110fb34e4e86b2 -
Trigger Event:
push
-
Statement type:
File details
Details for the file text_fx_engine-0.1.0-py3-none-any.whl.
File metadata
- Download URL: text_fx_engine-0.1.0-py3-none-any.whl
- Upload date:
- Size: 120.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
876e65b4c6e10c28e7331f39254faee6e22956d5f47be521991d892641694977
|
|
| MD5 |
31c39330f9c927836f0983389fd499a8
|
|
| BLAKE2b-256 |
d8bc6cbf0fd0fa2fbfe7e209ab070efc9431ef32ed9167185fb368511c7aec7a
|
Provenance
The following attestation bundles were made for text_fx_engine-0.1.0-py3-none-any.whl:
Publisher:
release.yml on tomastimelock/text-fx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
text_fx_engine-0.1.0-py3-none-any.whl -
Subject digest:
876e65b4c6e10c28e7331f39254faee6e22956d5f47be521991d892641694977 - Sigstore transparency entry: 1616132780
- Sigstore integration time:
-
Permalink:
tomastimelock/text-fx@4f7c6255acba63046a8998ebe7110fb34e4e86b2 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/tomastimelock
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4f7c6255acba63046a8998ebe7110fb34e4e86b2 -
Trigger Event:
push
-
Statement type: