Visual Effects
Enhance elements and the stage with built-in decorators and procedural backgrounds.
Decorators
Decorators add visual styling, atmospheric effects, or camera motion to elements via .decorate() without modifying stage positioning or layout coordinates.
Multiple decorators can be chained in sequence:
import { Title, gradient, glow } from "stageroutine";
const title = Title("StageRoutine")
.decorate(gradient())
.decorate(glow());
StageRoutine includes 10 built-in decorators:
- Camera & Motion:
kenBurns(),dream(),typewriter() - Lighting & Atmosphere:
glow(),gradient(),scrim() - Framing & Accents:
bracket(),rule() - Texture & Underlays:
grain(),vignette()
Camera Pan & Zoom
kenBurns() applies authentic camera panning and slow zooming across an Image, clipping Frame, or media container.
Camera motion restarts from frame zero whenever the scene activates and pauses when deactivated, keeping backward navigation (stage.prev()) snapshot-safe. Translations clamp to frame bounds so edges never reveal empty margins.
import { Frame, Image, kenBurns, paths } from "stageroutine";
// Photo from https://images.nasa.gov/details/GSFC_20171208_Archive_e000383
const photo = Image(
"/stageroutine/img/GSFC_20171208_Archive_e000383~large.jpg",
{ fit: "cover" },
);
const frame = Frame(paths.box({ radius: 12 }), photo, {
position: [80, 45],
origin: "center",
width: 40,
height: 26.67,
}).decorate(
kenBurns({
scale: 1.35,
focus: "top-right",
duration: 10,
loop: "alternate",
}),
);
For configuration options such as focal anchors, durations, and directional keyframes, see the kenBurns API reference.
Gradient & Glow
Combine gradient() and glow() to create luminous, animated typography for titles and key metrics.
gradient() flows multi-stop color sweeps across text. Its default colors follow the theme palette. glow() uses the theme secondary color for its pulsing aura. See Theming to set these colors.
import { Title, glow, gradient } from "stageroutine";
const title = Title("StageRoutine", {
variant: "hero",
position: [80, 45],
origin: "center",
})
.decorate(
gradient({
colors: ["#38bdf8", "#818cf8", "#c084fc", "#818cf8", "#38bdf8"],
}),
)
.decorate(glow({ pulse: true, duration: 3 }));
Keep a glow controller when you want to animate its strength:
const light = glow({ intensity: 0, pulse: false });
const star = Shape(paths.star(), { size: 16 }).decorate(light);
stage.scene("A little light").with(star);
stage.pause();
light.to({ intensity: 1 }).duration(0.5); // Seconds.
stage.pause();
Intensity is a multiplier: 0 hides the glow, 1 is normal, and larger values make it stronger.
The default is 1. Strength restores when you go backward.
Each controller attaches to one element. Create a new controller for each element.
The optional pulse modulates the strength. It pauses while hidden and stops at zero intensity.
Its default cycle is 4 seconds. Use pulse: false for a steady glow.
Glow uses the theme's secondary colour and composes with element blur and brightness.
Blur and brightness apply first, followed by effect filters in attachment order.
For configuration options, see the gradient and glow API references.
Shine
shine() creates a moving reflection. Attach it to a shape or card, then animate its progress.
Progress is a fraction from 0 to 1. Both endpoints leave the surface clear. The default is 0.
import { Card, shine } from "stageroutine";
const light = shine();
const card = Card("Ready to present", { width: 30, height: 12 }).decorate(light);
stage.scene("Catch the light").with(card);
stage.pause();
light.to({ progress: 1 }).duration(0.9); // Seconds.
stage.pause();
Reuse the same instance to share one light across several elements:
const light = shine();
diamond.decorate(light);
hexagon.decorate(light);
star.decorate(light);
light.to({ progress: 1 }).duration(2.4);
The band crosses the receivers' combined visible bounds. It reaches each receiver according to its position.
Separate shine() instances produce independent lights. All receivers of one light must belong to the same stage.
Ghost shapes catch light on their outlines. Other shapes and cards catch it on their filled surfaces, beneath their content.
Use surface: "outline" or surface: "fill" to choose explicitly. The default is "auto".
Text, icons, images, and arbitrary HTML do not yet expose receiving surfaces.
const light = shine({
color: "#fbbf24",
highlight: "#fff4ce",
angle: -20, // Travel direction in degrees. Default: -20.
width: 0.22, // Fraction of the combined span along that direction. Default: 0.22.
intensity: 1, // Nonnegative strength multiplier. Default: 1.
});
Angle 0 travels right. Positive angles turn clockwise.
By default, colour comes from the stage theme's secondary token, even when receivers have local themes.
The core uses a lighter version of that colour. Theme changes update the light.
The field stays aligned to the stage while receivers move, rotate, or resize. Hidden receivers do not enlarge its bounds. Going backward restores progress. The sweep uses the stage clock and stops when its transition finishes. Reduced-motion preferences suppress the travelling reflection.
Keep reactions such as glow, rotation, or a lasting highlight in your presentation's choreography.
See the shine API reference for all options.
Framing Brackets
bracket() attaches technical framing brackets around code blocks, cards, or shapes.
Supported styles include corner HUD brackets ("corners"), curly braces ("curly"), square brackets ("square"), and rounded parentheses ("round"), with optional apex labels and neon bloom.
import { CodeBlock, bracket } from "stageroutine";
const code = CodeBlock(
[
"// Create a stage",
"const stage = new Stage();",
"stage.mount('#stage');",
],
{
position: [80, 45],
origin: "center",
},
).decorate(
bracket({
style: "corners",
color: "#38bdf8",
offset: 14,
}),
);
For configuration options, see the bracket API reference.
Typewriter
typewriter() animates text letter-by-letter with simulated human typing cadence, backspaces, pauses, and a blinking cursor.
Typing triggers automatically whenever the element enters active visibility, and resets cleanly when stepping backward.
Set loop: true to restart the script when it finishes. A trailing { pause } step holds before each restart.
import { Title, typewriter } from "stageroutine";
const text = Title("", {
position: [80, 45],
origin: "center",
}).decorate(
typewriter({
delay: 0.2,
loop: true,
script: [
"Code-driven presentations",
{ pause: 0.8 },
{ delete: 13 },
"built for the stage.",
{ pause: 1.5 },
],
}),
);
For configuration options and custom typing scripts, see the typewriter API reference.
Liquid Ripple
Materializes elements through undulating liquid ripples, prismatic dispersion, and soft defocus blur settling into sharp focus during entrance transitions.
import { Title, dream } from "stageroutine";
const title = Title("Dream Entrance").decorate(
dream({ duration: 1.8, intensity: 40 }),
);
For configuration options, see the dream API reference.
Film Grain
Adds an animated film grain texture overlay to give elements or backgrounds a tactile, cinematic look.
import { Card, grain } from "stageroutine";
const card = Card("Film Grain").decorate(
grain({ opacity: 0.1, granularity: 1 }),
);
For configuration options, see the grain API reference.
Readability Scrim
Places a soft radial dark underlay behind text to ensure readability over busy or animated backgrounds.
import { Title, scrim } from "stageroutine";
const headline = Title("High Contrast").decorate(
scrim({ spread: "wide", opacity: 0.85 }),
);
For configuration options, see the scrim API reference.
Edge Vignette
Darkens the outer boundaries of the canvas or element with a smooth radial falloff to direct viewer attention inward.
import { Card, vignette } from "stageroutine";
const card = Card("Vignette Focus").decorate(
vignette({ opacity: 0.8 }),
);
For configuration options, see the vignette API reference.
Side Accents
Attaches an accent divider line or corner-wrapping bracket to any side of an element.
import { CodeBlock, rule } from "stageroutine";
const code = CodeBlock(["const app = express();"]).decorate(
rule({ side: "left", color: "#38bdf8", thickness: 3 }),
);
For configuration options, see the rule API reference.
Backgrounds
Backgrounds provide procedural canvas or WebGL backdrops beneath the stage:
import { Stage, AsciiFluid, vignette } from "stageroutine";
const stage = new Stage().background(
AsciiFluid().decorate(vignette({ opacity: 0.85 })),
);
Built-in Backgrounds
AsciiFluid(): WebGL fluid simulation rendered with ASCII charactersGradientFluid(): Smooth colorful fluid simulationStarfield(): Procedural 3D perspective starsCSSBackground(): Gradients and solid colors
Background Decorators
Backgrounds support decorators via .decorate(). Both vignette() and grain() can decorate backgrounds directly:
import { AsciiFluid, Starfield, grain, vignette } from "stageroutine";
// Soft edge vignette on fluid simulation
const fluidBg = AsciiFluid().decorate(vignette());
// Subtle tactile grain on starfield
const starsBg = Starfield().decorate(grain({ opacity: 0.08 }));