# OpenGraphImage — Animated cards (GIF / WebP)
← start here: [llms.txt index](https://opengraphimage.com/llms.txt)
A card can render as an ANIMATED image instead of a still. It is not a separate
endpoint — the same origin/template render path emits animation when the origin's
`format` config option is an animated one. Two exist:
gif Animated GIF. Widest support — email clients, chat, anywhere an
renders. Larger files and a limited color palette.
animated_webp Animated WebP. Far smaller files, full color. Best where WebP is
supported; degrades to a static WebP poster where it isn't.
Static formats (`png` / `jpeg` / `webp`) keep the fast single-shot render. The
animated formats route through a frame-capture pipeline, so they cost more render
budget — that is what the plan gate below is about.
## Enabling it
Set the origin's `format` at creation or on an existing origin. Through the CLI:
og origin create --template --format gif
og origin create --template --format animated_webp
og origin update --format animated_webp # flip an existing origin
or the GraphQL `create_origin(` mutation with `format: "gif"` /
`"animated_webp"` (the update mutation takes the same `format` arg). The dashboard
origin editor also exposes it as a Render Format choice (with the timing controls
below) once the plan allows it.
Switching back to a still is the same call with a static format
(`--format webp`). The plan gate below applies on every write, so a downgrade
that no longer includes animation is rejected until the format is a static one.
## The plan gate
Animation is a paid feature. An origin whose organization's plan does not include
it is REJECTED at write time with a validation error on `format` — it is not
silently downgraded at the config layer. Professional and Enterprise plans
include base (Standard) animation.
There are two rungs, not one:
animation can this origin be animated at all (Standard budget, a 10s
render ceiling). Professional + Enterprise.
animation_quality a longer/smoother render budget above the 10s Standard
ceiling. Enterprise only. Without it, the render budget is
clamped back to Standard on save — the animation still
works, it just can't exceed the Standard timing.
Separately: a WATERMARKED (unpaid) origin always serves a STATIC poster —
`gif` degrades to `png`, `animated_webp` to `webp`. Only an unwatermarked (paid)
origin actually animates. Removing the watermark is what turns animation on for
real, on top of the plan gate.
## Tuning (dashboard config editor)
When the format is animated, the origin editor surfaces optional timing knobs —
all default to the system value when left blank, and every value is re-clamped to
the hard system ceilings at render time:
Duration (ms) total length of the loop
FPS frames per second (higher = smoother, more budget)
Max colors GIF palette size (2–256); ignored for animated_webp
Loop count 0 = loop forever; N = play N times then hold the last frame
Capture strategy how frames are stepped: `scrub` (default) scrubs each
CSS/JS animation frame by frame in real time; `virtual_time`
fast-forwards Chrome's virtual clock deterministically
(better for time-driven JS)
These live in the dashboard only — the CLI/GraphQL surface just sets `format`.
## Author + verify
Author the card much like a still one (see
[render modes](https://opengraphimage.com/llms/render-modes.txt)) — whether output is animated is a property of
the ORIGIN's `format`, not the Liquid. CSS animations / keyframes in the template
are what get captured.
To drive a timeline explicitly, the template declares it in the `__og` stack:
window.__og = [
['animate', { duration: 3000, fps: 12, segments: [...] }],
['capture', '#card'],
];
`['animate', {duration, fps, segments}]` sets the loop length, frame rate, and any
keyframe segments; the following `['capture', selector]` bounds the output. For
VIDEO, the template implements an optional async hook `window.__ogFrame(t)` that
seeks a