The tachyonfx Effect DSL (Domain Specific Language) provides a text-based way to create, combine, and manipulate terminal effects. It mirrors regular Rust syntax while focusing specifically on effect creation and manipulation.
Key principle: Valid tachyonfx Effect DSL code is valid Rust code with the appropriate imports. This makes the DSL immediately familiar and enables flexible development workflows.
Note that the DSL is enabled by the "dsl" feature. It is part of the default feature set and depends on "std", as
such it does not work in no_std environments.
The tachyonfx Effect DSL serves several use cases:
- Runtime Configuration: Define effects in config files that can be loaded, parsed, and applied without recompilation
- Live Reloading: Update effects while your application is running
- Serialization: Convert effects to/from string representations for storage or transmission
- Rapid Prototyping: Experiment with different effect combinations through text editing
- User Customization: Allow end-users to define their own effects without modifying your codebase
use tachyonfx::dsl::EffectDsl;
// Create a new DSL compiler with all standard effects registered
let dsl = EffectDsl::new();
// Compile a simple dissolve effect
let effect = dsl.compiler()
.compile("fx::dissolve(500)")
.expect("Valid effect");Bind external variables for use in DSL expressions:
use ratatui::style::Color;
use tachyonfx::{dsl::EffectDsl, Motion};
let effect = EffectDsl::new()
.compiler()
.bind("motion", Motion::LeftToRight)
.bind("color", Color::Blue)
.compile("fx::sweep_in(motion, 10, 0, color, 500)")
.expect("Valid effect");Use let bindings within expressions:
let effect = dsl.compiler().compile(r#"
let color = Color::from_u32(0xff5500);
let timer = (500, CircOut);
fx::fade_to_fg(color, timer)
"#).expect("Valid effect");Effects support method chaining for configuration:
let effect = dsl.compiler().compile(r#"
fx::dissolve(1000)
.with_filter(CellFilter::Text)
.with_area(Rect::new(10, 10, 20, 5))
.with_color_space(ColorSpace::Hsv)
"#).expect("Valid effect");Combine effects with fx::sequence() and fx::parallel():
use tachyonfx::dsl::EffectDsl;
let dsl = EffectDsl::new();
let effect = dsl.compiler().compile(r#"
fx::sequence(&[
fx::dissolve(300),
fx::fade_to_fg(Color::Red, 500),
fx::fade_to_fg(Color::Blue, 500)
])
"#).expect("Valid effect");The DSL provides access to all standard tachyonfx effects:
- Dissolve/Coalesce:
fx::dissolve(),fx::coalesce(),fx::dissolve_to(),fx::coalesce_from() - Slide/Sweep:
fx::slide_in(),fx::slide_out(),fx::sweep_in(),fx::sweep_out() - Explosion:
fx::explode() - Stretch/Expand:
fx::stretch(),fx::expand() - Evolution:
fx::evolve(),fx::evolve_from(),fx::evolve_into()- character transformations - Translation:
fx::translate()
- Fading:
fx::fade_to(),fx::fade_to_fg(),fx::fade_from(),fx::fade_from_fg() - Painting:
fx::paint(),fx::paint_fg(),fx::paint_bg() - HSL Manipulation:
fx::hsl_shift(),fx::hsl_shift_fg() - Saturation:
fx::saturate(),fx::saturate_fg() - Lightness:
fx::lighten(),fx::lighten_fg(),fx::darken(),fx::darken_fg()
- Repetition:
fx::repeat(),fx::repeating(),fx::ping_pong() - Duration Control:
fx::never_complete(),fx::timed_never_complete(),fx::with_duration() - Delays:
fx::delay(),fx::sleep(),fx::consume_tick() - Advanced Control:
fx::freeze_at(),fx::remap_alpha(),fx::run_once() - Prolonging:
fx::prolong_start(),fx::prolong_end()
Most effects can be manipulated with spatial patterns using .with_pattern():
- RadialPattern:
RadialPattern::center(),RadialPattern::new(x, y) - DiamondPattern:
DiamondPattern::center(),DiamondPattern::new(x, y)— Manhattan distance-based diamond reveals - SpiralPattern:
SpiralPattern::center(),SpiralPattern::new(x, y)— spiral arm reveals with configurable arm count - DiagonalPattern:
DiagonalPattern::top_left_to_bottom_right(), etc. - CheckerboardPattern:
CheckerboardPattern::default(),CheckerboardPattern::with_cell_size() - SweepPattern:
SweepPattern::left_to_right(),SweepPattern::right_to_left(), etc. - Organic Patterns:
CoalescePattern::new(),DissolvePattern::new() - WavePattern:
WavePattern::new(wave_layer)— complex wave interference patterns with FM/AM modulation - CombinedPattern:
CombinedPattern::multiply(a, b),CombinedPattern::max(a, b),CombinedPattern::min(a, b),CombinedPattern::average(a, b)— combine two patterns with binary operations - BlendPattern:
BlendPattern::new(a, b)— crossfade between two patterns over effect lifetime - InvertedPattern:
InvertedPattern::new(pattern)— inverts a pattern's output
Patterns support method chaining:
RadialPattern::center()
.with_transition_width(2.5)
.with_center(0.3, 0.7)
SpiralPattern::center()
.with_arms(6)
.with_transition_width(1.5)WavePattern is built from composable wave layers and oscillators:
- Oscillator:
Oscillator::sin(kx, ky, kt),Oscillator::cos(kx, ky, kt),Oscillator::triangle(kx, ky, kt),Oscillator::sawtooth(kx, ky, kt)- Methods:
.phase(f32),.modulated_by(Modulator)
- Methods:
- Modulator:
Modulator::sin(kx, ky, kt),Modulator::cos(kx, ky, kt),Modulator::triangle(kx, ky, kt),Modulator::sawtooth(kx, ky, kt)- Methods:
.phase(f32),.intensity(f32),.on_phase(),.on_amplitude()
- Methods:
- WaveLayer:
WaveLayer::new(oscillator)- Methods:
.multiply(oscillator),.average(oscillator),.max(oscillator),.amplitude(f32),.power(i32),.abs()
- Methods:
WavePattern::new(
WaveLayer::new(Oscillator::sin(2.0, 0.0, 1.0))
.multiply(Oscillator::cos(0.0, 3.0, 0.5).modulated_by(
Modulator::sin(1.0, 1.0, 0.25).intensity(0.5)
))
.amplitude(0.8)
).with_contrast(2)The DSL supports all types needed for effect creation:
- Numbers:
u8,u16,u32,i32,f32,bool - Strings:
String - Colors:
Color::Red,Color::from_u32(0xff5500),Color::Rgb(255, 0, 0),Color::Indexed(16) - Duration:
Duration::from_millis(500),Duration::from_secs_f32(0.5), or bareu32for milliseconds
- EffectTimer:
EffectTimer::from_ms(500, Linear),EffectTimer::new(duration, interpolation), or tuple shorthand(500, Linear) - Motion:
Motion::LeftToRight,Motion::RightToLeft,Motion::UpToDown,Motion::DownToUp - Interpolation: All interpolation curves (
Linear,QuadOut,BounceIn,SmoothStep,Spring, etc.) - RepeatMode:
RepeatMode::Forever,RepeatMode::Times(3),RepeatMode::Duration(duration) - ColorSpace:
ColorSpace::Rgb,ColorSpace::Hsl,ColorSpace::Hsv - EvolveSymbolSet:
EvolveSymbolSet::BlocksHorizontal,EvolveSymbolSet::BlocksVertical,EvolveSymbolSet::CircleFill,EvolveSymbolSet::Circles,EvolveSymbolSet::Quadrants,EvolveSymbolSet::Shaded,EvolveSymbolSet::Squares - SimpleRng:
SimpleRng::new(seed),SimpleRng::default()— for pattern randomization
- Rect:
Rect::new(x, y, width, height)or struct syntaxRect { x: 0, y: 0, width: 10, height: 20 } - Layout:
Layout::horizontal([...]),Layout::vertical([...]),Layout::new(direction, constraints) - Constraint:
Constraint::Min(10),Constraint::Max(100),Constraint::Length(50),Constraint::Percentage(25),Constraint::Fill(1),Constraint::Ratio(1, 3) - Flex:
Flex::Legacy,Flex::Start,Flex::End,Flex::Center,Flex::SpaceBetween,Flex::SpaceAround,Flex::SpaceEvenly - Other:
Margin,Offset,Size,RefRect,Direction
Complete CellFilter support including:
- Basic:
CellFilter::All,CellFilter::Text,CellFilter::NonEmpty,CellFilter::FgColor(color),CellFilter::BgColor(color) - Spatial:
CellFilter::Area(rect),CellFilter::RefArea(ref_rect),CellFilter::Inner(margin),CellFilter::Outer(margin) - Compound:
CellFilter::AllOf([...]),CellFilter::AnyOf([...]),CellFilter::NoneOf([...]),CellFilter::Not(box) - Advanced:
CellFilter::Layout(layout, index),CellFilter::Static(box), function-based filters
- Style:
Style::new(),Style::default()with method chaining - Modifier: All modifier variants (
Modifier::BOLD,Modifier::ITALIC, etc.)
- Arrays/Vectors:
[1.0, 2.0, 3.0],vec![effect1, effect2],&[constraint1, constraint2] - Options:
Some(value),None - 2-sized Tuples:
(1000, QuadOut),(0.5, 0.7) - Boxed:
Box::new(value)
The DSL provides conveniences for readable code:
- Optional
fx::Prefix: Effect functions can be used withoutfx::prefix - Unqualified Enum Variants:
CellFilter::Textcan be written as justText - Timer Shorthand:
(500, Linear)instead ofEffectTimer::from_ms(500, Linear)
DSL compilation returns DslParseError on failure, providing detailed location information:
use tachyonfx::dsl::EffectDsl;
let result = EffectDsl::new().compiler().compile("fx::invalid_effect(500)");
match result {
Ok(effect) => { /* use effect */ }
Err(parse_error) => {
eprintln!("Error at line {}:{}: {}",
parse_error.start_line(),
parse_error.start_column(),
parse_error.source
);
eprintln!("{}", parse_error.context()); // Shows code with error highlighted
}
}DslParseError provides:
- Line/column location:
start_line(),start_column(),end_line(),end_column() - Error context:
context()- surrounding code with error highlighted - Error text:
error_text()- the specific problematic text - Underlying cause:
sourcefield contains the detailedDslError
use tachyonfx::fx;
use ratatui::style::Color;
let effect = fx::sequence(&[
fx::fade_from(Color::Black, Color::Reset, 500),
fx::dissolve(300)
]);
// Convert to DSL string
let expression = effect.to_dsl().expect("Valid DSL expression");
println!("{}", expression);let effect = dsl.compiler()
.compile("fx::sequence(&[fx::fade_from(Color::Black, Color::Reset, 500), fx::dissolve(300)])")
.expect("Valid effect");Register custom effects by providing a compiler function:
use tachyonfx::dsl::{EffectDsl, Arguments, DslError};
use tachyonfx::{fx, Effect};
use ratatui::style::Color;
let dsl = EffectDsl::new()
.register("color_pulse", | args: &mut Arguments| {
let color = args.color()?;
let duration = args.read_u32()?;
Ok(fx::sequence(&[
fx::fade_from_fg(color, duration / 2),
fx::fade_to_fg(color, duration / 2)
]))
});
// Use the custom effect
let effect = dsl.compiler().compile(r#"
fx::color_pulse(Color::Blue, 1000)
"#).expect("Valid effect");Some effects are intentionally excluded due to complexity or runtime requirements:
- Function-based:
fx::effect_fn,fx::effect_fn_buf(require closures) - Buffer-based:
fx::translate_buf,fx::offscreen_buffer(requireRefCount<Buffer>) - Geometry:
fx::resize_area(deprecated) - Advanced:
fx::dynamic_area,fx::dispatch_event(require shared references or channels)
- No Mutable Variables: Only immutable bindings are supported
- Limited Functions: Primarily supports method calls and object construction
- No Control Flow: No if/else, match, or loop constructs
- Comments: Supported but not preserved during serialization
- Runtime Dependencies: Effects requiring closures, buffers, or channels are not available
The DSL focuses on declarative effect creation using basic data types, ensuring simplicity and broad compatibility.