Manifest ReferenceAnimations & Transitions

Animations & Transitions

Reference for PanelAnimations (layer keyframes and viewport moves) and the Transition object — the seven transition types, directions, timing, and easing.

The format describes motion in two places: within a panel (PanelAnimations — layer keyframes and a camera move across the artwork) and between panels or pages (Transition — the visual effect while navigating). Both are declarative.

PanelAnimations

Defined as $defs/PanelAnimations, set per panel at chapters[].panels.<panelId>.animations (also overridable via variants). A panel has one animation. It can hold layer keyframes (layers move, fade or change their filters), a viewport move (a Ken-Burns-style pan/zoom over the artwork), or both.

PropertyTypeRequiredConstraintsDescription
namestringNo—Author-facing label. Not shown to readers.
durationMsintegerNo0–120000Animation duration in milliseconds.
loopbooleanNodefault falseRestart the animation when it ends.
keyframesAnimationKeyframe[]Noup to 2000 itemsLayer keyframes — see below.
startViewportRectNormalizedRectNox,y,w,h each 0–1Viewport at animation start, normalized to the panel.
endViewportRectNormalizedRectNox,y,w,h each 0–1Viewport at animation end.
easingenumNolinear, ease, ease-in, ease-out, ease-in-outEasing of the viewport move.

Layer keyframes (AnimationKeyframe)

Added within format 1.6.0. Each keyframe sets one property of one layer at one point in time:

PropertyTypeRequiredConstraintsDescription
layerIdIdentifierYes—The layer of this panel to animate (layers[].id).
propertyenumYessee the table belowWhat to animate.
timeMsintegerYes0–120000Position on the panel's timeline in milliseconds.
valuenumberYes—Value at that time, in the unit of the property.
easingenumNolinear, ease, ease-in, ease-out, ease-in-outCurve from this keyframe to the next one of the same track. Default linear.
idIdentifierNo—Stable id for authoring tools.

No other properties are allowed on a keyframe.

propertyValue
opacity0–1; replaces the layer's opacity.
transform.x, transform.yOffset from the layer's resting position as a fraction of the panel's width / height (0.1 = 10% right / down, negative = left / up).
transform.scaleScale factor around the layer's center (1 = unchanged).
transform.rotationDegrees clockwise around the layer's center.
blurBlur radius in px at a panel width of 1024 px; scaled with the rendered panel.
brightness, contrast, saturateMultiplier (1 = unchanged).

Offsets and blur are relative to the panel, not device pixels, so an animation looks the same on every screen.

How keyframes play:

  • Keyframes with the same layerId and property form a track, ordered by timeMs.
  • Before a track's first keyframe the layer holds the first value; after the last it holds the last value.
  • Between two keyframes the value is interpolated with the easing of the earlier keyframe.
  • Properties without keyframes are left as the layer defines them.
  • The animation starts when the panel is shown and runs for durationMs (or until the last keyframe, when that is later). With loop: true it restarts; otherwise it keeps its end state.
  • Players that honour a reduced-motion preference skip the motion and show the end state.
  • A keyframe whose layerId does not exist in the panel is ignored.
{
  "animations": {
    "name": "Robot rolls in",
    "durationMs": 4000,
    "loop": false,
    "keyframes": [
      { "layerId": "character", "property": "transform.x", "timeMs": 0, "value": -0.4, "easing": "ease-out" },
      { "layerId": "character", "property": "transform.x", "timeMs": 3000, "value": 0 },
      { "layerId": "character", "property": "opacity", "timeMs": 0, "value": 0 },
      { "layerId": "character", "property": "opacity", "timeMs": 800, "value": 1 }
    ]
  }
}

The character starts 40% of the panel width to the left, invisible, fades in over 0.8 s and rolls to its resting position over 3 s.

Player support. @panelwave/player plays layer keyframes and viewport moves from the release after 1.2.0 (merged, not yet published at the time of writing). Version 1.2.0 and earlier ignore both and show the static panel, as does any consumer that does not know them.

Viewport moves

startViewportRect and endViewportRect describe a camera move from one rectangle of the panel to another: the part of the panel the reader sees at the start and at the end of the animation. A NormalizedRect requires all four of x, y, w, h, each between 0 and 1.

{
  "animations": {
    "startViewportRect": { "x": 0, "y": 0, "w": 1, "h": 1 },
    "endViewportRect": { "x": 0.3, "y": 0.2, "w": 0.4, "h": 0.4 },
    "durationMs": 4000,
    "easing": "ease-in-out"
  }
}

This starts on the full panel and slowly zooms into the region at (0.3, 0.2) sized 40% × 40% — a classic dramatic push-in.

How a viewport move plays:

  • The move runs from the start rect to the end rect over durationMs, eased by easing, on the same timeline as the layer keyframes; loop restarts both.
  • A missing rect is the whole panel: an end rect alone is a push-in, a start rect alone a pull-back.
  • The rect is fitted into the panel box with a uniform scale — the artwork is never stretched — and centred. Near an edge the rect sits off-centre rather than showing empty space.
  • Everything anchored to the artwork moves together: layers, hotspots and speech bubbles. The panel box clips the result.
  • Without durationMs there is no travel: the end rect is shown as a static framing.
  • Reduced motion shows the end rect at once.

Layers can add depth to these moves with parallaxDepth — see Layers.

Transition

Defined as $defs/Transition. Transitions describe how the view changes while navigating, and appear in several places:

  • Graph edges — chapters[].graph.edges[].transition (the transition used when following that edge)
  • Hotspot goTo actions — action.transition (see Hotspots)
  • Page transitions — pages[].transitions.in / pages[].transitions.out (entering/leaving a page)
  • Format presets — settings.outputPresets.<format>.defaultTransition (per-output-format default, see Settings)

All properties are optional:

PropertyTypeConstraintsDescription
typeenumnone, cut, fade, slide, zoom, push, coverThe transition effect (7 types).
direnumleft, right, up, downDirection for directional types (slide, push, cover).
durationMsinteger0–60000Duration in milliseconds.
easingenumlinear, ease, ease-in, ease-out, ease-in-outEasing function.

Transitions on edges

The most common placement is on a graph edge, so different paths through the story can feel different:

{
  "graph": {
    "entry": "p1",
    "edges": [
      {
        "from": "p1",
        "to": "p2",
        "transition": { "type": "slide", "dir": "left", "durationMs": 300 }
      },
      {
        "from": "p2",
        "to": "p3-dream",
        "condition": { "==": [{ "var": "state.dreaming" }, true] },
        "transition": { "type": "fade", "durationMs": 800, "easing": "ease-in-out" }
      }
    ]
  }
}

Which transition applies

  • Navigation triggered by a hotspot goTo action uses the transition declared on that action.
  • Navigation along a graph edge uses the edge's transition.
  • When the chosen navigation declares no transition, the active output format's defaultTransition from settings.outputPresets serves as the fallback.

What can animate — summary

MechanismWhereWhat it does
PanelAnimations keyframespanels.<id>.animations.keyframesLayers move, fade, scale, rotate or change filters over time.
PanelAnimations viewport movepanels.<id>.animationsViewport pan/zoom across the panel artwork.
Transitionedges, hotspot actions, pages, output presetsEffect between panels/pages during navigation.
parallaxDepthlayer propertyPer-layer depth offset during viewport motion — see Layers.
Video layerspanels.<id>.layers[] / panels.<id>.video[]Real motion content — see Video.