Applying Shaders
RoyalTerminal exposes shaders at two levels:
TerminalControl.ShaderSourcesfor Avalonia hostsTerminalShaderPostProcessorfor lower-level Skia render integration and tests
Most applications should use TerminalControl.
TerminalShaderSource, TerminalShaderLanguage, and compatibility translation are defined in RoyalTerminal.Shaders. TerminalShaderPostProcessor and TerminalShaderFrameContext are Skia adapter types from RoyalTerminal.Rendering.Skia.
Apply a shader to TerminalControl
Create one or more TerminalShaderSource instances and assign them to ShaderSources:
using RoyalTerminal.Shaders;
Terminal.ShaderSources =
[
new TerminalShaderSource(
"Green Tint",
"""
uniform shader shaderTexture;
half4 main(float2 fragCoord) {
float4 color = shaderTexture.eval(fragCoord);
return half4(color.r * 0.72, color.g, color.b * 0.72, color.a);
}
""")
];2
3
4
5
6
7
8
9
10
11
12
13
14
15
Assign null or an empty list to remove shaders:
Terminal.ShaderSources = null;Enable animated shaders
Shaders that depend on Time, iTime, iTimeDelta, or iFrame should mark the source as animated:
Terminal.ShaderSources =
[
new TerminalShaderSource(
"Animated Scanline",
shaderSource,
TerminalShaderLanguage.SkiaRuntimeEffect,
requiresContinuousAnimation: true)
];
Terminal.ShaderAnimationEnabled = true;2
3
4
5
6
7
8
9
10
ShaderAnimationEnabled defaults to true. Set it to false when your host wants shader effects to update only when the terminal itself redraws.
Bind from MVVM
ShaderSources and ShaderAnimationEnabled are Avalonia direct properties, so a host can bind them from a ViewModel:
<rt:TerminalControl
ShaderSources="{Binding ActiveShaderSources}"
ShaderAnimationEnabled="{Binding ShaderAnimationEnabled}" />2
3
Keep the ViewModel framework-agnostic by storing shader selections as application state and creating TerminalShaderSource values in a presentation service or adapter.
Chain multiple shaders
ShaderSources is ordered. Each shader receives the previous shader's output as its input texture:
Terminal.ShaderSources =
[
new TerminalShaderSource("Bloom", bloomSource),
new TerminalShaderSource("Scanlines", scanlineSource, requiresContinuousAnimation: true),
];2
3
4
5
Chaining is useful for small composable effects, but each pass creates another framebuffer step. Prefer one combined shader for hot paths.
Use compatibility modes
Select the source language on TerminalShaderSource:
Terminal.ShaderSources =
[
new TerminalShaderSource(
"Ghostty MainImage",
ghosttyStyleSource,
TerminalShaderLanguage.GhosttyShadertoy,
requiresContinuousAnimation: true),
new TerminalShaderSource(
"Windows Terminal HLSL",
windowsTerminalSource,
TerminalShaderLanguage.WindowsTerminalHlsl)
];2
3
4
5
6
7
8
9
10
11
12
Compatibility modes are translated into Skia Runtime Effect source before compilation. See Ghostty/Shadertoy Compatibility and Windows Terminal HLSL Compatibility for the supported source patterns.
Use the low-level post processor
Use TerminalShaderPostProcessor when you are testing shader behavior or rendering outside TerminalControl:
using RoyalTerminal.Avalonia.Rendering;
using RoyalTerminal.Shaders;
using TerminalShaderPostProcessor processor = TerminalShaderPostProcessor.Create(sources);
if (!string.IsNullOrWhiteSpace(processor.CompileLog))
{
// Surface diagnostics in your settings UI or logs.
}
TerminalShaderFrameContext frameContext = new(
width,
height,
time,
timeDelta,
frame,
scale,
backgroundColor,
foregroundColor,
cursorColor,
cursorRect,
cursorStyle,
cursorVisible);
bool applied = processor.TryApply(canvas, inputFrame, destinationRect, frameContext);2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Invalid shader sources are skipped and recorded in CompileLog. TryApply returns false when no shader could be applied, letting callers fall back to drawing the unmodified terminal frame.
Demo app samples
RoyalTerminal.Demo includes a toolbar shader button that cycles through built-in samples:
OffCRT AmberHue ShiftTransparent KeyRetro ScanlinesWindows Terminal CRTGhostty Shadertoy
The samples live in TerminalShaderSampleCatalog and include direct Skia Runtime Effect source plus translated Windows Terminal HLSL and Ghostty/Shadertoy sources. They are useful as implementation examples and quick visual validation, not as a replacement for application-specific shader design.
Performance guidance
Framebuffer shaders run every time the terminal presenter redraws. Animated shaders can redraw even when terminal output is idle.
Use these rules for production hosts:
- prefer direct Skia Runtime Effect source for effects you own
- avoid long shader chains in latency-sensitive terminals
- disable continuous animation for static effects
- compile shaders when settings change, not during every frame
- keep compatibility-mode shaders small and inspect
CompileLogbefore enabling them