@vshaders/ease
v0.2.0
Published
Easing curves and motion-shaping functions as pure WGSL modules for vgpu shaders
Downloads
257
Maintainers
Readme
@vshaders/ease
Easing curves and motion-shaping functions as pure WGSL modules for vgpu shaders:
import { easeOutElastic } from "@vshaders/ease/easing";
import { linearToSrgb3 } from "@vgpu/wgsl-std/color";
struct Uniforms { resolution: vec2f, time: f32 }
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
@fragment fn main(@builtin(position) position: vec4f) -> @location(0) vec4f {
let uv = position.xy / uniforms.resolution - vec2f(0.5);
let pop = easeOutElastic(clamp(fract(uniforms.time * 0.5) * 2.0, 0.0, 1.0));
let radius = 0.35 * pop; // ring pops in, wobbles, settles
let ring = 1.0 - smoothstep(0.0, 0.02, abs(length(uv) - radius));
return vec4f(linearToSrgb3(vec3f(ring)), 1.0);
}Every module is pure WGSL — functions and constants only, no bindings, no entry points — so vgpu's resolver can prune whatever you don't import. Easing curves remap animation progress: feed them a t that advances linearly and get back a t that accelerates, overshoots, rings, or bounces.
@vshaders/ease/easing
The standard easing set as popularized by Robert Penner: ten curve families, each in three variants. All 30 functions share the signature (t: f32) -> f32 and map progress to eased progress with f(0) = 0 and f(1) = 1:
| Family | In | Out | InOut |
| --- | --- | --- | --- |
| quad | easeInQuad | easeOutQuad | easeInOutQuad |
| cubic | easeInCubic | easeOutCubic | easeInOutCubic |
| quart | easeInQuart | easeOutQuart | easeInOutQuart |
| quint | easeInQuint | easeOutQuint | easeInOutQuint |
| sine | easeInSine | easeOutSine | easeInOutSine |
| expo | easeInExpo | easeOutExpo | easeInOutExpo |
| circ | easeInCirc | easeOutCirc | easeInOutCirc |
| back | easeInBack | easeOutBack | easeInOutBack |
| elastic | easeInElastic | easeOutElastic | easeInOutElastic |
| bounce | easeInBounce | easeOutBounce | easeInOutBounce |
tis expected in[0, 1]and is NOT clamped. Out-of-range inputs extrapolate the curve — clamp first (clamp01from@vgpu/wgsl-std/math) if your driver can leave the range. Outputs are not clamped either: back, elastic, and the InOut bounce midpoints intentionally leave[0, 1](back overshoots ~10%, elastic rings up to ~±37%).- Endpoints are exact. expo and elastic guard their exponential edge cases, so
easeInExpo(0) == 0,easeOutExpo(1) == 1,easeInElastic(0) == 0,easeOutElastic(1) == 1(and the InOut variants at both ends) hold precisely rather than to within2^-10. - No NaNs from the domain edges. circ keeps its square roots real for out-of-range
t, and polynomial powers are written as products rather thanpow()(which WGSL leaves undefined for negative bases). - The back and elastic shape constants are named
consts in the module (backOvershoot = 1.70158,elasticFrequency = tau / 3, …) with comments deriving them.
@vshaders/ease/shape
Motion-shaping helpers that don't fit the fixed-endpoint easing mold:
smootherstep(edge0: f32, edge1: f32, value: f32) -> f32— Ken Perlin's quintic step6t⁵ − 15t⁴ + 10t³: likesmoothstepbut with zero second derivative at both edges, so chained motion has no curvature kink.valueis clamped to the edge interval; reversed edges (edge0 > edge1) fall from 1 to 0; a zero-width edge (edge0 == edge1) degrades to a hardstepinstead of dividing by zero.almostIdentity(value: f32, threshold: f32, floorValue: f32) -> f32— the identity forvalue >= threshold; below the threshold, the unique cubic that starts flat atfloorValueand joins the identity atthresholdwith matching value and slope. Use it to keep a length or radius from reaching zero without a visible seam.valueis expected>= 0andfloorValue <= threshold; a non-positivethresholdreturns the input unchanged.springResponse(t: f32, damping: f32, frequency: f32) -> f32— the unit-step response of a second-order system: starts at 0 with zero velocity and settles to 1.tis elapsed time (expected>= 0, not normalized — the curve settles asymptotically);frequencyis the undamped natural frequency in radians per unit oft(expected> 0);dampingis the damping ratio: 0 oscillates forever, values below 1 overshoot and ring, 1 is the no-overshoot limit. Negative damping is clamped to 0, anddamping >= 1degrades to the critically-damped response (the exactζ = 1curve) rather than the slower overdamped form.
import { springResponse } from "@vshaders/ease/shape";
// Inside a fragment shader with the usual Uniforms { resolution, time }:
// a bar that springs to a new width every second — overshoots, rings, settles.
let sinceHop = fract(uniforms.time);
let goal = select(0.25, 0.75, fract(uniforms.time / 2.0) < 0.5); // alternates each second
let width = mix(1.0 - goal, goal, springResponse(sinceHop, 0.35, 18.0));
let bar = step(position.x / uniforms.resolution.x, width);Provenance
The easing curve formulas are the standard set popularized by Robert Penner ("Motion, Tweening, and Easing", Programming Macromedia Flash MX, 2001; the equations are BSD-licensed and the closed forms are elementary math). smootherstep is Ken Perlin's quintic interpolant ("Improving Noise", SIGGRAPH 2002). almostIdentity is derived here as the unique cubic satisfying four Hermite boundary constraints, a construction popularized by Inigo Quilez. springResponse is the textbook step response of an underdamped second-order system from control theory. All WGSL in this package is an original implementation, with edge-case guards (exponential endpoints, zero-width edges, non-positive thresholds, damping >= 1) in the argument ranges the math leaves undefined.
Verifying
npx vgpu check path/to/your-entry-shader.wgslresolves the import graph, validates the composed shader, and prints its reflection.
License
MIT
