tachyonfx: shader-like effects for terminal user interfaces
Effects and animation library for Ratatui applications
At a glance
- What is it?
- tachyonfx is a Rust library for Ratatui that applies animated effects to already-rendered terminal cells, with 50 or more built-in effects, spatial patterns that control how an effect spreads, cell-level filters, parallel and sequential composition, and a string DSL. It compiles to WebAssembly and runs without the standard library, and the package description calls it a library for creating shader-like effects in TUIs.
- Who is it for?
- tachyonfx suits a developer building a Ratatui application that needs transitions, fades and motion rather than a static screen, and who wants the effects composable and filterable rather than hard-coded into the widget tree.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 47 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What it does: effects applied to the buffer after the widgets render
tachyonfx is an effects and animation library for Ratatui applications, and the description is precise about the mechanism. It builds complex animations by composing and layering simple effects, bringing smooth transitions and visual polish to the terminal. The basic concepts section states the model in three lines, and they are the whole mental model. Effects are stateful, so you create one and apply it every frame. Effects transform rendered content, and they are applied after the widgets render. And effects compose, so complex animations are built from simple pieces. The third point in the quick start is the implementation detail that matters most in practice, since the call is effects.process_effects(elapsed.into(), frame.buffer_mut(), screen_area), taking the elapsed time, a mutable handle on the frame's cell buffer and the area to work on. So this is a post-processing pass over rendered cells, not something that draws widgets itself.
use std::{io, time::Instant};
use ratatui::{crossterm::event, prelude::*, widgets::Paragraph};
use tachyonfx::{fx, EffectManager, Interpolation};
fn main() -> io::Result<()> {
let mut terminal = ratatui::init();
let mut effects: EffectManager<()> = EffectManager::default();
let fx = fx::fade_to(Color::Cyan, Color::Gray, (1_000, Interpolation::SineIn));
effects.add_effect(fx);
let mut last_frame = Instant::now();
loop {
let elapsed = last_frame.elapsed();
last_frame = Instant::now();
terminal.draw(|frame| {
let screen_area = frame.area();
let text = Paragraph::new("Hello, TachyonFX!").alignment(Alignment::Center);
frame.render_widget(text, screen_area);
effects.process_effects(elapsed.into(), frame.buffer_mut(), screen_area);
})?;
if event::poll(std::time::Duration::from_millis(16))? {
if let event::Event::Key(_) = event::read()? {
break;
}
}
}
ratatui::restore();
Ok(())
}The effect catalogue, grouped by what they do to the screen
The effect reference is the substance of the library, and it is grouped by category rather than alphabetically, which tells you how the effects relate to each other. The color effects transform colors over time for smooth transitions: fade_from and fade_to for color transitions, the foreground only variants, paint and its fg and bg forms to paint cells with a color over time, hsl_shift and hsl_shift_fg to animate through HSL color space, saturate to adjust saturation, and lighten and darken to push lightness toward white or black. The text and motion effects animate text and cell positions: coalesce and coalesce_from for text materialization, dissolve and dissolve_to for the reverse, evolve and its into and from forms for character evolution through symbol sets, slide_in and slide_out, sweep_in and sweep_out for color sweep transitions, explode for particle dispersion, expand for bidirectional expansion from the centre, and stretch for unidirectional stretching with block characters. The control effects are the ones that make a composition behave, and they are the ones worth learning first.
The control effects are where composition becomes predictable
The control effects fine-tune timing and behaviour, and they are the layer that turns a pile of animations into something you can reason about. parallel runs multiple effects simultaneously, and sequence chains effects one after another, so a fade then a pause then a sweep is a sequence rather than three overlapping effects. repeat and repeating loop effects with optional limits or indefinitely. ping_pong plays forward then reverse. delay and sleep add pauses before or during effects. prolong_start and prolong_end extend an effect's duration, freeze_at freezes an effect at a specific transition point, remap_alpha remaps effect progress to a smaller range, run_once ensures an effect runs exactly once, never_complete and timed_never_complete run indefinitely with an optional time limit, consume_tick is a minimal single-frame delay, and with_duration overrides the duration outright. The combination of parallel, sequence, repeat and never_complete is what makes a set of effects loop cleanly without the animation restarting visibly at the end of each cycle.
// Run multiple effects in parallel
let effects = fx::parallel(&[
fx::fade_from_fg(Color::Red, 500),
fx::sweep_in(Motion::LeftToRight, 10, 0, Color::Black, 800),
]);
// Or sequence them
let effects = fx::sequence(&[
fx::fade_from_fg(Color::Black, 300),
fx::coalesce(500),
]);Patterns decide how an effect spreads, and filters decide what it touches
Two mechanisms scope an effect, and they are separate. Patterns are spatial and control how the effect spreads over time and space, with radial, diagonal, checkerboard and organic patterns available, and the dissolve example shows a radial dissolve from the centre with a RadialPattern::center(), while the fade example attaches a DiagonalPattern from the top left to the bottom right with a transition width of 3.0, so the fade sweeps across the region rather than happening everywhere at once. Filters are cell-level and decide what the effect touches, using a CellFilter, so a fade can be set to apply to red text only with set_cell_filter(CellFilter::FgColor(Color::Red)). The feature list calls this cell-precise targeting, applying effects to specific regions or cells matching custom criteria, and it is the difference between an effect that is part of your design and one that is a blanket overlay on everything the frame contains.
// Radial dissolve from center
let effect = fx::dissolve(800)
.with_pattern(RadialPattern::center());
// Diagonal fade with transition width
let effect = fx::fade_to_fg(Color::Cyan, 1000)
.with_pattern(
DiagonalPattern::top_left_to_bottom_right()
.with_transition_width(3.0)
);The DSL, and a browser editor for iterating on effects
The DSL lets you create effects from strings at runtime, and the example is short, an EffectDsl with a compiler and a compile call on a string such as fx::dissolve(500), returning a Result so an invalid effect is a handled error rather than a panic. The DSL feature is optional and pulls in the anpa parser, and the documentation notes it mimics Rust syntax, which is the right ambition for a config format that people will type. The companion is TachyonFX FTL, a browser-based editor for creating and tweaking effects in real time using the DSL, and there is a link to try exabind in the browser to experience tachyonfx without installing anything. The point of an editor like that is that effect parameters are a search space, and tuning them by recompiling a Rust program is a slow loop, so the editor turns the tuning into a live process and the DSL is what lets the result be pasted back into the application.
use tachyonfx::dsl::EffectDsl;
let effect = EffectDsl::new()
.compiler()
.compile("fx::dissolve(500)")
.expect("valid effect");The packaging, the no_std path and the ratatui version it tracks
The Cargo manifest describes a library with two audiences, and the feature flags are the clearest sign. The default features are std and dsl, with std enabling the standard library variants of bon, compact_str, thiserror and ratatui-core, and dsl enabling the optional anpa dependency. There is also a web-time optional dependency, which is the WebAssembly and no_std story in one line, and the feature list states that it runs in browsers and embedded environments. The dependencies are small and deliberate, with ratatui-core at 0.1.2 with default features off, compact_str for small string storage, bon as a builder derive, micromath for the numeric work, thiserror for errors and unicode-width for the terminal character width problem. The full ratatui at 0.30.2 is a workspace dependency used by the tests and the examples, not by the library itself, which is a thoughtful separation. The package description is the one line that captures the whole project, a ratatui library for creating shader-like effects in TUIs.
The examples are a workspace, and the releases are dated
The examples are members of the Cargo workspace alongside the library, with examples/common, examples/minimal, examples/basic-effects, examples/effect-showcase, examples/tweens and examples/effect-registry, and each can be run with cargo run -p against its name. The effect-registry one is the most useful for evaluating the library, because an interactive registry is a browsable catalogue of the built-in effects rather than a script that plays them in sequence. The repository also has a benches/ directory with criterion as a dev dependency, so the effects are benchmarked, plus docs/ and docs-assets/ with build-docs.sh and serve-docs.sh scripts for the documentation site, a release-plz.toml and release.toml for the release process, and a rustfmt.toml. The releases are tagged with the crate name, tachyonfx-v0.25.2 on 2026-09-06, preceded by v0.25.1 in July and v0.25.0 in February, and the last push to the repository was on 2026-09-06 with the default branch being development rather than main.
cargo run -p basic-effects
cargo run -p minimal
cargo run -p effect-registry
cargo run -p effect-showcase
cargo run -p tweensEditorial conclusion
tachyonfx suits a developer building a Ratatui application that needs transitions, fades and motion rather than a static screen, and who wants the effects composable and filterable rather than hard-coded into the widget tree. It is the wrong choice if you need to animate content that has not been rendered yet, because the library operates on the buffer after the widgets have drawn, and a poor fit for a project that cannot take a dependency on a crate ecosystem where the ratatui major version matters, since the library tracks ratatui-core and the full ratatui is a workspace dependency used by the tests and examples. Before adopting it, look at the effect registry example, because that is where the built-in effects are browsed, and check the feature flags, since std and dsl are on by default and the no_std path is a deliberate choice rather than an accident.
Frequently asked questions
What does tachyonfx add to a Ratatui application?
It adds effects and animation, with 50 or more built-in effects covering color transformations, text animations and geometric distortions, plus support for custom effects. The effects transform rendered content, so they are applied after the widgets render, using process_effects with the elapsed time, the frame buffer and the screen area.
How do I combine several tachyonfx effects?
With fx::parallel to run effects simultaneously and fx::sequence to chain them one after another. The control effects extend this, with repeat and repeating to loop, ping_pong to play forward then reverse, delay and sleep to pause, with_duration to override a duration, never_complete to run indefinitely, and run_once to run exactly once.
What are patterns and cell filters in tachyonfx?
Patterns control how an effect spreads, with radial, diagonal, checkerboard and organic options, attached with with_pattern. Cell filters control what the effect touches, for example set_cell_filter(CellFilter::FgColor(Color::Red)) to apply an effect only to red text, which the documentation calls cell-precise targeting.
Can tachyonfx run without the standard library?
Yes. The feature list states WebAssembly and no_std support, and the Cargo features show std and dsl enabled by default with a separate no_std path, and an optional web-time dependency for the browser case. The library itself depends on ratatui-core rather than the full ratatui crate.
How do I create a tachyonfx effect from a string?
Use the Effect DSL, which is behind the optional dsl feature and pulls in the anpa parser. You call EffectDsl::new().compiler().compile("fx::dissolve(500)") and the call returns a Result, so an invalid effect is a handled error. The DSL mimics Rust syntax, and the browser-based TachyonFX FTL editor uses it to iterate on effects in real time.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/ratatui-tachyonfx)Was this useful?