Modules
std::shaders::motion
Stability: Tentative — motion shaders: loops and settling moves around an entity’s pose, and paths between two poses.
Two kinds of motion:
- Local motions for
Entity::local_motionreturn aLocalMotion. They move the entity around its pose for drawing only: the loops repeat forever, the settling moves hold still at their end. - Transitional motions for
Entity::travelreturn aTransitionalMotion: a path from the entity’s pose to the pose given totravel, over a duration..ease(…)and.delay(…)adjust it.
Distances are pixels and angles turn clockwise. Every function is const fn, not builtin.
use std::screen::{degrees, offset, pose};use std::shaders::motion::{bounce, decaying_quake, sine_integral, sway};
screen.entity(LANTERN).local_motion().change(Some(sway(degrees(6.0), ms(2400)))).apply_animation(instant());screen.entity(HANAKO).travel(pose(point(1200, 1080), 1.0, degrees(0.0)), bounce(40.0, ms(900)));screen.entity(SIGN).local_motion().change(Some(decaying_quake(12, 4, ms(800)))).apply_animation(instant());screen.entity(CARD).local_motion().change(Some(sine_integral(offset(0, -60), 12.0, ms(2000)))).apply_animation(instant());Overview
| Function | Kind | Motion |
|---|---|---|
bob(height, period) | local, loop | rises and comes back |
sway(angle, period) | local, loop | rocks around the anchor |
pulse(scale, period) | local, loop | grows and shrinks |
spin(period) | local, loop | turns around the anchor |
orbit(radius, period) | local, loop | circles through the pose |
decaying_quake(horizontal, vertical, duration) | local, settling | a shake that fades out |
sine_integral(amplitude, rate, settle) | local, settling | an overshooting approach |
arc(height, duration) | transitional | a parabolic arc |
bounce(overshoot, duration) | transitional | an overshooting arrival |
spiral(turns, duration) | transitional | a spiral into the end |
The loops use the behaviour Periodic { after: ZERO, period }: they start at once and repeat
every period. Changing a loop’s arguments with a tween keeps its clock, so a bob can grow from
bob(0.0, p) to bob(20.0, p) without a jump.
Bob
#[shader_arg]pub struct Bob { pub height: Decimal, pub period: Duration,}pub const BOB: LocalMotionShader<Bob> = asset("shaders/local_motion/bob.wgsl");pub const fn bob(height: Decimal, period: Duration) -> LocalMotionRises by height pixels and comes back once per period, forever, easing in and out:
y − height·(0.5 − 0.5·cos(2π·t/period)).
Sway
#[shader_arg]pub struct Sway { pub angle: Angle, pub period: Duration,}pub const SWAY: LocalMotionShader<Sway> = asset("shaders/local_motion/sway.wgsl");pub const fn sway(angle: Angle, period: Duration) -> LocalMotionRocks around the anchor by angle either way once per period, forever:
rotation + angle·sin(2π·t/period).
Pulse
#[shader_arg]pub struct Pulse { pub scale: Decimal, pub period: Duration,}pub const PULSE: LocalMotionShader<Pulse> = asset("shaders/local_motion/pulse.wgsl");pub const fn pulse(scale: Decimal, period: Duration) -> LocalMotionGrows to scale times its size and back once per period, forever, around the anchor.
Spin
#[shader_arg]pub struct Spin { pub period: Duration,}pub const SPIN: LocalMotionShader<Spin> = asset("shaders/local_motion/spin.wgsl");pub const fn spin(period: Duration) -> LocalMotionTurns clockwise around the anchor once per period, forever.
Orbit
#[shader_arg]pub struct Orbit { pub radius: Decimal, pub period: Duration,}pub const ORBIT: LocalMotionShader<Orbit> = asset("shaders/local_motion/orbit.wgsl");pub const fn orbit(radius: Decimal, period: Duration) -> LocalMotionCircles clockwise on a circle of radius pixels that passes through the pose, once per period,
forever. The entity starts at its pose and comes back to it every round.
Decaying quake
#[shader_arg]pub struct DecayingQuake { pub horizontal: i32, pub vertical: i32, pub duration: Duration,}pub const DECAYING_QUAKE: LocalMotionShader<DecayingQuake> = asset( "shaders/local_motion/decaying_quake.wgsl",);pub const fn decaying_quake(horizontal: i32, vertical: i32, duration: Duration) -> LocalMotionA shake that fades out (TyranoScript [quake2]): moves by horizontal and vertical pixels,
alternating direction every frame at 60 fps, with the amplitude falling off quadratically to 0
over duration; then holds still at the pose. The behaviour is Stall { after: duration }.
Sine integral
#[shader_arg]pub struct SineIntegral { pub amplitude: Offset, pub rate: Decimal,}pub const SINE_INTEGRAL: LocalMotionShader<SineIntegral> = asset( "shaders/local_motion/sine_integral.wgsl",);pub const fn sine_integral(amplitude: Offset, rate: Decimal, settle: Duration) -> LocalMotionAn overshooting approach, and an example of a motion defined by a differential equation. The
entity moves by amplitude · (2/π) · Si(rate · t), the solution of
dx/dt = amplitude · (2/π) · sin(rate · t) / t from x = 0: it overshoots amplitude and
settles at it in ringing swings. rate is in radians per second. The behaviour is
Stall { after: settle }, so the motion holds where it is at settle.
Si has no closed form; the shader integrates it numerically from 0 to t every frame, since a
shader keeps nothing between frames. The motion only changes how the entity is drawn: the
entity’s logical position stays where it was, displaced by amplitude on screen once it settles.
Arc
#[shader_arg]pub struct Arc { pub height: Decimal,}pub const ARC: TransitionalMotionShader<Arc> = asset("shaders/transitional_motion/arc.wgsl");pub const fn arc(height: Decimal, duration: Duration) -> TransitionalMotionMoves along a parabola that peaks height pixels above the straight line halfway, over
duration: mix(start, end, p) − (0, height · 4p(1 − p)) for progress p. Scale and rotation
change linearly.
Bounce
#[shader_arg]pub struct Bounce { pub overshoot: Decimal,}pub const BOUNCE: TransitionalMotionShader<Bounce> = asset( "shaders/transitional_motion/bounce.wgsl",);pub const fn bounce(overshoot: Decimal, duration: Duration) -> TransitionalMotionAn overshooting arrival (animate.css bounceIn*): moves straight, overshoots the end by
overshoot pixels, comes back past it by less, overshoots once more by less still, and settles,
over duration. Scale and rotation change linearly. When the start and end positions are the
same, the position does not bounce.
Spiral
#[shader_arg]pub struct Spiral { pub turns: Decimal,}pub const SPIRAL: TransitionalMotionShader<Spiral> = asset( "shaders/transitional_motion/spiral.wgsl",);pub const fn spiral(turns: Decimal, duration: Duration) -> TransitionalMotionSpirals into the end, turning turns times clockwise around it while the distance shrinks
linearly, over duration. Scale and rotation change linearly.
Last updated Oct 29, 2026