Animations are data. Build one with LayerAnimation, attach it with .animate(). All frames are local to the layer and integers.
Two keyframes: fromTo
Section titled “Two keyframes: fromTo”import { LayerAnimation } from "framefields";
const entrance = LayerAnimation.create() .fadeIn(0, 20, "power2.out") // opacity 0 → 1, frames 0–20 .fromTo("y", 460, 400, { start: 0, end: 24, ease: "expo.out" }) // to the layout y, not 0 .fromTo("scale", 0.95, 1, { start: 0, end: 30, ease: "back.out(1.4)" });
card.animate(entrance);Shortcuts: fadeIn(start, end, ease), fadeOut(start, end, ease), slideInX/slideInY(from, to, start, end, ease), rotateX/rotateY/rotateZ, translateZ, perspective, letterSpacing, typewriter(start, end), kineticSweep(fromOffset, toOffset, start, end, ease).
Many keyframes: keys
Section titled “Many keyframes: keys”Each tuple is [frame, value, ease?]; the ease shapes motion into that key. Frames are rounded, so beat math can go straight in.
const pop = LayerAnimation.create() .keys("scale", [[0, 0], [10, 1.12, "back.out(1.6)"], [16, 1, "power2.out"]]) .keys("opacity", [[0, 0], [6, 1]]);
LayerAnimation.keys("rotation", [[0, -8], [20, 0, "expo.out"]]); // static formColor props (fill, color, borderColor, …) keyframe too, blended in OKLab.
Easing
Section titled “Easing”<name>.<dir> with optional parameters: "expo.out", "back.out(1.7)", "power2.inOut".
| Names | none power1 power2 power3 sine circ expo back elastic bounce spring cubic hold |
|---|---|
| Directions | in out inOut ("linear" is an alias for none) |
| Use | Ease |
|---|---|
| Snap-overshoot entrances | back.out(1.4–1.7) |
| Decelerating moves | expo.out, power3.out, spring |
| Exits | power2.in |
| Loops, counters, spinners | linear (and nothing else) |
Composing animations
Section titled “Composing animations”.animate() adds to what a node has. Tracks on the same prop merge into one sorted track; a later key wins on a shared frame.
const rainIn = LayerAnimation.keys("y", [[0, -300], [d, -300], [d + 12, 0, "back.out(1.5)"]]);const press = LayerAnimation.keys("y", [[40, 0], [42, 6, "power2.in"], [47, 0, "power2.out"]]);const glow = LayerAnimation.keys("color", [[42, "#ffd400"], [60, "#2a2b2d", "sine.inOut"]]);
key.animate(rainIn).animate(press, glow); // or LayerAnimation.merge(rainIn, press, glow)Procedural drivers
Section titled “Procedural drivers”LayerAnimation.create() .wiggle("x", { frequency: 1.5, amplitude: 25, seed: 3 }) // seeded noise, deterministic .spring("scale", { damping: 14, stiffness: 190, mass: 1 });Bind any prop to a signal with .signal(prop, signal, { multiplier, offset, smoothing }) — see Audio & signals.
Kinetic typography
Section titled “Kinetic typography”Text animators work per glyph, word or line on the GPU. Add one to the text, then sweep its selection with kineticSweep.
import { Layer, LayerAnimation, TextAnimator } from "framefields";
Layer.text("TypeScript in.", { fontFamily: "Archivo", fontWeight: 800, fontSize: 200, fill: "#e6e8e6", animators: [TextAnimator.waveRise({ y: 90, rotationX: 70 })],}).animate(LayerAnimation.create().kineticSweep(-1, 1, 0, 40));| Preset | Effect |
|---|---|
TextAnimator.waveRise({ y, rotationX, opacity, blur, unit }) |
glyphs rise and untilt in a wave |
TextAnimator.wordPop({ scale, y }) |
words pop in |
TextAnimator.blurIn(...) |
glyphs resolve from blur |
TextAnimator.flip3D(...) |
glyphs flip in around X |
TextAnimator.colorSweep(...) |
color passes through the text |
unit is "character" (default), "word" or "line". For terminal-style typing use .typewriter(start, end).
3D transforms on 2D layers
Section titled “3D transforms on 2D layers”Any layer can tilt in perspective.
Layer.box({ width: 600, height: 400, background: "#181715", borderRadius: 24, transformStyle: "preserve-3d", perspective: 1200,}).animate( LayerAnimation.create() .fromTo("rotateX", 25, 0, { start: 0, end: 30, ease: "expo.out" }) .fromTo("rotateY", -35, 0, { start: 0, end: 30, ease: "expo.out" }) .fromTo("translateZ", -200, 0, { start: 0, end: 30, ease: "expo.out" }),);For cameras, lights and meshes, see 3D.
Gotchas
Section titled “Gotchas”- Keyframes past
durationFramesnever play (animation_truncated). - Animating
x/yoverrides the layout position. - A track whose keys all hold one value does nothing (
static_track).