Inertia — a keyframe animation editor for the UI you already built<br>Why Inertia<br>Your app is the canvas<br>Design tools export something that plays next to your UI rather than being it. Code-first libraries keep you in a compile-run-tweak loop where a 40 ms timing change costs a rebuild. Inertia sits between the two.
Real components<br>A real RoundedRectangle, a real Card composable, a real . No re-implementation and no design file that drifts from the app.
Native playback<br>SwiftUI plays tracks through KeyframeAnimator; Compose and React sample the same tracks on their own clocks. Nothing is rasterized.
Live editing<br>Your app connects to the editor over a local WebSocket, reports its tagged hierarchy, and receives updates as you edit.
How it works<br>Three steps, then it’s in your repo
01<br>Tag a view<br>Wrap anything you want to move with an id. That one call is the whole integration — shapes and tracks attach to the id.
02<br>Drag it on a timeline<br>Run the build inside the editor, select a tagged view, and move, rotate, scale, or fade it. The playhead is shared with the running app.
03<br>Commit the file<br>The editor writes an animation.inertia file. Designers scrub the timeline, developers git add the result — one file, three runtimes, identical ids.
The editor<br>Scrub against a build that’s actually running<br>The centre panel is your app — installed and launched on the iOS Simulator, an Android emulator, or your dev server in a WebView. Select a tagged view, drag it, and the keyframe lands on the timeline.
TranslateRotateScaleOpacityVectors<br>Inertia — Home · translate
Dragging a card in the running app. Green alignment guides snap it against its neighbours, the X/Y sliders read −0.857 and 0 as a fraction of the container, and the readout confirms X: 11 Y: −16, W: 189 H: 189.
Runtimes<br>One API, three platforms<br>Every runtime has the same three pieces: a container that owns the animation data, an actionable wrapping each view you want to move, and a playback handle for starting it.<br>trigger starts a track; one arriving mid-run joins the run in progress.<br>restart rewinds — every actionable in a container is drawn from one clock.<br>cancel returns an animation to its initial values and leaves it there.<br>isRepeating switches looping and play-once at runtime.
SwiftUIJetpack ComposeReact<br>ContentView.swifteditor port 8060<br>import SwiftUI<br>import Inertia
InertiaContainer(dev: isEditor, id: "animation") {<br>ContentView()
struct ContentView: View {<br>@Environment(\.inertiaDataModel) private var inertia: InertiaDataModel!
var body: some View {<br>VStack(spacing: 16) {<br>Card(title: "Welcome")<br>.inertia("card0") // tag the view you want to move
Button("Trigger") { inertia.restart("card0") }
RuntimePackageEditor targetShapesSwiftUIInertia (SPM)iOS Simulator, driven through simctlMetalJetpack Composeinertia-compose (JitPack)Android emulator, over adbOpenGL ES 2.0Reactinertia-react + inertia-baseYour dev server, in a WKWebViewWebGL
The handoff<br>One .inertia file<br>The editor writes an animation.inertia file into a project folder you version alongside your source — one record per tagged id, each a pose plus the track that leaves it.<br>translate — an [x, y] offset as a fraction of the container, so a track survives a resize.<br>scale , opacity — uniform factor, and 0 through 1.<br>rotate / rotateCenter — degrees, anchored top-left or at the view’s centre.<br>invokeType — auto starts when the view appears; trigger waits for your call.
animation.inertiaone record, shown decoded<br>"id": "card0",<br>"initialValues": {<br>"opacity": 1, "rotate": 0, "scale": 1, "translate": [0, 0]<br>},<br>"invokeType": "trigger",<br>"keyframes": [<br>"duration": 0.001,<br>"values": { "scale": 0, "translate": [-0.008, -0.269] }<br>},<br>"duration": 2.011,<br>"values": { "rotate": 90, "scale": 1, "translate": [-0.013, 0.278] }<br>],<br>"shapes": []
Also in the box<br>Details that make it usable
Alignment guides<br>Drag a view and it snaps against its neighbours, on SwiftUI and Compose alike.
Vertex shapes<br>Author geometry behind a view — rectangles, ovals, triangles, or raw vertices with per-corner colour. No shape API to call.
Shared playhead<br>Pause, resume, seek and loop length are shared both ways, so the timeline tracks a running animation.
Editor mode is opt-in<br>In release, none of the socket code runs. The container loads the animation and plays it on its own clock.
Per-view state<br>Two views can share one animation id and keep their own playback state — trigger one without touching the other.
Versioned with your app<br>One .inertia project folder per app, sitting in the repo next to the code it animates.
Get started<br>Get a view moving in a few minutes<br>Add the package, tag a view, and open the editor. The quickstart walks all three runtimes end to end.<br>Read the quickstartFull documentation
Download<br>Get the macOS editor<br>Leave an email and the build is yours — the editor, the three runtimes, and the sample project the quickstart uses.<br>Leave this empty<br>Email addressGet the download<br>One email, for the download link and release...