Skip to Content
ftui-extrasVisual FX catalog

Visual FX catalog

The ftui-extras visual_fx module ships a collection of effects that render at sub-character resolution via the VFX rasterizer. Each effect is a plain struct you construct, tick, and render into a Frame. All of them respect the FxQuality degradation tier and consume only ThemeInputs.

This page documents every effect that has a concrete implementation in the tree. Items marked with {/* TODO */} are referenced in design docs but not yet in source.

All effects are exported from visual_fx/effects/mod.rs.

How to view effects

Most demo harnesses check the FTUI_HARNESS_VIEW environment variable to switch screens. To see the visual FX showcase:

FTUI_HARNESS_VIEW=visual_effects cargo run -p ftui-demo-showcase

The concrete screen (Metaballs, Plasma, etc.) is usually selected by a second env var or a keybinding inside the showcase. See screen catalog for the full set of FTUI_HARNESS_VIEW values.

Metaballs

What it draws. Smooth blobs that merge as they approach each other — the classic iso-surface of a sum of inverse-squared fields.

Key equation. Each ball ii at position pip_i with radius rir_i contributes a field:

fi(x,y)=ri2(x,y)pi2f_i(x, y) = \frac{r_i^2}{\lVert (x, y) - p_i \rVert^2}

The rendered iso-surface is the set where ifi(x,y)τ\sum_i f_i(x, y) \ge \tau for some threshold τ\tau.

Source: effects/metaballs.rs (55 KB).

Public API: MetaballsFx, MetaballsParams, MetaballsPalette, Metaball.

How to view: FTUI_HARNESS_VIEW=visual_effects then pick the metaballs screen.

Plasma

What it draws. Animated interference patterns — coloured waves that blend, reminiscent of 1990s demoscene intros.

Key equation. The standard plasma wave is a sum of sinusoids:

plasma(x,y,t)=sinx+cosy+sin(xy)+sint14\mathrm{plasma}(x, y, t) = \bigl\lvert \sin x + \cos y + \sin(xy) + \sin t \bigr\rvert \cdot \tfrac{1}{4}

This is mapped through a palette to produce per-cell RGB. A low-quality variant (plasma_wave_low) uses one or two sine ops only; the full variant mixes four or more frequencies.

Source: effects/plasma.rs (126 KB).

Public API: PlasmaFx, PlasmaPalette, plasma_wave, plasma_wave_low.

How to view: FTUI_HARNESS_VIEW=visual_effects then pick the plasma screen.

Doom Fire

What it draws. Animated fire rising from the bottom of the viewport — the classic PSX Doom fire effect.

Key equation. A cellular automaton on a heat grid. Each frame, for each cell (x,y)(x, y):

h(x,y)=max(0,  h(x,y+1)r(x,y))h'(x, y) = \max\bigl(0,\; h(x, y+1) - r(x, y)\bigr)

where hh is heat, hh' is the next frame’s heat, and r(x,y){0,1,2}r(x, y) \in \{0, 1, 2\} is a small random decay plus horizontal drift. Heat is mapped to a red → yellow → white palette.

Source: effects/doom_fire.rs (24 KB).

Public API: DoomFireFx.

Feature flag: always available (no gate), since the effect is tiny.

How to view: FTUI_HARNESS_VIEW=visual_effects and select doom fire.

Screen Melt

What it draws. The “screen dripping downward like melting wax” effect used as a transition in classic DOOM between levels.

Key equation. Each column has an offset dcd_c that increases monotonically with randomised speed. Displayed pixel (c,r)(c, r) reads from source pixel (c,rdc)(c, r - d_c) until dcd_c exceeds the viewport height.

dc(t+1)=dc(t)+vc,vc{0,1,2}d_c(t+1) = d_c(t) + v_c, \quad v_c \in \{0, 1, 2\}

Source: effects/screen_melt.rs (32 KB).

Public API: ScreenMeltFx.

How to view: FTUI_HARNESS_VIEW=visual_effects and trigger the melt transition.

Underwater Warp

What it draws. A wavy distortion of the scene, as if viewed through moving water.

Key equation. A displacement field samples source pixels with an offset given by two orthogonal sine waves:

Δx(x,y,t)=Asin(kxy+ωt)\Delta x(x, y, t) = A \sin(k_x y + \omega t) Δy(x,y,t)=Acos(kyx+ωt+ϕ)\Delta y(x, y, t) = A \cos(k_y x + \omega t + \phi)

The final pixel is read from (x+Δx,  y+Δy)(x + \Delta x,\; y + \Delta y).

Source: effects/underwater_warp.rs (35 KB).

Public API: UnderwaterWarpFx.

How to view: FTUI_HARNESS_VIEW=visual_effects and select the warp screen.

Doom Melt (feature-gated)

What it draws. A combined Doom Fire + Screen Melt: the fire rises while the old frame melts away.

Source: effects/doom_melt/.

Public API: DoomMeltFx.

Feature flag: doom (implies visual-fx).

How to view: build with --features doom then FTUI_HARNESS_VIEW=visual_effects.

Quake Console (feature-gated)

What it draws. A sliding drop-down console overlay styled after Quake’s dev console. Not strictly a visual effect in the field-math sense — it is a scripted animation of a pane entering from the top.

Key behaviour. Linear interpolation of height from 0 to target with configurable easing:

h(t)=target_heightease ⁣(tT)h(t) = \mathrm{target\_height} \cdot \mathrm{ease}\!\left(\frac{t}{T}\right)

Source: effects/quake_console/.

Public API: QuakeConsoleFx.

Feature flag: quake (implies visual-fx).

How to view: build with --features quake then FTUI_HARNESS_VIEW=visual_effects.

Samplers and field-building blocks

Not effects in themselves, but the primitives every effect uses:

Source: effects/sampling.rs.

ItemPurpose
Sampler traitCompute a field value at normalised (x,y)(x, y)
FnSamplerWrap a closure as a sampler
MetaballFieldSamplerSum of inverse-square contributions from multiple balls
PlasmaSamplerWraps plasma_wave as a sampler
BallStatePosition / velocity physics for metaballs
CoordCacheCache normalised coordinates for repeated sampling
cell_to_normalizedMap cell coords to [1,1][-1, 1]
fill_normalized_coordsIterate over cells emitting normalised coordinates

You can build your own effect by providing a new Sampler implementation and a canvas adapter.

Canvas adapters

Source: effects/canvas_adapters.rs.

AdapterModeFor
MetaballsCanvasAdapterBraille / half-block (auto)MetaballsFx
PlasmaCanvasAdapterHalf-blockPlasmaFx

Both are feature-gated behind canvas.

Additional effects referenced in design docs

These effects appear in the research notes but have no concrete implementation yet. Treat them as planned, not shipped.

EffectStatus
Gray–Scott reaction–diffusion{/* TODO: not in source */}
Clifford attractor{/* TODO: possibly a sampler variant */}
Mandelbrot / Julia sets with deep zoom{/* TODO: no dedicated module yet */}
Lissajous / Harmonograph{/* TODO: planned */}
Flow field (curl / Perlin){/* TODO: can be built on FnSampler */}
Wave interference (slit diffraction){/* TODO: close to Plasma */}
Spiral galaxy (N-body){/* TODO: planned */}
Spin lattice (LLG dynamics){/* TODO: planned */}

The scaffolding in sampling.rs is general enough to add most of these as Sampler implementations plus a canvas adapter.

Common pitfalls

  • Skipping tick() before render. Every stateful effect exposes a tick() method that advances its internal clock. Rendering without ticking gives a frozen image.
  • Re-creating the effect each frame. Effects carry internal state (ball positions, plasma phase, particle arrays). Construct once, store on your model, tick per frame.
  • Running at Full quality on a huge viewport. The area clamp drops to Reduced automatically past ~16K cells; bypassing it manually makes you exceed the frame budget.
  • Feeding the effect a global theme. Effects only read ThemeInputs. Derive a ThemeInputs once per theme change and cache it.

Where next