ttfx README

The project's README from github.com/omacom/ttfx. As one markdown file: README.md.

ttfx

Terminal text effects as a single static binary. Pipe text in, pick an effect:

ls -la | ttfx decrypt
cat banner.txt | ttfx beams
fortune | ttfx --random-effect
git log --oneline -10 | ttfx matrix
the decrypt effect resolving the Omarchy logo

Credit where it's due

This is a port of TerminalTextEffects (TTE) by ChrisBuilds. Every effect, the animation engine, and the command-line interface are their design — this project translates that work to Rust and adds nothing of its own to the art. If you like what you see here, star the original.

TTE is MIT licensed and so is this port; the original copyright is preserved in LICENSE and NOTICE. Please file effect ideas upstream, where they belong.

Why a port

TTE is a Python package. That's the right call for a library, but for a shell toy that lives in your prompt pipeline it means an interpreter, an install step, and ~65 ms of import before the first frame. ttfx is one dependency-free binary that starts in under a millisecond.

That difference is the whole reason this exists, and it compounds: once running, ttfx renders every effect hundreds of times faster than Python TTE. Time to render each whole animation at 200×50 cells (46 lines of 190 characters), pacing disabled so this measures throughput rather than sleep():

Effect Frames Python TTE ttfx Faster
beams 754 7,193 ms 8.2 ms 876×
binarypath 2,003 19,489 ms 57.3 ms 340×
blackhole 1,814 11,714 ms 39.8 ms 294×
bouncyballs 9,169 8,665 ms 19.7 ms 439×
bubbles 12,207 12,991 ms 25.6 ms 506×
burn 3,178 8,276 ms 11.1 ms 746×
colorshift 528 6,614 ms 13.7 ms 482×
crumble 1,958 8,343 ms 34.3 ms 243×
decrypt 5,506 10,134 ms 11.7 ms 868×
errorcorrect 5,252 7,232 ms 11.5 ms 626×
expand 314 3,651 ms 11.9 ms 306×
fireworks 1,503 16,502 ms 42.4 ms 389×
highlight 129 1,418 ms 2.5 ms 559×
laseretch 14,490 18,051 ms 26.4 ms 683×
matrix ¹ 2,315 — — —
middleout 245 2,549 ms 7.1 ms 360×
orbittingvolley 1,169 1,967 ms 10.6 ms 186×
overflow 313 2,723 ms 9.9 ms 276×
pour 7,252 6,783 ms 12.0 ms 567×
print 10,065 8,020 ms 5.8 ms 1,387×
rain 4,853 5,166 ms 13.4 ms 384×
randomsequence 207 1,223 ms 2.8 ms 443×
rings 1,580 13,003 ms 73.8 ms 176×
scattered 438 4,055 ms 15.1 ms 269×
slice 400 2,703 ms 5.8 ms 464×
slide 375 2,643 ms 8.3 ms 317×
smoke 645 4,016 ms 5.8 ms 694×
spotlights 832 9,848 ms 27.7 ms 355×
spray 718 3,364 ms 19.4 ms 174×
swarm 5,242 16,675 ms 69.8 ms 239×
sweep 220 1,663 ms 3.3 ms 508×
synthgrid 687 2,602 ms 5.2 ms 497×
thunderstorm ¹ 1,083 — — —
unstable 552 5,014 ms 22.4 ms 223×
vhstape 727 6,872 ms 17.8 ms 387×
waves 635 10,656 ms 11.7 ms 914×
wipe 138 1,346 ms 2.4 ms 552×

ttfx is 423× faster than Python TTE (geometric mean over the 35 effects that run to completion; median 439×, range 174×–1,387×). Starting up to draw a single character takes 0.8 ms against 66 ms.

¹ matrix and thunderstorm run for a fixed wall-clock duration, so Python and ttfx finish at the same moment; what ttfx buys there is a far higher frame rate inside that window.

Measured on an AMD Ryzen 9 9955HX with ttfx 0.5.0 against TerminalTextEffects 0.15.0 on CPython 3.14.7, both pinned to two cores, output to /dev/null: ttfx best of five runs, Python best of two. Reproduce it with tools/fx/speed.py --python (see tools/fx/speed.py --help for installing Python TTE).

For energy rather than speed, python3 tools/tests/bench_energy.py [effect ...] reports the joules one paced run costs, read from the CPU's RAPL counters (Linux, needs root to read them).

