Declarative video compilation engine — write timelines in .ktx text, compile to .mp4
Project description
KinetiX
The LaTeX of Video — a declarative video compilation engine.
Goodbye drag-and-drop, hello code-first.
No AI magic — pure logic-driven timelines.
Ditch the GUI, embrace hacker workflows.
Quick Start
Prerequisites: Python ≥ 3.10. ffmpeg (optional, for --live preview only).
pip install kinetix-video
Grab the demo.ktx and test assets, then:
kinetix demo.ktx # compile to demo.mp4
To hack on the source:
git clone git@github.com:Bruce-lhq/kinetix.git
cd kinetix
pip install -e .
CLI
kinetix <input.ktx> [output.mp4] [options]
Options:
--live Live preview via ffplay (no encoding)
--preview START-END Render a time slice, e.g. --preview 00:30-01:00
--graph Generate timeline topology PNG (no video render)
--no-subtitles Skip SRT subtitle rendering
VS Code Extension
Syntax highlighting + frame snapshot preview for .ktx files.
cp -r vscode-ktx ~/.vscode/extensions/ktx-syntax-0.2.0/
Then Cmd+Shift+P → Developer: Reload Window. Open any .ktx file:
| Feature | How |
|---|---|
| Syntax highlighting | Automatic on .ktx files |
| Snapshot preview | Ctrl+Shift+K → enter time → frame renders in side panel |
| Command palette | Cmd+Shift+P → KinetiX: Snapshot Frame at Time |
The snapshot command renders a single frame at any timecode (e.g. 5, 1:30, 00:05) as a full-resolution PNG for quick visual inspection.
.ktx Syntax
Asset Declarations
[v1]: clips/video.mp4 [tag1, tag2] # video with tags
[img]: clips/photo.jpg [overlay] # image
[bgm]: clips/music.mp3 # audio
[t1]: text("Line 1\nLine 2", font: songti, bg_opacity: 0.5)
Supported formats: .mp4 .mov .avi .mkv | .jpg .jpeg .png .bmp .gif | .mp3 .wav .aac .flac .m4a
Style Macros
Define Style("intro"):
fadein: 1s
layer: 0
transition: "crossfade", dur: 0.5s
filter: "blackwhite"
[v1] @ 00:00 | style: "intro" | mute: true
Style properties: fadein fadeout transition volume mute layer anchor filter speed. Entry values override style defaults.
Timeline
# Expression time — anchor to any named clip
[v2] @ v1.end - 1s | layer: 0
[a1] @ v1.start + 2s | layer: 0
# prev.end chains (audio/video tracked independently)
[v3] @ prev.end | layer: 0
# Split: same asset, different segments
[v_part1] @ 00:00 | trim_start: 0s | trim_end: 5s
[v_part2] @ prev.end | trim_start: 5s | trim_end: 10s
# Audio ducking roles
[voice] @ 00:00 | role: voice
[bgm] @ 00:00 | role: bgm | volume: -20
Timeline Properties
| Property | Value | Description |
|---|---|---|
duration |
10s |
Override asset duration |
layer |
0, 1, … |
Z-order (0 = bottom) |
pos |
(x, y) or (50vw, 30vh) |
Pixel or relative position |
anchor |
(0, 0) |
Relative anchor point |
style |
"intro" |
Reference a Style macro |
crop |
(x, y, w, h) |
Frame crop |
trim_start |
2s |
Start time trim |
trim_end |
8s |
End time trim |
filter |
"blackwhite" |
Global filter |
transition |
"crossfade" |
Entrance transition |
fadein / fadeout |
1s |
Fade in/out (video + audio) |
mute |
true |
Mute video track |
speed |
2 |
Playback speed (affects timeline) |
volume |
-28 |
Volume in dB |
role |
voice / bgm / sfx |
Audio role (for ducking) |
trim_start+durationcan be combined withouttrim_end: the clip will span[trim_start, trim_start + duration].
Position Units (CSS-like)
pos supports mixed units that auto-scale when you change output resolution:
| Unit | Meaning | Example |
|---|---|---|
vw |
% of canvas width | 50vw = half canvas width |
vh |
% of canvas height | 30vh = 30% canvas height |
pw |
% of asset width | 10pw = 10% of image/video width |
ph |
% of asset height | 5ph |
px / number |
Absolute pixels | 200px = 200px |
[img] @ 00:05 | pos: (50vw, 30vh) # relative
[img] @ 00:05 | pos: (100, 200) # absolute pixels
Anchor Coordinates
anchor: (x, y) # x, y ∈ [-1, 1], screen coords (y↓)
| Coord | Position | Coord | Position | Coord | Position |
|---|---|---|---|---|---|
(-1,-1) |
Top-left | (0,-1) |
Top-center | (1,-1) |
Top-right |
(-1, 0) |
Mid-left | (0, 0) |
Center | (1, 0) |
Mid-right |
(-1, 1) |
Bot-left | (0, 1) |
Bot-center | (1, 1) |
Bot-right |
Keyframe
scaleis applied before anchor.postakes priority overanchor.
Keyframes & Easing
[img1]:
scale: { 0s: 0.5, 5s: 1.2, curve: "ease_in_out" }
pos: { 0s: (0,0), 5s: (200,100) }
opacity:{ 0s: 0.0, 1s: 1.0, 4s: 1.0, 5s: 0.0 }
rotate: { 0s: 0, 5s: 90 }
Easing curves: linear (default) | ease_in | ease_out | ease_in_out
Filters
| Value | Effect |
|---|---|
blackwhite |
Grayscale |
invert |
Invert colors |
mirror_x |
Horizontal flip |
mirror_y |
Vertical flip |
painting |
Oil-painting effect |
Text Cards
[t]: text("Content", font: songti, size: 64, bg_opacity: 0.3, color: "#FFFFFF", stroke_width: 2, stroke_color: "#00000060")
Parameters can appear in any order. Defaults:
| Parameter | Default | Description |
|---|---|---|
font |
heiti |
songti / heiti |
size |
Auto | Font size in px |
bg_opacity |
0.0 |
0=transparent, 1=solid background |
color |
#FFFFFF |
Text color |
bg |
#000000 |
Background color |
stroke_width |
0 |
Text outline width in px |
stroke_color |
#000000 |
Stroke color (supports alpha: #00000060) |
Auto word-wrap with \n for hard line breaks. Text clips render at natural size (transparent RGBA), centered on canvas by default. Use opacity keyframes for true transparent fade-in/out.
Subtitles
Subtitles: subtitles.srt
Standard SRT format, rendered at the bottom of the frame.
Output Config
Format: mp4, Res: 1080p, FPS: 30
Res options: 480p 720p 1080p 4k
Complete Example
See the full demo.ktx in the repo — a multi-scene promotional clip with text overlays, easing keyframes, and live preview support. Run it with:
kinetix demo.ktx # compile to demo.mp4
kinetix demo.ktx --live # real-time preview via ffplay
kinetix demo.ktx --graph # generate timeline topology PNG
Minimal example showing all core features:
Define Style("cinematic"):
layer: 0
transition: "crossfade", dur: 2.4s
[v1]: test_assets/v_chaos.mp4
[v2]: test_assets/v_order.mp4
[logo]: test_assets/logo.png
[bgm]: test_assets/bgm_epic.mp3
[t1]: text("Goodbye drag-and-drop, hello code-first editing", font: songti, size: 64,
stroke_width: 2, stroke_color: "#00000060")
[bgm] @ 00:00 | volume: -10 | trim_start: 24s | duration: 16s | fadein: 1s | fadeout: 3s
[v1] @ 00:00 | style: "cinematic" | mute: true | duration: 7.5s
[t1] @ 1s | duration: 3s
[v2] @ v1.end - 2.4s | style: "cinematic" | mute: true | duration: 7.5s
[logo] @ v2.end - 2.5s | layer: 1 | duration: 5s
[v1]:
scale: { 0s: 1.0, 7.5s: 1.12, curve: "ease_in" }
[t1]:
scale: { 0s: 0.85, 3s: 1.0, curve: "ease_out" }
opacity: { 0s: 0.0, 0.6s: 1.0, 2.4s: 1.0, 3s: 0.0, curve: "ease_in_out" }
[logo]:
scale: { 0s: 0.22, 1.8s: 0.35, curve: "ease_out" }
rotate: { 0s: -18, 1.8s: 0, curve: "ease_out" }
opacity: { 0s: 0.0, 1.0s: 1.0, curve: "ease_out" }
Format: mp4, Res: 1080p, FPS: 30
Architecture
parse(.ktx) → apply_styles → resolve_timeline → render(.mp4) | live_preview(ffplay) | graph(.png)
Generate a timeline topology graph with kinetix demo.ktx --graph:
License
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 kinetix_video-0.2.0.tar.gz.
File metadata
- Download URL: kinetix_video-0.2.0.tar.gz
- Upload date:
- Size: 28.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
786dc39b289d442bc84be6dc1bf816e375290dd128302858f5ef2e04b1f49ab7
|
|
| MD5 |
26a0c78166138b9d6488655285945d77
|
|
| BLAKE2b-256 |
4dfbd668f8b6898c546187ff1690cae3df59e81b7e5ad2eeab05a4cddb0aedec
|
Provenance
The following attestation bundles were made for kinetix_video-0.2.0.tar.gz:
Publisher:
publish.yml on Bruce-lhq/kinetix
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
kinetix_video-0.2.0.tar.gz -
Subject digest:
786dc39b289d442bc84be6dc1bf816e375290dd128302858f5ef2e04b1f49ab7 - Sigstore transparency entry: 1502807362
- Sigstore integration time:
-
Permalink:
Bruce-lhq/kinetix@420e2ad8c1a105f9e85eec9c2ee046336e405604 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/Bruce-lhq
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@420e2ad8c1a105f9e85eec9c2ee046336e405604 -
Trigger Event:
push
-
Statement type:
File details
Details for the file kinetix_video-0.2.0-py3-none-any.whl.
File metadata
- Download URL: kinetix_video-0.2.0-py3-none-any.whl
- Upload date:
- Size: 28.7 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 |
4aab6144dce1814544d07669b4e0941bf52fda46e2708cc579b9d5a339fe37af
|
|
| MD5 |
ed12e092837532a097b8a06361891e02
|
|
| BLAKE2b-256 |
c6337f934ecc4a55ccb2d3535a48463ab6cf5a1541f95c9d4ea9c30fc163fa23
|
Provenance
The following attestation bundles were made for kinetix_video-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on Bruce-lhq/kinetix
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
kinetix_video-0.2.0-py3-none-any.whl -
Subject digest:
4aab6144dce1814544d07669b4e0941bf52fda46e2708cc579b9d5a339fe37af - Sigstore transparency entry: 1502807420
- Sigstore integration time:
-
Permalink:
Bruce-lhq/kinetix@420e2ad8c1a105f9e85eec9c2ee046336e405604 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/Bruce-lhq
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@420e2ad8c1a105f9e85eec9c2ee046336e405604 -
Trigger Event:
push
-
Statement type: