--- name: tweenframe description: Make animated GIF, WebP, APNG, MP4 or WebM files and still images (banners, before/after comparisons, captioned GIFs, animated text) with the TweenFrame CLI and API. --- # TweenFrame TweenFrame renders finished media files from structured inputs: texts, colors, photos and output options go in, a GIF, WebP, APNG, MP4, WebM, PNG, JPEG, SVG or PDF comes out. You drive it with the `tweenframe` CLI (or the HTTP API it wraps). Your job is the file the user asked for: render it, check it and report it. Do not hand back a list of commands instead of the result. ## Setup - Install the CLI (Node.js 22.12 or newer): `npm install -g https://tweenframe.com/cli/tweenframe-cli.tgz` - Auth: none to set up. The first command that needs a token (such as an upload, an image analysis, a check, a render or an email sign-in) creates an anonymous identity and stores it for later runs; when it expires, the next such command replaces it. `tweenframe auth status` shows the stored identity. Browsing the catalog needs no token. - Help is complete and offline: `tweenframe help`, `tweenframe --help` (every option with its type, range and default, plus examples). `tweenframe docs` lists the guides below and `tweenframe docs ` prints one. ## Workflow Find the recipe nearest to what the user wants with `tweenframe recipes list` (each entry has an `inputs_summary`: required inputs, image slots, text and color counts) and read its inputs with `tweenframe recipes show ` (a readable summary of every input, the output defaults and the example names; `--json` prints the full schema). Then one of two: - **Its inputs give the result.** Save the closest example with `tweenframe recipes show --example minimal > work/inputs.json`, change only what the user asked for, put the user's images in as `file:///absolute/path` values, then `tweenframe recipes render --input --wait --output `. - **The result needs a change the inputs cannot make** (another layout, more or fewer blocks, other logic). Save the recipe's document with `tweenframe recipes show --document > work/recipe.json`, edit it, check it with `tweenframe recipes check --document work/recipe.json --input work/inputs.json` until it passes, then `tweenframe recipes render --document work/recipe.json --input work/inputs.json --wait --output `. The authoring guide explains the document and has examples to start from when no recipe is near; icons and decor shapes for it come from `tweenframe library search `. **Storyboard before every animated file.** Before the final render, run the same `recipes render` with `--format storyboard --output work/board.png`, open the image, check every slide and every transition, and fix what is wrong. Only then render the final file. The storyboard shows each slide and each transition in one image for the price of one render. **A size budget, or a video to turn into a GIF.** Keep `normal` quality and about 900 px wide; never start with `minimal` quality or a smaller width. For a budget, first work on the animations (hard cuts or cheaper transitions instead of long full-frame ones, fewer and smaller animated areas and slides), then lower the fps, then compress with `tweenframe gif compress --target-size --output `; it may lower the width, so check the result's. Convert a video with `tweenframe video to-gif --width 900 --output ` (the default width is smaller; `video to-gif --help` has it); see [gif-tools.md](/docs/agent/gif-tools.md). **A photo without its background.** `tweenframe image remove-background --wait --output .png` cuts the subject out onto a transparent background ([assets.md](/docs/agent/assets.md#remove-a-background)); use the PNG like any image. **The user gave a Figma frame** (a banner, a card) and wants other words or their photo on it: dump the frame through the Figma MCP with the `figma-dump` script, then `tweenframe recipes from-figma work/figma.json --output work/recipe.json` turns it into a recipe whose inputs are its texts; the loop, pictures included, is in [import-figma.md](/docs/agent/import-figma.md). **The user wants to keep the design or share a link to it** (render it again later with other inputs, or hand out a URL that renders it from its query): `tweenframe templates save` and `tweenframe links create`, in [links.md](/docs/agent/links.md). They need an account (`registration_required` with an anonymous token). **The user wants a link to the file itself**: a `result_url` is temporary; `tweenframe publish create --result-url ` answers a lasting URL, in [output.md](/docs/agent/output.md). It needs an account too. Output options such as `--format`, `--width` and `--fps` are flags of `recipes render`, not inputs. Keep working files (input JSON, documents, drafts) outside the folder that holds the deliverables, and give every variant its own filename. How recipes, inputs and output options work in detail is in the recipes guide; read it before your first render. ## Guides Plain Markdown on the same origin as this file, also printed by `tweenframe docs `: | Guide | Read it when | | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [recipes.md](/docs/agent/recipes.md) | Before the first render: catalog, versions, input schemas, examples, images, output options, limits. | | [authoring.md](/docs/agent/authoring.md) | You change a recipe's document or write your own: the check and render loop, the skeleton, common errors, and a table of topic guides (layout, text and media, icons, animation, logic, clips, canvas, design, examples). | | [output.md](/docs/agent/output.md) | You choose a format, size, frame rate or quality, or a file is too large, blurry or cut off. | | [assets.md](/docs/agent/assets.md) | You work with the user's photos: uploads, focal points, crops, supported formats. | | [gif-tools.md](/docs/agent/gif-tools.md) | A GIF must fit a size budget, or a video must become a GIF: `gif compress`, `video to-gif`. | | [import-figma.md](/docs/agent/import-figma.md) | The user gave a Figma frame to change: dump it through the Figma MCP, import it as a recipe, put the pictures in, change its texts. | | [links.md](/docs/agent/links.md) | The user wants to keep a recipe document or share a link that renders it (needs an account). | | [errors.md](/docs/agent/errors.md) | A command or request failed: one section per error code. | | [api.md](/docs/agent/api.md) | You call the HTTP API without the CLI. | For a recipe document, read authoring.md first, then only the topic guide you need (`tweenframe docs layout`, `… icons`, …), not all of them. GIF size: what makes it large and what to change first is in [output.md](/docs/agent/output.md#gif-size-what-makes-it-large). ## Errors The CLI prints results as one JSON document on stdout (`{"data": ...}`) and errors as one on stderr, exiting with code 1: ```json { "error": { "code": "invalid_recipe_inputs", "message": "…", "details": { "path": ["slides", 0, "backdrop", "media", "src"] }, "hint": "…", "docs": "/docs/agent/errors.md#invalid_recipe_inputs" } } ``` Read `code`, `details.path` and `hint`; when that is not enough, open the `docs` link (the CLI prints it as a full URL). Fix the value and retry; never resend the same request unchanged. A `quota_exceeded` error means stop and tell the user. A `registration_required` error means the request needs an account: do not change it yourself; ask the user for their email and follow the hint. ## Report For each file: path, format, pixel size, byte size, and duration when it is animated, measured from the file itself. Name the recipe (`id` and `version` from the render response). Say which values you chose that the user did not give you (texts, crops, sizes), and anything you could not do and why.