Penpot Motion: keyframe animation for boards (experimental)

Hi everyone :waving_hand:

Over the last few weeks I’ve been vibe-coding an animation tool for Penpot: keyframe animation for boards, right in the workspace, in the spirit of Figma Motion. It lives on a branch of my fork behind a feature flag. I’d love for you to try it and tell me what works and what doesn’t.

What it does

Timeline

  • Each board gets its own timeline in a dock at the bottom (View → Show animation timeline, or Shift+M for motion mode).
  • You can keyframe position, size, rotation, scale, opacity, corner radii, fill and stroke color/opacity, stroke width, shadows, layer and background blur, and path trim (strokes that draw themselves).
  • Easing presets, a cubic-bezier editor, back easings, springs (gentle, quick, bouncy, slow, custom) and hold.
  • Playback modes: once, loop, ping-pong.
  • Preset animations (fade, move, scale, rotate, slide, pop, zoom, spin, …) stay editable. Applied to several layers, they get a stagger.
  • Markers, snapping, box selection, copy/paste of keyframes and arrow-key nudging.

Editing on the canvas

  • In motion mode the canvas shows the board at the playhead, and you edit shapes where they appear. Each drag, resize or rotation becomes a keyframe.
  • REC / auto-keyframe records every property you change at the playhead.
  • Motion paths: the path of a moving layer shows on the canvas, with handles to curve it.
  • The design tab shows the values at the playhead. Typing a value there sets the keyframe.

Components & collaboration

  • Copies of an animated component play the animation of their main, also in other boards and when resized.
  • Time-stamped comments: a comment placed in motion mode belongs to a moment of the animation, and a click on it moves the playhead there.
  • Edits to different layers merge, so two people can animate the same board. Animations survive duplicating, copy/paste and moving layers between boards.
  • View mode plays the animations, and animated boards get a play button on their title.

Export & import

  • Export a board as MP4, WebM, GIF, animated AVIF, animated SVG, Lottie (vectors, gradients, strokes, trim, shadows, blur, clipping) or CSS @keyframes (respects prefers-reduced-motion). Inspect shows the animation CSS of the selection.
  • Import Lottie (JSON or dotLottie, by drag & drop or File → Import Lottie…) as a board with its timeline. A warning lists what it could not bring over.

Plugins & MCP

  • The plugin API, and through it the Penpot MCP server, can create timelines, keyframes, presets and markers, and set the export settings. So an AI agent can animate a board for you.

Performance

  • Works with both renderers, but the WASM renderer is the target. It now draws the viewport in one pass during playback. A toggle plays in full quality (shadows and blur) instead.

Try it

Branch: GitHub - girafic/penpot at girafic-penpot-timeline-animation · GitHub

  1. Check out the branch and start the devenv (./manage.sh run-devenv).
  2. Add enable-feature-animation to the flags (penpotFlags in frontend/resources/public/js/config.js). Turn on the WASM renderer for the video exports. (Optional also add enable-available-viewer-wasm to the flags)
  3. Select a board and open View → Show animation timeline (or press Shift+M).

:warning: This is experimental. The data model can still change, so please don’t use it on files you care about.

Not there yet

  • Shape morphing (animating path points). The Lottie import also skips track mattes, merge paths and text layers.
  • Text-specific animation (per word or letter).
  • Without the WASM renderer, the only export is Lottie, and only when all layers are vectors.
6 Likes

This is absolutely incredible.

:exploding_head:

I’m curious about the data model itself and how it can coexist with Penpot’s own format.

1 Like

Thx @diacritica, in short, Motion extends Penpot’s format rather than adding a second one:

  • All animation data sits in one optional page key, :timelines, next to :flows and :guides. There is one timeline per board, keyed by the board’s id. Each timeline has one track per animated layer, keyed by the layer’s id. Each track holds keyframes (one property, one time, one value, one easing) and editable presets such as fade or move.

  • Shapes don’t change: no new shape attributes, no file migration. A layer’s own attributes are its rest state. Playback turns the timeline into Penpot’s normal modifiers, the ones used while dragging, so both renderers draw it and nothing is written back.

  • Edits use three new change types built like :set-flow, so undo, multi-user sync and version history work as for any other edit.

  • animation/v1 is a frontend-only feature and is never stored on a file. A build without Motion opens animated files, shows the static design and leaves the key alone.

1 Like