The effects

All 37, each animating the Omarchy logo. Every frame below came out of the Rust binary — and is byte-identical to what the Python original produces from the same input and seed.

beams
beams
Create beams which travel over the canvas illuminating the characters behind them
binarypath
binarypath
Binary representations of each character move towards the home coordinate of the character
blackhole
blackhole
Characters are consumed by a black hole and explode outwards
bouncyballs
bouncyballs
Characters are bouncy balls falling from the top of the canvas
bubbles
bubbles
Characters are formed into bubbles that float down and pop
burn
burn
Burns vertically in the canvas
colorshift
colorshift
Display a gradient that shifts colors across the terminal
crumble
crumble
Characters lose color and crumble into dust, vacuumed up, and reformed
decrypt
decrypt
Display a movie style decryption effect
errorcorrect
errorcorrect
Some characters start in the wrong position and are corrected in sequence
expand
expand
Expands the text from a single point
fireworks
fireworks
Characters launch and explode like fireworks and fall into place
highlight
highlight
Run a specular highlight across the text
laseretch
laseretch
A laser etches characters onto the terminal
matrix
matrix
Matrix digital rain effect
middleout
middleout
Text expands in a single row or column in the middle of the canvas then out
orbittingvolley
orbittingvolley
Four launchers orbit the canvas firing volleys of characters inward to build the input text from the center out
overflow
overflow
Input text overflows and scrolls the terminal in a random order until eventually appearing ordered
pour
pour
Pours the characters into position from the given direction
print
print
Lines are printed one at a time following a print head. Print head performs line feed, carriage return
rain
rain
Rain characters from the top of the canvas
randomsequence
randomsequence
Prints the input data in a random sequence
rings
rings
Characters are dispersed and form into spinning rings
scattered
scattered
Text is scattered across the canvas and moves into position
slice
slice
Slices the input in half and slides it into place from opposite directions
slide
slide
Slide characters into view from outside the terminal
smoke
smoke
Smoke floods the canvas colorizing any characters it crosses
spotlights
spotlights
Spotlights search the text area, illuminating characters, before converging in the center and expanding
spray
spray
Draws the characters spawning at varying rates from a single point
swarm
swarm
Characters are grouped into swarms and move around the terminal before settling into position
sweep
sweep
Sweep across the canvas to reveal uncolored text, reverse sweep to color the text
synthgrid
synthgrid
Create a grid which fills with characters dissolving into the final text
thunderstorm
thunderstorm
Create a thunderstorm in the terminal
unstable
unstable
Spawn characters jumbled, explode them to the edge of the canvas, then reassemble them in the correct layout
vhstape
vhstape
Lines of characters glitch left and right and lose detail like an old VHS tape
waves
waves
Waves travel across the terminal leaving behind the characters
wipe
wipe
Wipes the text across the terminal to reveal characters

Every effect takes its own options — ttfx <effect> --help. A few of the GIFs above shorten a timed phase so the loop stays watchable (matrix --rain-time 3, thunderstorm --storm-time 3, vhstape --total-glitch-time 250, spotlights --search-duration 80, errorcorrect --error-pairs 0.5); everything else is stock.

Fidelity

This is a parity port, not a reimplementation-in-spirit. Given the same input, config, and random draws, ttfx produces byte-identical frames to the Python original — verified mechanically in CI against a pinned upstream checkout (v0.15.0), not by eyeballing.

