Hello all,
I’d like to share Elm-Motion, a library for animations and scrolling built around one idea: define an animation or scroll once, then run it on whichever engine fits - without rewriting the animation or scroll.

Build once, swap engines
The animation definition is engine-agnostic. The same fadeIn runs on any engine:
-- Define it once - reusable, engine-agnostic
fadeIn : AnimBuilder { eng | withTiming : () } -> AnimBuilder { eng | withTiming : () }
fadeIn =
Opacity.begin
>> Opacity.to 1
>> Opacity.duration 2500
>> Opacity.end
-- Run it with the Transition engine (pure CSS, minimal setup)
Transition.animate model.animState <|
Transition.for "box"
>> fadeIn
-- ...or the Keyframe engine (CSS @keyframes) - same `fadeIn`, no rewrite
Keyframe.animate model.animState <|
Keyframe.for "box"
>> fadeIn
-- ...or the Sub engine (pure Elm, frame-based) - same `fadeIn`, no rewrite
Sub.animate model.animState <|
Sub.for "box"
>> fadeIn
-- ...or the WAAPI engine (JS Web Animations API) - same `fadeIn`, no rewrite
WAAPI.animate model.animState <|
WAAPI.for "box"
>> fadeIn
Some engines have more capabilities than others - the type system and compiler keep you honest about what each one supports.
Animation engines
- Transition - Simplest - CSS transitions, minimal setup
- Keyframe - CSS keyframes, looping, full control
- Sub - Pure Elm, frame-based, real-time queries
- WAAPI - JS Web Animations API, fine-grained control
- ScrollTimeline - Scroll-driven (via WAAPI)
- ViewTimeline - Viewport/element-in-view driven (via WAAPI)
Scroll engines
- Cmd - Fire-and-forget
- Task - Composable, with error handling
- Sub - Stateful, full control, mid-scroll queries
The same scroll configuration works across all three.
Pure Elm vs JS interop
- Pure Elm, no setup: Transition, Keyframe, Sub.
- Needs JS companion (
@phollyer/elm-motion, on npm + CDN): WAAPI, ScrollTimeline, ViewTimeline — these drive the browser’s Web Animations / scroll-timeline APIs through ports.
Links
- Repo: phollyer/elm-motion — clone and play; use elm-doc-preview for the API docs
- Full docs + interactive examples for every engine: Elm Motion
Status
It’s not published yet - I can’t possibly test every edge case or use, so if you’d like to have a play, any and all feedback would really be appreciated.
Thanks for taking a look!