Suite Checks What it proves
tools/parity/run_suite.sh 354 every effect's frame stream, byte for byte, across configs and seeds
tools/parity/tty_compare.sh 41 the full terminal byte stream — canvas prep, cursor moves, teardown
tools/tests/cli_corpus.sh 19 exit codes and stdout/stderr routing
tools/tests/*_behavior.py pty what only a real terminal shows: resize restarts, signal teardown
cargo test goldens + traces easing/geometry/gradient values and engine state machines

./bin/test runs the lot, which is all CI does.

Making that possible meant reproducing upstream's quirks deliberately, not "fixing" them: Python's banker's rounding, gradients built from integer floor division rather than float interpolation, a bezier arc-length approximation that drops its final segment, and looping scenes that report themselves complete on every tick. They're catalogued in plan.md; the places where Python's unordered iteration had to be pinned down are in docs/ordering-inventory.md.

Two deliberate differences. Random number generation is not bit-compatible with CPython — ttfx uses xoshiro256++, so --seed is reproducible within ttfx but won't match Python's Mersenne Twister. (The parity harness swaps a shared PRNG into both sides, which is what makes frame comparison possible at all.) And Python plugin effects aren't supported, since there's no interpreter to load them.

Usage

<producer> | ttfx [terminal options] <effect> [effect options]

ttfx --help                 # all 37 effects and the terminal options
ttfx <effect> --help        # options for one effect
ttfx --random-effect        # surprise me (--include-effects / --exclude-effects to filter)
ttfx --print-completion bash|zsh

Terminal options (canvas size and anchoring, color handling, frame rate, text wrapping) go before the effect name; effect options after it. Option names and defaults match tte, so existing invocations work with the binary name swapped.

Building

cargo build --release
cargo build --release --target x86_64-unknown-linux-musl   # static, ~3.3 MB

./bin/test runs every suite. It needs python3, and the parity half needs a copy of upstream, which it clones at the pinned commit on first run:

./tools/parity/fetch_reference.sh   # what bin/test calls; safe to run by hand

Upstream is not vendored here — the harness fetches it, because it's their code.

Scope

Linux and macOS. Built for Omarchy originally; nothing targets a specific libc, and CI runs the tests and CLI corpus on both platforms. The byte-exact parity suites stay pinned to Linux/glibc — Apple's libm rounds a few transcendentals a last-ulp differently, which quantization hides in real frames but a bit-exact comparison would surface.

License

MIT — see LICENSE, which carries both this project's copyright and the original TerminalTextEffects copyright, and NOTICE for the attribution in full.

ttfx (omacom/ttfx)

Searching Database…

349 ·GitHub
这是什么?ttfx 是一个单一的静态 Rust 二进制,用于渲染终端文本动画效果——你把文本通过管道送入,再从 37 种效果库(beams、matrix、fireworks 等)中选择一种。它是 Python 项目 TerminalTextEffects(TTE)逐行移植的 Rust 版本。
这是独立的 CLI 工具,还是依赖原始的 Python TTE?它是一个完全独立的 CLI 二进制,零运行时依赖——不会调用 Python 或原始包。它是一个对等移植:与上游 TTE 相同的 CLI、相同的选项、逐字节一致的输出,只是换成了编译后的实现。
它怎么运行?把文本通过管道或文件送入二进制并指定一个效果,选项语法与原始 tte 命令完全一致(例如把现有 tte 调用里的二进制名换掉即可)。运行 ttfx --help 可以查看所有效果及其选项。
使用它需要安装 Python 吗?不需要——编译好的二进制本身没有依赖,也不需要 Python。只有当你从源码构建并运行完整的测试/一致性套件时才需要 Python3,该套件会克隆上游的 Python TTE 仓库来对比输出。
它能在 Windows 上运行吗?不能,只支持 Linux 和 macOS,没有声明 Windows 目标。另外,由于没有可加载它们的 Python 解释器,原始 TTE 的 Python 插件效果也不受支持。
它是专门为 Omarchy 桌面构建的,还是可以作为通用终端工具使用?它最初是为 Omarchy 做的工具,但并不绑定它——代码中没有任何针对特定 libc 的内容,CI 也会一般性地测试 Linux 和 macOS。
ttfx 比原始的 Python TTE 快多少?平均快约 423 倍(35 种效果的几何平均,中位数 439 倍),因效果不同而从 174 倍到 1,387 倍不等。仅启动时间就从约 66ms 降到约 0.8ms。
Rust 移植版的行为和原版完全一致吗?一致——在相同输入、配置和随机种子下,它生成的帧与 Python TTE 逐字节相同,这一致性在 CI 中对着固定版本的上游检出来机械化验证,而不是靠肉眼比较。
它包含多少种效果?共 37 种效果,每一种都用同一个 logo 演示动画,涵盖烟花、matrix 雨、黑洞、VHS 故障等。
为什么不直接用 Python 版本,而要构建这个?TTE 是 Python 包,每次运行都要付出约 65ms 的解释器启动成本,第一帧还没渲染就已经消耗掉了。ttfx 完全消除了这一开销,适合作为轻量的 shell 管道工具使用。
What are similar projects?spotify-tui, ripgrep, xsv, edit, oha, TurboBench, hyperfine, dust
Creator
omacom
Size
7 MB
Stars
349
Age
2mo
Commits · 90 Days