# Shotstack Documentation (Full) This file is generated from markdown docs visible in the sidebar for high-recall agent retrieval. Use `llms.txt` for lightweight navigation, and the API reference at https://shotstack.io/docs/api/ for endpoint-level details (parameters, status codes, error responses). The full OpenAPI spec for the Edit API is at https://shotstack.io/docs/api/api.edit.json. Rate limits are in the Limitations section below. Every page is also available individually as raw Markdown by appending `.md` to its docs URL. --- ## What is Shotstack? - Markdown URL: https://shotstack.io/docs/guide/what-is-shotstack.md - Web URL: https://shotstack.io/docs/guide/ - Description: Shotstack is a video editor that runs in the cloud and is driven by software. AI agents, automations and apps send an edit, and Shotstack returns the finished video. # What is Shotstack? [Shotstack](https://shotstack.io/) is a video editor that runs in the cloud and is driven by software. AI agents, automations and apps send an edit, and Shotstack returns the finished video. An edit is a JSON description of a video: the clips, text, audio and effects on a timeline, and the output you want. You can write it by hand, build it in your code, or ask an AI agent to write it for you. ## One edit, everywhere The same edit moves between every part of Shotstack without conversion: - The **Edit API** renders it in the cloud and returns a video, image or audio file. - **Studio** opens it on a visual timeline, so a person can review and change it. - A **template** saves it with merge fields such as `{{NAME}}`, so you can render it again with new data. An agent can draft a video, a person can adjust it in Studio, and your app can render a version of it for every record in your data. ## Three ways in | Way in | For | How | | :-------------------- | :------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------- | | With an AI agent | Anyone using Claude, ChatGPT, Cursor or similar | The [MCP server](/agents/mcp-server) for chat clients and IDEs, the [CLI and skill](/agents/cli) for coding agents, or [Director](/agents/director) in Studio | | In code and workflows | Developers and automation builders | The [Edit API](/getting-started/hello-world-using-curl) and SDKs from your backend, or the [n8n, Make and Zapier apps](/architecting-an-application/automation-tools) | | Inside your product | Product teams | The [Studio SDK](/studio-sdk), a video editor you embed in your app | ## What the engine does - **Renders** edits in the cloud, in parallel, and tells you by webhook when each one is ready. - **Generates** video, images, voice-overs and music from prompts while the video renders. - **Ingests** your source files, transcodes them and transcribes speech with the Ingest API. - **Serves** finished files from a CDN, or delivers them to your own storage. ## Start here - [Make your first video with an AI agent](/getting-started/quickstart-agent), with no API key needed to preview. - [Make your first video with the API](/getting-started/hello-world-using-curl), using curl. - [Learn how an edit is structured](/getting-started/core-concepts). --- ## Edit Videos Using Code - Markdown URL: https://shotstack.io/docs/guide/getting-started/core-concepts.md - Web URL: https://shotstack.io/docs/guide/getting-started/core-concepts/ - Description: Learn the basics of how a video edit is put together using JSON # Edit Videos Using Code Shotstack is a cloud based [video editing API](https://shotstack.io). Every video starts as an edit: JSON that describes how the video should be put together. You can write the edit by hand, build it in your code or ask an [AI agent](/agents) to write it, and [Studio](/studio-sdk) shows the same edit on a visual timeline. The edit follows the principles of desktop editing software such as a timeline, tracks and clips. If you have used Adobe Premiere or After Effects, these concepts should be familiar. These core concepts and principles are described below: ### Edit An edit encompasses everything about the video that you want to create and is made of two components - the `timeline` which describes the assets \(video, images, text, audio and more\) that you want to use and how they are arranged \(edited\), and the `output` format \(mp4, gif, mp3, jpg, png or bmp\) and resolution \(a preset from preview to 4K, or a custom size\). ```json { "timeline": {...}, // the main video edit description "output": {...} // the output format and resolution } ``` :::info The Edit is the JSON payload that you POST to the _render_ endpoint to create your video. ::: ### Timeline The timeline represents the time that the video will play for, using seconds and starting from 0. A timeline is made up of `tracks` which are like layers that contain clips. ```json { "timeline": { "tracks": [{...}] // array of tracks }, ... } ``` ### Tracks Tracks are like layers within the timeline which span the entire length of the timeline, a track contains one or more `clips` Tracks can be visualized as layers that can be placed one on top of another. Clips on the top most track will obscure those on tracks below it. This can be useful for when you want to layer a title over a video clip or you want to fade between clips. Tracks also help to logically organize different types of content, for example a title track which plays over the top of a video and a watermark track that plays over the top of everything. Tracks are an array within the timeline, the first element is the top track and the last element is the bottom most track: ```json { "timeline": { "tracks": [ { ... // top track }, { ... // bottom track } ] }, ... } ``` ### Clips Clips are where you select the asset \(video, image, rich text and more\) you want to display within a track on the timeline, when it starts, how long it plays for and what effects, transitions and filters you want to apply. Clips are placed on a track at a specific `start` time, for example a clip with a `start` time of 3 will appear in the video after three seconds have played. Clips also have a `length` which determines how long the clip will play for in seconds. You can choose to set the `start` or `length` property automatically by setting it to `auto` or `end`. See the [smart clips](/docs/guide/architecting-an-application/smart-clips) section for all options. Each `asset` type has a specific set of options and features but you must specify the `type` of asset and also the `content` of the asset such as a URL `src` to a video or image file, or a `text` string for rich text. ```json { "timeline": { "tracks": [ { "clips": [ { // place an image at the very start of the track/timeline that plays for 4 seconds. "asset":{ "type": "image", "src": "https://s3-ap-southeast-2.amazonaws.com/my-bucket/photo.jpg", }, "start": 0, "length": 4 }, { // place a video that starts on the fourth second of the track/timeline ("start": 4) that has // the first two seconds trimmed ("trim": 2) and plays for 6 seconds ("length": 6). "asset":{ "type": "video", "src": "https://s3-ap-southeast-2.amazonaws.com/my-bucket/footage.mp4", "trim": 2 }, "start": 4, "length": 6 } ] }, ] }, ... } ``` :::danger Do not place clips on a track so that their start or end times overlap. This will result in the clips flickering because the API does not know which clip to display. ::: A clip can also have transitions, effects and filters applied to them. A clip may look something like this when more options are applied: ```json { "asset":{ "type": "video", "src": "https://s3-ap-southeast-2.amazonaws.com/my-bucket/footage.mp4", "trim": 2, "volume": 0.5 }, "start": 0, "length": 4, "transition": { "in": "fade", "out": "fade" }, "filter": "boost", "effect": "slideRight" } ``` From these basic building blocks you can assemble a sophisticated, reusable template. Replace assets and text with [merge fields](/architecting-an-application/merging-data), or add clips with loops, conditions and other code, to build a range of video based applications. --- ## Quickstart with an AI agent - Markdown URL: https://shotstack.io/docs/guide/getting-started/quickstart-agent.md - Web URL: https://shotstack.io/docs/guide/getting-started/quickstart-agent/ - Description: Make your first Shotstack video with Claude Code, Cursor, Codex CLI or another AI agent. Preview it in Studio with no API key, then render it when you're ready. # Quickstart with an AI agent Ask your AI agent for a video. It writes the edit, checks it and opens it in Studio, where you can play it and change anything by hand. You don't need an API key to preview. You need one to export the finished file. This guide uses the Shotstack CLI and skill with a coding agent in your terminal, such as Claude Code, Cursor, Codex CLI or Gemini CLI. Using Claude, ChatGPT or another chat client instead? [Connect the MCP server](/agents/mcp-server). It works the same way and shows Studio right in the conversation. ## 1. Give your agent the Shotstack skill You need [Node.js](https://nodejs.org/). Install the CLI and add the skill: ```bash npm install -g @shotstack/cli npx skills add shotstack/shotstack-cli ``` The skill teaches your agent how to write a valid edit and when to use each CLI command. Your agent reads it whenever you ask for a video. Already installed? Both are updated often, so update them first: `npm install -g @shotstack/cli@latest` and `npx skills update shotstack -y`. ## 2. Ask for a video Tell your agent what the video is for, where it will be shown and what it should contain. The more you say, the fewer questions it asks before it starts: ```text Make a 15-second vertical video for our spring sale: a bold headline, "30% off this week", three product photos and upbeat music. Use sample media for now, then open it in Studio. ``` Your agent writes the edit to a file, then runs two commands: ```bash shotstack validate edit.json # checks the edit offline shotstack studio edit.json # opens the edit at shotstack.studio ``` Neither command needs an API key, and neither uses credits. ## 3. Review it in Studio Studio opens in your browser with the edit on a timeline. Play it, move clips, change the text and swap images. Keep going there, or ask your agent for changes and it updates the same edit. ## 4. Render the video To export an MP4, [get your API keys](/getting-started/request-api-keys) and save one with `shotstack login`. Then ask your agent to render, or run: ```bash shotstack render edit.json --env stage --watch ``` Renders in the `stage` environment are free and carry a watermark. Use `--env v1` for production renders, which use credits. ## Next steps - **Make it reusable.** Ask your agent to save the edit as a template with merge fields, then render it with different data. See [From draft to production](/agents/draft-to-production). - **Generate the media.** Ask for an AI voice-over, music, images or clips. They're generated from prompts while the video renders. See [Generating assets with AI](/generating-assets/generative-ai). - **Choose how your agent connects.** See [AI agents](/agents). --- ## Hello World - Markdown URL: https://shotstack.io/docs/guide/getting-started/hello-world-using-curl.md - Web URL: https://shotstack.io/docs/guide/getting-started/hello-world-using-curl/ - Description: Create your first video in 5 minutes using the command line and Curl. Covers the render request, the render status values (queued, fetching, preprocessing, generating, rendering, saving, done, failed) and serving the video from the CDN. # Hello World This Hello World guide should provide you with a basic understanding of how quick and easy it is to create a simple video using styled rich-text, an audio track and a simple fade transition. Before you begin ensure you have registered and received your [API keys](./request-api-keys.md). ### Requirements This tutorial uses Curl to create your first 'Hello World' video. Curl is a cross platform command line utility that can be used to POST data to an API. ### Create the Hello World edit We'll use a text file to define the JSON data so that it is easy to edit and format. Open your preferred text editor and copy and paste the following JSON, save the file as **hello.json**: ```json { "timeline": { "tracks": [ { "clips": [ { "asset": { "type": "rich-text", "text": "HELLO WORLD", "font": { "family": "Montserrat", "color": "#ffffff", "size": 32 }, "align": { "horizontal": "left" } }, "start": 0, "length": 5, "transition": { "in": "fade", "out": "fade" } } ] }, { "clips": [ { "asset": { "type": "audio", "src": "https://s3-ap-southeast-2.amazonaws.com/shotstack-assets/music/moment.mp3", "effect": "fadeOut" }, "start": 0, "length": "end" } ] } ] }, "output": { "format": "mp4", "size": { "width": 1024, "height": 576 } } } ``` ### POST the edit to the Shotstack API You can now go ahead and POST the **hello.json** edit data to the API for rendering, to do this we'll use the **render** endpoint. From the same directory as the **hello.json** file, run the following Curl command: ```bash curl -X POST \ -H "Content-Type: application/json" \ -H "x-api-key: $SHOTSTACK_API_KEY" \ -d @hello.json \ https://api.shotstack.io/edit/stage/render ``` :::info Replace the **x-api-key** value with your own sandbox (stage) API key. ::: You should receive a 201 response code and payload similar to: ```json { "success":true, "message":"Created", "response":{ "message":"Render Successfully Queued", "id":"d2b46ed6-998a-4d6b-9d91-b8cf0193a655" } } ``` :::info Take a note of the **id**, it will be different from the example above, you will need this in the next step. You should keep track of **id** in your own database as there is currently no way to retrieve a list of render id's. ::: ### Poll the API to check the render status Your video is now either waiting in a queue to be rendered or is being processed. To check the status and know when rendering is complete and the video file is ready we need to poll the API. Using the **id** noted in the previous step and your own key, call the staging API using the following command: ```bash curl -X GET \ -H "Content-Type: application/json" \ -H "x-api-key: $SHOTSTACK_API_KEY" \ https://api.shotstack.io/edit/stage/render/d2b46ed6-998a-4d6b-9d91-b8cf0193a655 ``` :::info Replace the UUID at the end of the render URL with the **id** output in the previous step. ::: In less than one minute you should receive a 200 response code and payload similar to the output below. The status parameter tells you what stage the render is at, **done** means the video is ready for download. ```json { "success": true, "message": "OK", "response": { "id": "d2b46ed6-998a-4d6b-9d91-b8cf0193a655", "owner": "5ca6hu7s9k", "plan": "free", "status": "done", "url": "https://shotstack-api-stage-output.s3-ap-southeast-2.amazonaws.com/5ca6hu7s9k/d2b46ed6-998a-4d6b-9d91-b8cf0193a655.mp4", "data": { "output": { ... }, "timeline": { ... } }, "created": "2024-01-16T12:02:42.148Z", "updated": "2024-01-16T12:02:51.867Z" } } ``` :::caution Warning If the status is **failed** then there has been an error and the render will not continue. Attempt to resolve the issue based on the error message then try again. If the problem persists contact us for support. ::: #### Render status values Keep polling until the status is `done` or `failed`. The other values are in-progress stages. They can appear in a different order, or be skipped, depending on what the edit contains. | Status | Meaning | | :-------------- | :-------------------------------------------------------------- | | `queued` | The render is waiting to be rendered | | `fetching` | Assets are being fetched | | `preprocessing` | Video assets are being processed for compatibility | | `generating` | AI assets, for example speech from a prompt, are being generated | | `rendering` | The video is being rendered | | `saving` | The output file is being saved | | `done` | The render is finished and `url` points to the output file | | `failed` | The render stopped and `error` explains why | To avoid polling, you can use a [webhook](/docs/guide/architecting-an-application/webhooks) to be notified when the render is `done` or `failed`. ### Hosting and serving the rendered video When the status returned from polling equals **done**, then the video is ready for [hosting](/docs/guide/serving-assets/hosting) or downloading. By default all videos are automatically sent to our [video hosting and CDN service](/docs/guide/serving-assets/hosting). After a few seconds the video should be available at the following locations: | Staging | Production | | :--------------------------------- | :------------------------------ | | **https://cdn.shotstack.io/au/stage/** | **https://cdn.shotstack.io/au/v1/** | The video created in this guide would be available at: ``` https://cdn.shotstack.io/au/stage/5ca6hu7s9k/d2b46ed6-998a-4d6b-9d91-b8cf0193a655.mp4 ``` The URL is built from the `owner` and `id` values in the render status response, so you do not need another API call to get it. The [Serve API](/docs/guide/serving-assets/serve-api) is optional: use it if you need to confirm a hosted file has arrived, look up its size or delete it. ### Transferring the video to your own hosting service If you use your own hosting solution, you can [opt-out](/docs/guide/serving-assets/self-host) from the Shotstack hosting service and transfer the file to the location of your choice. Videos are stored temporarily for a 24 hour period at the following locations: | Staging | Production | | :------------------------------------------------------------------ | :------------------------------------------------------------------ | | **https://shotstack-api-stage-output.s3-ap-southeast-2.amazonaws.com/** | **https://shotstack-api-v1-output.s3-ap-southeast-2.amazonaws.com/** | Download or transfer the video using the URL returned in the get render status response: ```bash curl -O https://shotstack-api-stage-output.s3-ap-southeast-2.amazonaws.com/5ca6hu7s9k/d2b46ed6-998a-4d6b-9d91-b8cf0193a655.mp4 ``` :::warning Danger Do not serve the temporary URLs from your application as the file will be deleted after 24 hours. Check the [Hosting and Serving Assets](/docs/guide/serving-assets/hosting) guide for the correct way to serve assets generated by the video editing API. ::: ### Conclusion You will now have a basic understanding of the key steps needed to create a simple edit: 1. Prepare the edit data 2. POST the edit data to the API 3. Poll the API and wait for the render to complete 4. Serve the video from the Shotstack CDN 5. Optionally, download or transfer the video to your own storage --- ## Request API Keys - Markdown URL: https://shotstack.io/docs/guide/getting-started/request-api-keys.md - Web URL: https://shotstack.io/docs/guide/getting-started/request-api-keys/ - Description: Sign up to get sandbox and production API keys. New accounts get 10 free credits (10 minutes of production video with no watermark), valid for 30 days. No credit card needed. # Request API Keys [Sign up](https://app.shotstack.io/register) to the Shotstack service to generate API keys. Keys for both a staging sandbox and a live production environment are available in the dashboard. Find your keys in the menu under your account name (top right hand corner): ![API keys menu link](../../static/img/keys-dropdown.png) Click on the **API Keys** item in the menu to open the keys page: ![API keys dashboard page](../../static/img/api-keys-dashboard.png) Keys are a random 40 character string that look something like: ```text HBHjHylztJqjEeIFQxNJWB5g2mPa4lI40wX8oNVD ``` Staging sandbox and production URL's are: | Sandbox URL | Production URL | | :------------------------------| :-------------------------- | | https://api.shotstack.io/edit/stage | https://api.shotstack.io/edit/v1 | :::tip Free credits and the sandbox New accounts get **10 free credits, valid for 30 days**, and no credit card is needed to sign up. One credit is one minute of video, so that is 10 minutes of production video with no watermark. Use the sandbox environment for testing. Sandbox renders are free but watermarked, and [AI generation](/docs/guide/generating-assets/ai-generation-pricing#the-sandbox) is still charged. You need at least one credit in your balance to use the sandbox. ::: :::caution Using your production key will use up your credits and may result in being [charged](https://shotstack.io/pricing/). On pay-as-you-go there is no overage charge: when your credits run out, the API stops rendering until you buy more. Subscriptions keep rendering and bill any overage at the end of the billing cycle. See the [pricing FAQ](https://shotstack.io/pricing/). ::: --- ## AI agents - Markdown URL: https://shotstack.io/docs/guide/agents.md - Web URL: https://shotstack.io/docs/guide/agents/index/ - Description: Let your AI agent make videos with Shotstack. It writes the edit, you review it in Studio, and Shotstack renders it once or for every record in your data. # Make videos with AI agents Your agent writes the edit. Shotstack checks it, shows it in Studio, renders it and keeps it reusable. An AI agent can plan a video and write every clip, caption and transition as an edit. Shotstack gives it the rest: a check before anything renders, a browser editor where a person can review and change the draft, cloud rendering, and templates that run again with new data. New to this? Start with the [quickstart](/getting-started/quickstart-agent). :::tip For agents Before you start, update the Shotstack CLI and skill to their latest releases. They change often, and each release improves the rules and guidance you follow. See [Keep the CLI and skill current](/agents/cli#keep-the-cli-and-skill-current). The MCP server is hosted, so it's always current. ::: ## Choose how your agent connects | You use | Connect with | | :---------------------------------------------------------------------------- | :------------------------------------------------------------------- | | A chat client or IDE, such as Claude, ChatGPT, Cursor, VS Code or Windsurf | The [MCP server](/agents/mcp-server) | | A coding agent in a terminal, such as Claude Code, Codex CLI or Gemini CLI | The [CLI and skill](/agents/cli) | | Shotstack Studio | [Director](/agents/director), our agent inside Studio | | Your own app or agent | The Edit API as [a tool your model can call](/agents/your-own-agent) | Every route uses the same edit, so you can mix them. An agent can draft a video, a person can adjust it in Studio, and your app can render it. ## How it works 1. **Compose.** The agent reads the [authoring conventions](/agents/conventions) and writes the edit as JSON. 2. **Check.** The edit is checked against the schema before anything renders, offline and free. 3. **Review.** The edit opens in Studio: inline in chat clients, or as a link everywhere else. A person can play it and change anything. 4. **Render.** Shotstack renders a video, GIF, image or audio file in the cloud. 5. **Reuse.** Save the edit as a template with merge fields. See [From draft to production](/agents/draft-to-production). Your app renders it for every record, with no agent involved. ## What it costs - **Writing, checking and previewing** edits is free. - **Rendering** is free in the `stage` environment, with a watermark. Production renders use credits. See [Pricing](https://shotstack.io/pricing/). - **AI generation** is priced per model. Your agent can quote a generation before it runs. A prompt, model and options that haven't changed reuse the asset generated before at no charge, and a failed generation isn't charged. ## Authoring conventions An edit has rules every agent needs to follow: track order is reversed, asset types differ from what CSS suggests, and fonts come from a set list. The same [conventions guide](/agents/conventions) ships with the CLI skill and is returned by the MCP server's `get_shotstack_guide` tool. ## Plain text everywhere Every doc URL on `shotstack.io/docs/guide/` accepts a `.md` suffix and returns plain markdown. The [`/llms.txt`](https://shotstack.io/docs/guide/llms.txt) and [`/llms-full.txt`](https://shotstack.io/docs/guide/llms-full.txt) indexes give an LLM the full corpus in one fetch. --- ## MCP Server - Markdown URL: https://shotstack.io/docs/guide/agents/mcp-server.md - Web URL: https://shotstack.io/docs/guide/agents/mcp-server/ - Description: Connect Shotstack to Claude, ChatGPT, Cursor, VS Code, JetBrains, and other AI clients. # Shotstack MCP Server A [Model Context Protocol](https://modelcontextprotocol.io/) server that lets any MCP-aware AI client compose and render Shotstack videos from natural language. ``` Endpoint: https://mcp.shotstack.io/ (Streamable HTTP, OAuth or x-api-key header) ``` Get an API key at [app.shotstack.io](https://app.shotstack.io). ## Add it to your client ### Claude.ai · Claude Desktop Settings → **Connectors** → Add custom connector → URL `https://mcp.shotstack.io/` → complete OAuth. ### Claude Code ```sh claude mcp add --transport http shotstack https://mcp.shotstack.io ``` ### ChatGPT Settings → **Connectors** → Advanced → enable Developer mode → Create → URL `https://mcp.shotstack.io/`. Developer mode requires Business, Enterprise, or Edu. ### Cursor `~/.cursor/mcp.json`: ```json { "mcpServers": { "shotstack": { "url": "https://mcp.shotstack.io/" } } } ``` ### VS Code (GitHub Copilot) `.vscode/mcp.json` (VS Code 1.101+): ```json { "servers": { "shotstack": { "type": "http", "url": "https://mcp.shotstack.io/" } } } ``` ### Codex CLI ```sh codex mcp add shotstack --url https://mcp.shotstack.io ``` ### Gemini CLI ```sh gemini mcp add -s user --transport http shotstack https://mcp.shotstack.io/ ``` ### Windsurf `~/.codeium/windsurf/mcp_config.json`: ```json { "mcpServers": { "shotstack": { "serverUrl": "https://mcp.shotstack.io/" } } } ``` ### Zed `settings.json`: ```json { "context_servers": { "shotstack": { "url": "https://mcp.shotstack.io/" } } } ``` ### JetBrains IDEs Settings → **Tools** → MCP → Add → URL `https://mcp.shotstack.io/`. ### Goose `goose configure` → Add extension → **Remote Extension (Streaming HTTP)** → URL `https://mcp.shotstack.io/`. ### Raycast Extensions → MCP → Add server → URL `https://mcp.shotstack.io/`. ### Headless / CI (no browser) Skip OAuth by passing your API key as a header: ```sh claude mcp add --transport http shotstack https://mcp.shotstack.io --header "x-api-key: YOUR_KEY" ``` ### stdio-only clients (fallback) If your client only supports local stdio servers, bridge to the remote endpoint with [mcp-remote](https://github.com/geelen/mcp-remote): ```json { "mcpServers": { "shotstack": { "command": "npx", "args": ["-y", "mcp-remote", "https://mcp.shotstack.io/", "--header", "x-api-key:YOUR_KEY"] } } } ``` For scripting and automation outside a chat client, use the [Shotstack CLI](https://github.com/shotstack/shotstack-cli) instead. ## What it does | Tool | Purpose | |---|---| | `studio` | Mounts the inline Studio canvas in the chat. **Default for chat clients** — user clicks Render in the iframe. | | `render_video` | Submits an Edit JSON to the render API directly. Use for automation or when the user says "just render it". | | `get_render_status` | Polls a render. Returns `queued`/`fetching`/`rendering`/`saving`/`done`/`failed` and the output URL. | | `create_studio_link` | Generates a `https://shotstack.studio/s/{slug}` short link for sharing. | | `get_shotstack_guide` | Returns the Edit JSON authoring conventions. | | `create_template`, `update_template`, `list_templates`, `get_template`, `render_template`, `delete_template` | Manage reusable templates with `{{PLACEHOLDER}}` merge fields. | | `list_models` | Lists the AI generation models and whether this key can use each one, with each model's options schema. | | `quote_generation` | Returns the credits a generation would cost, without charging. | | `generate_asset` | Generates one image, video or audio asset from a prompt. | | `get_generation_status` | Polls a generation and returns the asset URL when it's ready. | ## How agents should use it 1. **Call `get_shotstack_guide` before composing Edit JSON.** Track ordering, asset types, fonts, and the top-5 mistakes are non-negotiable. The full ruleset is at [`/agents/conventions`](/agents/conventions). 2. **Default to `studio`, not `render_video`.** Mount the edit inline so the user can preview and click Render. Only call `render_video` directly when explicitly asked or when there's no human in the loop. 3. **Use public HTTPS URLs for assets.** Never hallucinate URLs; ask the user when unsure. 4. **Quote before generating.** Inside an edit, give an asset a `prompt` and `model` and it generates at render time. On its own, call `list_models`, then `quote_generation`, and get the user's yes before `generate_asset`. --- ## CLI + Skill - Markdown URL: https://shotstack.io/docs/guide/agents/cli.md - Web URL: https://shotstack.io/docs/guide/agents/cli/ - Description: Use Shotstack from a terminal. The CLI and its skill let coding agents write, check, preview and render videos. # Shotstack CLI [![View on GitHub](https://img.shields.io/badge/GitHub-View_on_GitHub-blue?logo=github)](https://github.com/shotstack/shotstack-cli) [![npm version](https://img.shields.io/npm/v/@shotstack/cli.svg)](https://www.npmjs.com/package/@shotstack/cli) A terminal-native CLI for the Shotstack Edit API plus a [Claude Code Skill](https://docs.claude.com/en/docs/claude-code/skills) packaging the same conventions for IDE-based coding agents. ## Install ```sh npm install -g @shotstack/cli npx skills add shotstack/shotstack-cli ``` The skill works in any agent that supports [Agent Skills](https://docs.claude.com/en/docs/claude-code/skills), including Claude Code, Cursor, Codex CLI and Gemini CLI. ## Keep the CLI and skill current The CLI and skill are updated often. Each release improves the authoring rules, motion guidance and caption presets your agent follows, and that shows in the videos it makes. Check for a new release before you start: ```sh shotstack --version # the version you have npm view @shotstack/cli version # the latest release npm install -g @shotstack/cli@latest # update the CLI npx skills update shotstack -y # update the skill ``` ## Try it without an API key Writing, checking and previewing an edit needs no key and no credits: ```sh shotstack validate edit.json # offline: schema, overlapping clips, fonts and media URLs shotstack studio edit.json # opens the edit at https://shotstack.studio/s/abc12345 ``` New to this? Follow the [quickstart](/getting-started/quickstart-agent). ## Add your API key Rendering, generating, ingesting and templates need a key. Get one at [app.shotstack.io](https://app.shotstack.io), then: ```sh shotstack login # prompts for your API key, saves it for future sessions ``` `shotstack login` stores the key per environment in `~/.shotstack/credentials.json` (chmod 600). Prefer env vars for CI? `export SHOTSTACK_API_KEY=...` works too and takes precedence over the stored key. Renders in the `stage` environment are free and watermarked. ## Commands ```sh # Render an Edit JSON and poll until done. shotstack render edit.json --watch # → done https://shotstack-api-...-e21df7168d9a.mp4 # Poll an existing render. shotstack status --watch # Save an edit as a template, then list, fetch, update or delete templates. shotstack template create edit.json --name "Product promo" shotstack template list shotstack template get # e.g. a template a person designed in Studio shotstack template update edit.json shotstack template delete # List generation models, or print the options one accepts. shotstack models shotstack models # Generate one asset from a prompt. --quote prints the cost without charging. shotstack generate "" --quote shotstack generate "" --watch # Bring your own local files into a render via the Ingest API. shotstack ingest upload ./clip.mp4 --watch # → ready https://shotstack-ingest-api-v1-sources.s3.../source.mp4 shotstack ingest fetch https://example.com/v.mp4 --watch shotstack ingest list --output json ``` Every command accepts `--env stage|v1` (defaults to `v1`) and `--output json` for machine-readable output. Full reference: the [CLI README](https://github.com/shotstack/shotstack-cli#commands). ## Five CLI rules for agents 1. **Pipe → `--output json`.** Default output is human-readable; pass `--output json` when scripting. 2. **Use `--watch`, not a polling loop.** `render`, `status`, and `ingest upload|fetch|status` accept `--watch` and exit on terminal state. 3. **Validate before rendering.** Run `shotstack validate ` to catch schema errors offline — no API call, no credits. 4. **Fetch the schema before composing JSON.** Pull [`api.edit.json`](https://shotstack.io/docs/api/api.edit.json) and the [conventions](/agents/conventions) — LLM training data is often stale. 5. **Hand off to a human via `studio` when uncertain.** Don't burn render credits iterating; let a human click Render after reviewing. Exit codes: `0` success, `1` permanent error, `2` transient (safe to retry). ## The skill The CLI repo ships a [SKILL.md](https://github.com/shotstack/shotstack-cli/blob/master/skills/shotstack/SKILL.md) that teaches your agent how to use the CLI and how to author Edit JSON correctly. Your agent reads it on demand, and its `references/` (timeline layering, caption presets, SVG constraints, fonts, asset library, troubleshooting) load only when relevant. ## Conventions are shared with the MCP server The skill's `shared/agent-core.md` is the same content the [MCP server](/agents/mcp-server) returns from its `get_shotstack_guide` tool, and the same content rendered at [`/agents/conventions`](/agents/conventions). ## Reference - [CLI on GitHub](https://github.com/shotstack/shotstack-cli) — full command reference, changelog, source - [CLI on npm](https://www.npmjs.com/package/@shotstack/cli) — current version, install - [Edit JSON conventions](/agents/conventions) — track ordering, asset types, font allowlist - [Edit API schema](https://shotstack.io/docs/api/api.edit.json) — `api.edit.json`, the source of truth for the JSON shape - [Ingest API guide](/ingesting-footage/ingest-api/) — what `shotstack ingest` wraps - [Edit API reference](https://shotstack.io/docs/api/) — full REST API for the underlying service --- ## Director - Markdown URL: https://shotstack.io/docs/guide/agents/director.md - Web URL: https://shotstack.io/docs/guide/agents/director/ - Description: Director is Shotstack's own AI agent, built into Studio in your dashboard. Describe a video and it builds and edits the timeline with you. # Director Director is Shotstack's own AI agent, built into [Studio](https://app.shotstack.io/studio) in your Shotstack dashboard. Describe the video you want in the chat panel and Director builds it on the timeline. Ask for changes in plain words, or edit by hand. Both work on the same edit. ## Same edit as every other agent Director writes the same Edit JSON as the [MCP server](/agents/mcp-server) and the [CLI](/agents/cli), so its work isn't locked to Studio: - Render it through the Edit API like any other edit. - Save it as a [template](/architecting-an-application/templates) and render it with new data from your app. - Pick it up with your own agent: `shotstack template get ` fetches a template designed in Studio. ## When to use Director Use Director when you want an agent without setting one up: no client to connect and no skill to install. Use the MCP server or the CLI when you want your own agent, such as Claude, ChatGPT or Cursor, to do the work inside the tools you already use. --- ## Your own agent - Markdown URL: https://shotstack.io/docs/guide/agents/your-own-agent.md - Web URL: https://shotstack.io/docs/guide/agents/your-own-agent/ - Description: Give your own AI agent a Shotstack render tool. Connect it to the MCP server, or define one tool that sends an edit to the Edit API. # Use Shotstack in your own agent If you're building your own agent, give it Shotstack as a tool. There are two ways. ## Connect the MCP server If your agent framework supports MCP, connect it to `https://mcp.shotstack.io/` and pass your API key in an `x-api-key` header. Your agent gets every Shotstack tool, including Studio previews, templates and AI generation. See [MCP server](/agents/mcp-server) for the tools and how agents should use them. Building in n8n? The Shotstack node works as a tool for n8n's AI Agent node. See [Automation tools](/architecting-an-application/automation-tools#with-ai-agents). ## Define a render tool Otherwise, describe one tool that takes an edit, load the [agent guide](https://github.com/shotstack/shotstack-cli/blob/master/skills/shotstack/shared/agent-core.md) into the system prompt, and call the Edit API when the model uses the tool. This example uses the Anthropic Python SDK. The same pattern works with OpenAI function calling or any framework that supports tools. ```python import os import time import anthropic import requests API = "https://api.shotstack.io/edit/stage" # stage renders are free and watermarked HEADERS = {"x-api-key": os.environ["SHOTSTACK_API_KEY"]} # The guide the CLI skill and MCP server use. Fetching it each run picks up new authoring rules as they ship. guide = requests.get( "https://raw.githubusercontent.com/shotstack/shotstack-cli/master/skills/shotstack/shared/agent-core.md" ).text render_tool = { "name": "render_video", "description": "Render a Shotstack edit and return the URL of the finished video.", "input_schema": { "type": "object", "properties": {"edit": {"type": "object", "description": "A Shotstack Edit JSON with a timeline and output."}}, "required": ["edit"], }, } def render_video(edit): render_id = requests.post(f"{API}/render", json=edit, headers=HEADERS).json()["response"]["id"] while True: render = requests.get(f"{API}/render/{render_id}", headers=HEADERS).json()["response"] if render["status"] in ("done", "failed"): return render.get("url") or render.get("error", "Render failed") time.sleep(5) client = anthropic.Anthropic() messages = [{"role": "user", "content": "Make a 10-second video announcing our spring sale."}] while True: reply = client.messages.create( model="claude-sonnet-5-5", max_tokens=8000, system=guide, tools=[render_tool], messages=messages, ) messages.append({"role": "assistant", "content": reply.content}) if reply.stop_reason != "tool_use": break messages.append({ "role": "user", "content": [ {"type": "tool_result", "tool_use_id": block.id, "content": render_video(block.input["edit"])} for block in reply.content if block.type == "tool_use" ], }) print(reply.content[-1].text) ``` Polling is the simplest way to wait for a render. In production, set a [webhook](/architecting-an-application/webhooks) instead. ## Next steps - Once the video is right, save it as a template so your app can render it without the agent. See [From draft to production](/agents/draft-to-production). - Give the agent more tools for templates and AI generation, or use the MCP server, which has them already. --- ## From draft to production - Markdown URL: https://shotstack.io/docs/guide/agents/draft-to-production.md - Web URL: https://shotstack.io/docs/guide/agents/draft-to-production/ - Description: Turn an agent's draft into a template your app renders for every record, with no agent in the loop. # From draft to production An agent is most useful while you design a video. Once the design is right, save it as a template, and your app can render it for every product, listing or customer without calling a model. Each video follows the design you approved. ## 1. Draft with an agent Ask your agent for the video and review it in Studio until it's right. See the [quickstart](/getting-started/quickstart-agent). ## 2. Mark what changes per video Ask your agent to replace the parts that change with merge fields, such as `{{ADDRESS}}` or `{{PHOTO}}`: ```json { "asset": { "type": "rich-text", "text": "{{ADDRESS}}" }, "start": 0, "length": 5 } ``` A merge field can go in any string: text, image and video URLs, colours, and the prompts of [AI-generated assets](/generating-assets/generative-ai). See [Merging Data](/architecting-an-application/merging-data). ## 3. Save it as a template | From | How | | :------------ | :---------------------------------------------------------- | | An MCP client | Ask your agent. It uses the `create_template` tool. | | The CLI | `shotstack template create edit.json --name "Listing video"` | | Your code | `POST /templates` with a `name` and the edit as `template` | Each returns the template's ID. ## 4. Render it for every record Send one request per record to `POST /templates/render`, with the template ID and that record's values: ```json { "id": "f5493c17-d01f-445c-bb49-535fae65f219", "merge": [ { "find": "ADDRESS", "replace": "12 Harbour St" }, { "find": "PHOTO", "replace": "https://example.com/photos/12-harbour-st.jpg" } ] } ``` - **Send every field.** The `merge` array in the request replaces any default values saved in the template. - **Get a webhook.** Save the template with a `callback` URL in its edit, and every render of it posts to that URL when it finishes. See [Webhooks](/architecting-an-application/webhooks). - **Renders run in parallel**, so a batch doesn't wait on itself. The n8n, Make and Zapier apps render templates the same way, with your data mapped to merge fields. See [Automation tools](/architecting-an-application/automation-tools). ## 5. Change the design without changing your code Open the template in Studio, or ask your agent to change it: `shotstack template get `, edit, then `shotstack template update edit.json`. Your app keeps sending the same request, and the next render uses the new design. Updating replaces the template, so try changes on a copy first. ## When the agent stays in the loop Some videos need a different story every time. Then your agent writes a new edit for each request and renders it directly, with the MCP server's `render_video` tool or `shotstack render`. Use this when no single template fits, and plan for a model call on every video. --- ## Edit JSON conventions for AI - Markdown URL: https://shotstack.io/docs/guide/agents/conventions.md - Web URL: https://shotstack.io/docs/guide/agents/conventions/ - Description: The authoring rules every agent needs to compose valid Shotstack Edit JSON — track ordering, asset types, fonts, and the top 5 mistakes. # Shotstack Agent Core Universal Edit JSON authoring conventions. Read this **before composing any Edit JSON**. The conventions agents most often get wrong are listed here once. The same content ships with the [Shotstack CLI Skill](/agents/cli) and the [Shotstack MCP server](/agents/mcp-server) — `get_shotstack_guide` returns this exact text. ## Before composing JSON: check the schema Don't invent property names or enum values. The Shotstack schema is published — fetch one of these before composing JSON from scratch: - [`shotstack.io/docs/api/api.edit.json`](https://shotstack.io/docs/api/api.edit.json) — single-file OpenAPI Schema. Machine-validatable; load it once and validate locally instead of round-tripping the API. - [`shotstack.io/docs/api/`](https://shotstack.io/docs/api/) — interactive HTML reference. Fastest for human scanning. - [`shotstack.io/docs/guide/llms-full.txt`](https://shotstack.io/docs/guide/llms-full.txt) — single-file LLM-friendly version of the full guide + reference. - [`github.com/shotstack/oas-api-definition`](https://github.com/shotstack/oas-api-definition/tree/main/schemas) — raw OpenAPI YAML, source of truth. CSS naming conventions (`alignment`, `vertical: "center"`) **do not** apply. The spec uses precise names that often differ from web/CSS instincts: | You'd guess (wrong) | API uses (right) | |---|---| | `alignment` | `align` | | `align.vertical: "center"` | `align.vertical: "middle"` | | `font.name` | `font.family` | | `duration` | `length` | | `transitions: [...]` (array) | `transition: { in, out }` (object) | | `fit: "cover"` (CSS instinct: scale+crop maintaining aspect) | `fit: "crop"` — Shotstack's `cover` STRETCHES without maintaining aspect ratio | When the API rejects a property, the error message names the field — fix and retry. Don't guess twice. ## Track ordering is REVERSED `timeline.tracks` is an array. **The first element (`tracks[0]`) is the TOP layer; the last element is the BOTTOM layer.** This is opposite to most z-index conventions. > "Tracks are layered on top of each other in the same order they are added to the array with the top most track layered over the top of those below it." — [Shotstack docs](/architecting-an-application/guidelines) Practical rule: **put captions, overlays, and titles in early tracks; put video/image backgrounds in later tracks.** ```json { "timeline": { "tracks": [ { "clips": [/* TOP — captions */] }, { "clips": [/* MIDDLE — title overlay */] }, { "clips": [/* BOTTOM — background video */] } ] } } ``` ## Smart clip strings These string values are accepted in addition to numbers: | Where | Value | Meaning | |---|---|---| | `clip.start` | `"auto"` | Start when the previous clip on the same track finishes | | `clip.start` | `"alias://"` | Inherit start from another clip with `alias: ""` | | `clip.length` | `"auto"` | Asset's natural duration. Use for foreground (video, voiceover, scene). | | `clip.length` | `"end"` | Until timeline ends, capped at asset duration. Use for background (music, captions, watermark). | | `clip.length` | `"alias://"` | Inherit length from another clip | `"end"` does NOT loop short audio — use a numeric `length` if you need precise control. The `alias://` protocol is also used in `rich-caption` `src` to auto-transcribe a referenced audio/video clip — see the [caption reference](https://github.com/shotstack/shotstack-cli/blob/master/skills/shotstack/references/caption.md). ## Public HTTPS URLs only All asset `src` URLs must be publicly accessible HTTPS. **No local file paths, no `data:` URIs, no signed URLs that expire mid-render.** The render workers fetch assets from the public internet. For test renders without your own assets, use the placeholder media listed in the [asset library reference](https://github.com/shotstack/shotstack-cli/blob/master/skills/shotstack/references/asset-library.md). ## Don't overlap clips on the same track Clips on the same track must not have overlapping `start`/`length` ranges. Overlapping clips flicker because the engine can't decide which to display. Put parallel content on separate tracks. ## Output resolution Pick `output.resolution` (preset) OR `output.size.width`+`output.size.height` (custom): | Preset | Pixels @ fps | |---|---| | `preview` | 512×288 @ 15 | | `mobile` | 640×360 @ 25 | | `sd` | 1024×576 @ 25 | | `hd` | 1280×720 @ 25 (default) | | `1080` | 1920×1080 @ 25 | Custom sizes must be divisible by 2. ## Asset types Use only the **current** asset types; the deprecated ones still parse but should not be used in new templates. ### Current | Type | Purpose | |---|---| | `video` | Video file (mp4, mov, webm), or generated from a `prompt`. | | `image` | Static image (jpg, png), or generated from a `prompt`. | | `audio` | Audio clip placed at a specific time on the timeline, or speech or music generated from a `prompt`. | | `rich-text` | Styled text overlay with full typography control. **Use this instead of `text`/`html`/`title`.** | | `svg` | Vector graphics from raw SVG markup. See the [SVG reference](https://github.com/shotstack/shotstack-cli/blob/master/skills/shotstack/references/svg.md). | | `rich-caption` | Word-level animated captions sourced from audio, video, or subtitle files. See the [caption reference](https://github.com/shotstack/shotstack-cli/blob/master/skills/shotstack/references/caption.md). | | `luma` | Luma matte for masking effects. | `timeline.fonts[]` is a separate field for custom font URLs (not an asset type). For background music, **use an `audio` asset on its own track** with `length: "end"`. Do NOT use `timeline.soundtrack` — it is deprecated. The audio asset path supports keyframes, custom timing, fades, and effects; soundtrack does not. ### Deprecated — do not use `text`, `title`, `caption`, `html`, `shape`, `text-to-image`, `text-to-speech`, `image-to-video`. They still parse but produce inferior output. Replace with: | If you'd use… | Use instead | |---|---| | `text` or `title` | `rich-text` | | `caption` | `rich-caption` | | `html` | `rich-text` | | `shape` | `svg` with ``, ``, `` etc. | | `timeline.soundtrack` | `audio` asset on its own track with `length: "end"` | | `text-to-image` | `image` with `prompt` and `model` | | `text-to-speech` | `audio` with the text as `prompt`, a speech `model`, and `options.voice` | | `image-to-video` | `video` with `prompt`, an image-to-video `model`, and the image as `options.startSrc` | ### AI-generated assets Set `prompt` and `model` on a `video`, `image` or `audio` asset to generate it; `options` configures the model. `GET /models` lists the models and their options, and `POST /generate/quote` prices an asset before you generate it. Generation is charged in credits **even through the sandbox stage endpoint**, where rendering is otherwise free. An identical asset that was generated before is reused at no charge. Renders with assets to generate wait for them, so they take longer. See [Generating Assets with AI](/docs/guide/generating-assets/generative-ai). ## Fonts Prefer **custom Google Fonts via `timeline.fonts[]`** over the built-in font list. The Studio SDK exposes ~400 Google Fonts. See the [fonts reference](https://github.com/shotstack/shotstack-cli/blob/master/skills/shotstack/references/fonts.md) for the URL pattern and the built-in fallback list. Per the [rich-text docs](/architecting-an-application/rich-text), custom fonts give you the full Google Fonts catalogue with predictable rendering. System fonts like `Arial`, `Helvetica`, and `Times New Roman` are NOT installed; text set in them renders in Roboto, with no render error. ```json { "timeline": { "fonts": [ { "src": "https://fonts.gstatic.com/s/montserrat/v31/JTUSjIg1_i6t8kCHKm45xW5rygbi49c.ttf" } ], "tracks": [ { "clips": [{ "asset": { "type": "rich-text", "text": "Hello", "font": { "family": "JTUSjIg1_i6t8kCHKm45xW5rygbi49c", "size": 60, "color": "#ffffff" } }, "start": 0, "length": 3 }] } ] } } ``` Use the **font file basename** (without `.ttf`/`.otf`) as `font.family`, matched ignoring case. The family name stored inside the font file also matches, but it isn't always the name on Google Fonts (Space Grotesk's file calls itself `Space Grotesk Light`), so the filename is the safe choice. A `font.family` that matches nothing renders in Roboto with no error. The exception is a lone entry in `timeline.fonts[]`, which is used whatever `font.family` says, so a wrong name only shows once a second font is added. `shotstack validate` warns on any `font.family` that isn't a built-in or a loaded filename. ## Top 5 mistakes 1. **Reverse track order.** `tracks[0]` is the TOP layer, not the bottom. Captions go in early tracks; backgrounds go in late tracks. 2. **System fonts.** `Arial`, `Helvetica`, `Times New Roman`, etc. are not installed. Use Google Fonts via `timeline.fonts[]` (preferred) or one of the built-in fonts. 3. **Captions fill the whole frame.** A `rich-caption` clip without `width`, `height`, and `fit: "none"` covers the entire output. Use a named preset. 4. **`` inside an SVG asset.** Raw `` is unsupported. Use a `rich-text` asset for any text content; reserve SVG for shapes only. 5. **Composing custom caption styles when presets exist.** The five named presets (Nico, Kai, Kapow, Lovely Little Lychee, Rizz) cover the common styles. Use one verbatim unless the user asks for something specific. ## Per-topic deep dives For details beyond this core guide (rich-caption presets, SVG constraints, full font URL list, troubleshooting), see the [`references/` directory](https://github.com/shotstack/shotstack-cli/tree/master/skills/shotstack/references) in the Shotstack CLI repo, or fetch the topic-specific docs from [`/llms-full.txt`](https://shotstack.io/docs/guide/llms-full.txt). --- ## Editing Videos, Images and Audio - Markdown URL: https://shotstack.io/docs/guide/architecting-an-application/guidelines.md - Web URL: https://shotstack.io/docs/guide/architecting-an-application/guidelines/ - Description: Guidelines on how to create an application incorporating the Shotstack API. # Editing Videos, Images and Audio Shotstack provides a framework to automate the editing process for your videos, images and audio and the heavy lifting of rendering thousands of videos concurrently. Shotstack is useful is in building media based applications and automating workflows where a template can be used and assets created in batches or large volumes. ## Key steps to editing media ### Prepare JSON using the Studio An edit in Shotstack is defined using JSON which specifies when different assets (images, videos, titles) will appear and play on a timeline. In order to create a video you first need to prepare the JSON data so that it contains the correct assets arranged on the timeline. You can use the Shotstack Studio to help create this JSON data. ### Post JSON to API render endpoint Once your have prepared an edit using the Studio you POST the data to the _**render** endpoint of our API. ### Poll render endpoint The render process takes approximately 10 seconds for a 30-second video. Your application needs to poll the API on a regular basis to check the status of the video render by calling the _**render**_ endpoint with the **id** of the current render. When the status is `done` you can proceed to downloading or transferring the video to your application. :::info Tip We recommend [using webhooks](webhooks.md) to reduce the number of calls to the API and receive a notification as soon as rendering completes. ::: ### Hosting and serving video Once a video is finished rendering, it is available at a temporary URL as well as automatically transferred to the [Shotstack hosting service](/docs/guide/serving-assets/hosting). ### Download or transfer the video Instead of using the Shotstack hosting service, the video can be fetched from our servers \(S3\) and transferred to your own application for further processing and hosting. You may wish to transfer the video to your own S3 or storage service, upload it Google Drive, Vimeo or any of our other destinations. :::danger Warning If you are not using our hosting service, we will store the video on our servers for 24 hours only. After that time they will be deleted. ::: --- ## Rich Text - Markdown URL: https://shotstack.io/docs/guide/architecting-an-application/rich-text.md - Web URL: https://shotstack.io/docs/guide/architecting-an-application/rich-text/ - Description: Create advanced animated text with gradients, shadows, and effects # Rich Text The rich-text asset provides advanced text rendering capabilities with support for animations, gradients, shadows, strokes, and custom styling. It's ideal for creating professional titles, animated text effects, and stylized typography. ## Common rich-text patterns Before diving into individual properties, here are solutions to common text needs: ### Animated title Create an eye-catching title with a word-by-word ascend animation, a custom font and a linear gradient: ```json { "timeline": { "fonts": [ { "src": "https://shotstack-assets.s3.amazonaws.com/fonts/Oswald-VariableFont.ttf" } ], "tracks": [ { "clips": [ { "asset": { "type": "rich-text", "text": "Evening Night", "font": { "family": "Oswald", "size": 125, "weight": 900 }, "style": { "letterSpacing": 8, "gradient": { "type": "linear", "stops": [ { "offset": 0, "color": "#005AA7" }, { "offset": 1, "color": "#FFFDE4" } ] } }, "animation": { "preset": "ascend", "duration": 1 } }, "start": 0, "length": 5, "width": 1000, "height": 300 } ] } ] }, "output": { "format": "mp4", "size": { "width": 1280, "height": 720 } } } ``` ### Bold call-to-action text Create impactful text with stroke outline and shadow for maximum visibility: ```json { "timeline": { "background": "#000000", "fonts": [ { "src": "https://shotstack-assets.s3.amazonaws.com/fonts/SpecialElite-Regular.ttf" } ], "tracks": [ { "clips": [ { "asset": { "type": "rich-text", "text": "SUBSCRIBE NOW", "font": { "family": "Special Elite", "size": 64, "weight": 800, "color": "#ffd700" }, "style": { "letterSpacing": 4, "textTransform": "uppercase" }, "stroke": { "width": 4, "color": "#000000", "opacity": 1 }, "shadow": { "offsetX": 6, "offsetY": 6, "blur": 12, "color": "#000000", "opacity": 0.7 }, "background": { "color": "#FF5962", "borderRadius": 25 }, "animation": { "preset": "shift", "duration": 2, "style": "character", "direction": "up" }, "align": { "horizontal": "center" } }, "start": 0, "length": 5, "width": 800, "height": 200 } ] } ] }, "output": { "format": "mp4", "size": { "width": 1280, "height": 720 } } } ``` ### Typewriter subtitle effect Create text that appears character by character: ```json { "timeline": { "background": "#000000", "fonts": [ { "src": "https://shotstack-assets.s3.amazonaws.com/fonts/SpecialElite-Regular.ttf" } ], "tracks": [ { "clips": [ { "asset": { "type": "rich-text", "text": "Let me tell you a story...", "font": { "family": "SpecialElite-Regular", "size": 42, "color": "#ffffff" }, "animation": { "preset": "typewriter", "duration": 1.5, "style": "character" } }, "start": 0, "length": 5, "width": 900, "height": 150 } ] } ] }, "output": { "format": "mp4", "size": { "width": 1280, "height": 720 } } } ``` ## Basic usage A simple rich-text element requires only text, dimensions, and font properties: ```json { "asset": { "type": "rich-text", "text": "Welcome to Shotstack", "font": { "family": "Roboto", "size": 48, "color": "#ffffff" } }, "start": 0, "length": 5, "width": 800, "height": 400 } ``` This creates white Roboto text at 48px within an 800×400 pixel container. The `width` and `height` define the text's maximum area, and text is centered by default. The clip appears at the beginning of the timeline and lasts 5 seconds. ## Fonts The font properties control the core typography of your text. Choose from built-in fonts or load custom fonts to match your brand. Font styling is broken down into six properties: 1. `font` 2. `style` 3. `stroke` 4. `shadow` 5. `align` 6. `animation` ### Font properties Customize the appearance of your text with font styling options: ```json { "asset": { "type": "rich-text", "text": "Styled Text", "font": { "family": "Roboto", "size": 64, "weight": 800, "color": "#ff6b6b", "opacity": 0.9 } }, "start": 0, "length": 5, "width": 800, "height": 400 } ``` This creates bold text using Roboto (weight 800) at 64px with a coral red color at 90% opacity. **Font options:** - `family` - Font name (default: "Roboto") - `size` - Font size in pixels, 12-200 (default: 48) - `weight` - Font weight: 100-900 or "normal", "bold" (default: "400") - `color` - Hex color code (default: "#ffffff") - `opacity` - Text opacity, 0-1 (default: 1) ### Available fonts Built-in fonts: - Roboto - Montserrat - Open Sans - Work Sans - Uni Neue - Arapey - Clear Sans - Didact Gothic - MovLette - Permanent Marker - Sue Ellen Francisco ### Custom fonts Use your own fonts by providing a URL to an OTF or TTF font file: ```json { "timeline": { "background": "#FFD166", "fonts": [ { "src": "https://shotstack-assets.s3.amazonaws.com/fonts/PressStart2P-Regular.ttf" } ], "tracks": [ { "clips": [ { "asset": { "type": "rich-text", "text": "GAME OVER", "font": { "family": "Press Start 2P", "size": 60, "color": "#EF476F" } }, "start": 0, "length": 5, "width": 1000, "height": 400 } ] } ] }, "output": { "format": "mp4", "size": { "width": 1280, "height": 720 } } } ``` ![custom fonts example](/img/rich-text/image.png) :::info The font file must be publicly accessible. The `family` property can be set using either: 1. **The font's embedded name** (recommended) — use the font's real family name as stored in the font file, e.g. `"Press Start 2P"` for `PressStart2P-Regular.ttf`. This is the name you would see when installing the font on your computer. 2. **The filename** — use the filename without the extension, e.g. `"PressStart2P-Regular"` for `PressStart2P-Regular.ttf`. ::: ### International fonts Shotstack supports everything from Arabic calligraphy to Devanagari ligatures, as long as the font provides the necessary glyphs. #### Hindi ```json { "timeline": { "background": "#015C37", "fonts": [ { "src": "https://shotstack-assets.s3.amazonaws.com/fonts/Hind-Regular.ttf" } ], "tracks": [ { "clips": [ { "asset": { "type": "rich-text", "text": "जहाँ चाह, वहाँ राह।", "font": { "family": "Hind", "size": 60, "color": "#FFFFFF" } }, "start": 0, "length": 5, "width": 1000, "height": 400 } ] } ] }, "output": { "format": "mp4", "size": { "width": 1280, "height": 720 } } } ``` ![Hindi text example](/img/rich-text/hindi-font.png) #### Chinese (Traditional) ```json { "timeline": { "background": "#CD071E", "fonts": [ { "src": "https://shotstack-assets.s3.amazonaws.com/fonts/NotoSansTC-VariableFont_wght.ttf" } ], "tracks": [ { "clips": [ { "asset": { "type": "rich-text", "text": "千里之行,始于足下。", "font": { "family": "NotoSansTC-VariableFont_wght", "size": 75, "color": "#FABC3C" } }, "start": 0, "length": 5, "width": 1000, "height": 400 } ] } ] }, "output": { "format": "mp4", "size": { "width": 1280, "height": 720 } } } ``` ![Simplified Chinese example](/img/rich-text/chinese-font.png) ## Text styling Beyond fonts, text styling properties control spacing, transformations, and decorations. These properties help create modern, spacious layouts and emphasize important text. ### Letter spacing and line height Adjust text layout with spacing and transformation options: ```json { "timeline": { "tracks": [ { "clips": [ { "asset": { "type": "rich-text", "text": "Spaced Out Text", "font": { "family": "Roboto", "size": 48, "color": "#ffffff" }, "style": { "letterSpacing": 35, "lineHeight": 1.5, "textTransform": "uppercase", "textDecoration": "underline" } }, "start": 0, "length": 5, "width": 1000, "height": 400 } ] } ] }, "output": { "format": "mp4", "size": { "width": 1280, "height": 720 } } } ``` ![letter spacing and line height example](/img/rich-text/image-2.png) This adds 35 pixels between characters, increases line height to 1.5× the font size (more space between lines), converts text to uppercase, and adds an underline. **Style options:** - `letterSpacing` - Space between characters in pixels (default: 0) - `lineHeight` - Relative line height, 0-10 (default: 1.2) - `textTransform` - "none", "uppercase", "lowercase", "capitalize" (default: "none") - `textDecoration` - "none", "underline", "line-through" (default: "none") ## Gradients Gradients add visual interest by transitioning between colors. Use linear gradients for modern, directional effects, or radial gradients for spotlight and glow effects. Gradients work best with bold, large text and override the font color property. ### Linear gradient Create text with a smooth color transition from one color to another: ```json { "timeline": { "tracks": [ { "clips": [ { "asset": { "type": "rich-text", "text": "MILKSHAKE", "font": { "family": "Montserrat", "size": 125, "weight": 800 }, "style": { "gradient": { "type": "linear", "angle": 45, "stops": [ { "offset": 0, "color": "#C6FFDD" }, { "offset": 0.5, "color": "#FBD786" }, { "offset": 1, "color": "#f7797d" } ] } } }, "start": 0, "length": 5, "width": 900, "height": 400 } ] } ] }, "output": { "format": "mp4", "size": { "width": 1280, "height": 720 } } } ``` ![three-color gradient transition](/img/rich-text/image-1.png) A three-color gradient transition at a 45-degree diagonal. The `angle` controls the direction (0° is left to right, 90° is top to bottom), and `stops` define colors at specific positions (0 is start, 0.5 is middle and 1 is end). ### Radial gradient Create text with a color that radiates from the center outward: ```json { "timeline": { "background": "#FFFFFF", "tracks": [ { "clips": [ { "asset": { "type": "rich-text", "text": "NOVA", "font": { "family": "Montserrat", "size": 200, "weight": 800 }, "style": { "gradient": { "type": "radial", "stops": [ { "offset": 0, "color": "#ffd89b" }, { "offset": 0.5, "color": "#f6416c" }, { "offset": 1, "color": "#2d3561" } ] } } }, "start": 0, "length": 5, "width": 900, "height": 400 } ] } ] }, "output": { "format": "mp4", "size": { "width": 1280, "height": 720 } } } ``` ![radial gradient example](/img/rich-text/image-3.png) The gradient radiates from yellow at the center through pink at the midpoint to dark blue at the edges. Radial gradients create a spotlight or glow effect and work best with centered text. **Gradient options:** - `type` - "linear" or "radial" (default: "linear") - `angle` - Gradient angle in degrees, 0-360 (default: 0, only for linear) - `stops` - Array of color stops (minimum 2 required) - `offset` - Position of stop, 0-1 (required) - `color` - Hex color code (required) :::info Gradients require at least 2 color stops. Use 2-4 stops for smooth, professional gradients. More stops can create banding effects. When using gradients, the gradient fill overrides the `font.color` property. ::: ## Stroke Add a bold outline around text for visibility and impact: ```json { "asset": { "type": "rich-text", "text": "Outlined Text", "font": { "family": "Permanent Marker", "size": 72, "color": "#ffffff" }, "stroke": { "width": 4, "color": "#000000", "opacity": 1 } }, "start": 0, "length": 5, "width": 800, "height": 400 } ``` ![outlined text](/img/rich-text/image-5.png) The 4-pixel black stroke creates a bold outline that makes white text readable over any background. Stroke width of 3-6 pixels works well for most text sizes. Combine stroke with shadow for maximum visibility. **Stroke options:** - `width` - Stroke width in pixels (default: 0) - `color` - Hex color code (default: "#000000") - `opacity` - Stroke opacity, 0-1 (default: 1) ## Shadow Add depth and improve readability with drop shadows: ```json { "timeline": { "background": "#F9DB6A", "tracks": [ { "clips": [ { "asset": { "type": "rich-text", "text": "MONDAY", "font": { "family": "Montserrat", "size": 175, "color": "#432963", "weight": 900 }, "shadow": { "offsetX": 8, "offsetY": 8, "blur": 6, "color": "#77E6F0", "opacity": 0.8 } }, "start": 0, "length": 5, "width": 1000, "height": 400 } ] } ] }, "output": { "format": "mp4", "size": { "width": 1280, "height": 720 } } } ``` ![drop shadow example](/img/rich-text/image-4.png) The shadow appears 8 pixels down and to the right with an 6-pixel blur, creating a soft depth effect. Lower opacity (0.5-0.8) creates subtle shadows, while higher opacity (0.9-1.0) creates dramatic shadows. **Shadow options:** - `offsetX` - Horizontal offset in pixels (default: 0) - `offsetY` - Vertical offset in pixels (default: 0) - `blur` - Blur radius in pixels (default: 0) - `color` - Hex color code (default: "#000000") - `opacity` - Shadow opacity, 0-1 (default: 0.5) ## Background Add a colored background box behind text: ```json { "asset": { "type": "rich-text", "text": "Background Text", "font": { "family": "Roboto", "size": 48, "color": "#ffffff" }, "background": { "color": "#3498db", "opacity": 0.8, "borderRadius": 20 } }, "start": 0, "length": 5, "width": 600, "height": 200 } ``` ![Lower-third with translucent blue rounded background and white text rendered over a forest scene](/img/rich-text/translucent-background.png) By default, the background fills the entire clip container (600×200 pixels) with a semi-transparent blue color. The rounded corners (`borderRadius: 20`) soften the edges. ### Wrapping the background to the text When the text length is unknown at template-authoring time — merge fields, user input, transcribed names — a fixed-width background looks awkward. Short text leaves a wide empty bar; long text overflows or feels cramped against the edges. Set `background.wrap` to `true` and the background shrinks to fit the rendered text plus the asset's padding (and stroke width, if present), producing a pill that hugs whatever the runtime value turns out to be. The render below uses an identical template for three different names. Without wrap (left), the background is always the full clip width. With wrap (right), the pill resizes to each name: ```json { "asset": { "type": "rich-text", "text": "Alexandra Wellington", "font": { "family": "Montserrat", "size": 48, "color": "#ffffff", "weight": 600 }, "padding": 18, "background": { "color": "#ef4444", "borderRadius": 60, "wrap": true } }, "start": 0, "length": 2, "width": 600, "height": 100 } ``` The clip stays 600×100, but the red pill only wraps the text plus 18 pixels of padding on each side. The text stays positioned within the clip per its `align` settings; the pill follows the text. Combined with `border`, the border tracks the wrapped pill in the same way. **Background options:** - `color` - Hex color code (optional) - `opacity` - Background opacity, 0-1 (default: 1) - `borderRadius` - Border radius in pixels (default: 0) - `wrap` - When `true`, the background shrinks to fit the rendered text plus padding instead of filling the clip (default: false) ## Alignment Position text within its container using horizontal and vertical alignment: ```json { "timeline": { "tracks": [ { "clips": [ { "asset": { "type": "rich-text", "text": "\"The mystery of human existence lies not in just staying alive, but in finding something to live for.\" \n \n- Fyodor Dostoevsky", "font": { "family": "Open Sans", "size": 48 }, "align": { "horizontal": "left", "vertical": "top" } }, "start": 0, "length": 5, "width": 500, "height": 600 } ] } ] }, "output": { "format": "mp4", "size": { "width": 1280, "height": 720 } } } ``` ![alignment example](/img/rich-text/image-7.png) This positions text in the top-left corner of the 500x600 container. Text is centered both horizontally and vertically by default. **Alignment options:** - `horizontal` - "left", "center", "right" (default: "center") - `vertical` - "top", "middle", "bottom" (default: "middle") ## Animations Text animations bring your text to life. Choose from preset animations like typewriter, fade-in, slide-in, and more. Control animation speed and duration to match your video's pacing. Animations are perfect for intros, subtitles, and attention-grabbing titles. ### Example: Typewriter animation ```json { "timeline": { "tracks": [ { "clips": [ { "asset": { "type": "rich-text", "text": "Ascend while others stay idle", "font": { "family": "Roboto", "size": 60, "color": "#ffffff" }, "animation": { "preset": "typewriter", "duration": 2, "style": "word" } }, "start": 0, "length": 5, "width": 800, "height": 400 } ] } ] }, "output": { "format": "mp4", "size": { "width": 1280, "height": 720 } } } ``` The optional `duration` parameter controls how long the animation takes, with the default being set to the length of the clip. The animation starts when the clip begins. ### Available animation presets #### fadeIn ```json "animation": { "preset": "fadeIn", // Animation type "duration": 2, // Animation length (default: full clip) "style": "character" // "word", "character", or "full" (default) } ``` **typewriter** ```json "animation": { "preset": "typewriter", // Animation type "duration": 2, // Animation length (default: full clip) "style": "character" // "word" or "character" (default: character) } ``` **slideIn** ```json "animation": { "preset": "slideIn", // Animation type "duration": 2, // Animation length (default: full clip) "style": "character", // "word" or "character" (default) "direction": "left" // "left" (default), "right", "up", "down" } ``` **ascend** ```json "animation": { "preset": "ascend", // Animation type "duration": 2, // Animation length (default: full clip) "direction": "up" // "up" (default), "down" } ``` **shift** - Text shifts from a direction with fade ```json "animation": { "preset": "shift", // Animation type "duration": 2, // Animation length (default: full clip) "style": "character", // "word" or "character" (default) "direction": "left" // "left" (default), "right", "up", "down" } ``` ## Related topics - [Animations](/docs/guide/architecting-an-application/animations) - Animate clip properties over time - [Positioning](/docs/guide/architecting-an-application/positioning) - Position and scale rich-text assets --- ## Positioning - Markdown URL: https://shotstack.io/docs/guide/architecting-an-application/positioning.md - Web URL: https://shotstack.io/docs/guide/architecting-an-application/positioning/ - Description: Position assets precisely on the video canvas using position, offset, fit, and scale properties # Positioning Positioning controls where assets appear in your video. Because source assets vary in size and aspect ratio, Shotstack provides multiple properties that work together to give you precise control over placement and sizing. ## Common positioning patterns Before diving into how each property works, here are solutions to common positioning needs: ### Consistent sizing for variable images When using images of different sizes or aspect ratios (common in templates with user-provided content), set fixed dimensions with the appropriate fit mode to ensure consistent layout: ```json { "asset": { "type": "image", "src": "https://shotstack-assets.s3.amazonaws.com/images/woods1.jpg" }, "start": 0, "length": 5, "width": 400, "height": 400, "fit": "contain" } ``` ![Three images of different aspect ratios (portrait, square, landscape) all sized to 400x400 with fit contain, showing letterboxing](/img/positioning/consistent-sizing-fixed-dimensions.png) By setting fixed `width` and `height` values, every image—regardless of its original dimensions—will occupy exactly 800x450 pixels in your video. This is critical when working with user-provided content where source image sizes vary unpredictably. The `fit` property determines how the source image fills this fixed area: - `fit: "contain"` (shown above) - Scales the image proportionally to fit entirely within the 800x450 area. Maintains aspect ratio but may add letterboxing. Best when you must show the complete image. - `fit: "crop"` - Fills the 800x450 area by cropping the source image. Centers the crop region. - `fit: "cover"` - Scales the image to completely fill the 800x450 area. Does not aintain aspect ratio and may distort the image. ### Logo in corner Places a logo in the top-right corner with margin: ```json { "asset": { "type": "image", "src": "https://shotstack-assets.s3.amazonaws.com/logos/shotstack/small/logo-mono-white.png" }, "start": 0, "length": 5, "fit": "crop", // Default setting. Can be ommitted. "scale": 0.07, "position": "topRight", "offset": { "x": -0.05, "y": -0.05 } } ``` ![Shotstack logo positioned in top-right corner with offset margin](/img/positioning/logo-corner-offset.png) The `offset` property adds spacing from the corner anchor point, while the `fit` properties value of `crop` crops the image to the size of the viewport. The `scale` property resizes the clip to 7% of the viewport size. ### Full-bleed background Fills the entire canvas with an image, cropping edges if needed: ```json { "asset": { "type": "image", "src": "https://shotstack-assets.s3.amazonaws.com/images/woods1.jpg" }, "start": 0, "length": 5, "fit": "crop", // Default setting. Can be omitted. "position": "center" // Default setting. Can be omitted. } ``` ![Forest image filling entire canvas using fit crop, centered position](/img/positioning/full-bleed-background.png) The `"fit": "crop"` property scales the image to fill the clip area while maintaining aspect ratio. ## The coordinate system Shotstack uses normalized coordinates that work regardless of your output resolution. The viewport center is `(0, 0)`, edges extend from `-1` to `1` in both directions. This means the same positioning values work identically for 720p, 1080p, or 4K videos—no need to recalculate for different resolutions. ![Coordinate system diagram showing viewport with center at (0,0) and edges from -1 to 1 in both directions](/img/positioning/coordinate-system-diagram.png) ## Position property The `position` property anchors your asset to one of nine points on the viewport: - `center` (default) - `top`, `bottom`, `left`, `right` - `topLeft`, `topRight`, `bottomLeft`, `bottomRight` ```json { "asset": { "type": "image", "src": "https://shotstack-assets.s3.amazonaws.com/logos/shotstack/small/logo-mono-white.png" }, "start": 0, "length": 5, "fit": "none", "position": "topLeft" } ``` ![Shotstack logo positioned at top-left corner using position property with fit none](/img/positioning/position-top-left.png) Use `position` when you want to align content to standard locations like corners or edges. :::info Make sure you are using the correct `fit` property. The default `"fit": "crop"` will scale the asset to the viewport, allowing you to control how the asset is cropped. ::: ## Offset property The `offset` property fine-tunes placement from the `position` anchor point, relative to the total width and height of the viewport. Values range from `-1` to `1`: - `x`: Negative moves left, positive moves right - `y`: Negative moves down, positive moves up ```json { "asset": { "type": "image", "src": "https://shotstack-assets.s3.amazonaws.com/logos/shotstack/small/logo-mono-white.png" }, "start": 0, "length": 5, "fit": "none", "position": "topLeft", "offset":{ "x": 0.05, "y": -0.05 } } ``` ![Shotstack logo positioned at top-left with offset creating 5% margin from edges](/img/positioning/offset-with-margin.png) This example positions the asset at `bottomLeft`, then moves it right by 10% of width of the viewport, and up by 10% of the height of the viewport, creating margin from the edges. :::info Offset values use a relative `-1` to `1` range, based on the `width` and `height` selected in the `output`. Values > 1 will move the image offscreen. ::: ## Fit property The `fit` property controls how assets scale to fit the clip: ### Crop Scales the asset to fill the clip bounds while maintaining aspect ratio. This may crop the asset if aspect ratio doesn't match the viewport size: ```json { "asset": { "type": "image", "src": "https://shotstack-assets.s3.amazonaws.com/images/mountain-mist-lrg.jpg" }, "start": 0, "length": 5, "fit": "crop" } ``` ![Mountain landscape image with fit crop, filling canvas while maintaining aspect ratio](/img/positioning/fit-crop.png) ### Cover Scales the asset to fill the clip area. This does not maintain the aspect ratio and may distort the asset if the viewport aspect ratio doesn't match: ```json { "asset": { "type": "image", "src": "https://shotstack-assets.s3.amazonaws.com/images/mountain-mist-lrg.jpg" }, "start": 0, "length": 5, "fit": "cover" } ``` ![Mountain landscape image with fit cover, stretched to fill canvas without maintaining aspect ratio](/img/positioning/fit-cover.png) ### Contain Scales the asset to fit within the clip bounds while maintaining aspect ratio, but may add letterboxing if aspect ratios don't match: ```json { "asset": { "type": "image", "src": "https://shotstack-assets.s3.amazonaws.com/images/mountain-mist-lrg.jpg" }, "start": 0, "length": 5, "fit": "contain" } ``` ![Mountain landscape image with fit contain, showing letterboxing to preserve aspect ratio](/img/positioning/fit-contain.png) ### None No scaling applied. Asset uses its original dimensions: ```json { "asset": { "type": "image", "src": "https://shotstack-assets.s3.amazonaws.com/images/mountain-mist-lrg.jpg" }, "start": 0, "length": 5, "fit": "none" } ``` ![Mountain landscape image with fit none, showing original dimensions without scaling](/img/positioning/fit-none.png) Use when you want exact control over sizing, typically combined with `scale`. :::info If no `fit` property is specified, the default behavior is `crop`, which will crop assets to fill the frame. ::: ## Width & Height Clips are set to the width and height of the viewport by default, unless you explicitly set its dimensions. :::info The `width` and `height` properties are not currently available in Shotstack Studio. These properties are only available when using the Shotstack API directly. ::: ### Implicit dimensions When `width` and `height` are **not** explicitly set the clip will assume the dimensions of the viewport as set in the `output` property. ### Explicit dimensions You can set the exact pixel dimensions for a clip. This is helpful if you want to make sure your assets are contained within a fixed boundary: ```json { "asset": { "type": "video", "src": "https://shotstack-assets.s3.amazonaws.com/footage/drone-city-flyover.mp4" }, "start": 0, "length": 5, "width": 480, "height": 270, "position": "bottomRight", "offset": { "x": -0.1, "y": -0.1 } } ``` ![Picture-in-picture effect with smaller video overlay positioned in bottom-right corner using fixed dimensions](/img/positioning/picture-in-picture.png) This creates a picture-in-picture effect with a fixed-size video overlay. :::info Fixed size clips are particularly useful if you are using UGC or other assets from which you cannot guarantee the size and/or aspect ratio. ::: ## Scale The `scale` property resizes assets proportionally. A value of `1` is normal size, `0.5` is half size, `2` is double size: ```json { "asset": { "type": "image", "src": "https://shotstack-assets.s3.amazonaws.com/images/woods1.jpg" }, "start": 0, "length": 5, "scale": 0.2 } ``` ![Forest image scaled down to 20% of viewport size using scale property](/img/positioning/scale-example.png) Use `scale` when you want proportional resizing. Use `width` and `height` when you need exact dimensions. :::info The `scale` property is applied to the `clip` **after** `fit` scaling is applied to the asset. ::: ## How properties work together Properties are applied in this order: 1. **Width/Height** - Sets explicit dimensions of the clip 2. **Fit** - Scales asset to the clip 3. **Position** - Anchors to grid point 4. **Offset** - Fine-tunes from anchor relative to viewport 5. **Scale** - Final size multiplier Example using multiple properties: ```json { "asset": { "type": "image", "src": "https://shotstack-assets.s3.amazonaws.com/images/woods1.jpg" }, "start": 0, "length": 5, "fit": "contain", "position": "topLeft", "width": 500, "height": 500, "offset": { "x": 0.1, "y": -0.1 }, "scale": 0.8 } ``` ![Forest image in 500x500 clip with fit contain, positioned top-left with offset and scaled to 80%](/img/positioning/combined-properties.png) This example: 1. The clip container is explicitly set to 500px by 500px. 2. The asset is resized to fit within the 500px by 500px container without cropping the image, potentially with letterboxing. 3. Anchors the clip to the top-left corner of the viewport (`topLeft`) 4. Adds 10% margins margings (relative to the width and height of the viewport) from the corner 5. Scales the clip down by 80% to 400px by 400px. ## Related topics - [Animations](/docs/guide/architecting-an-application/animations) - Animate position and offset over time --- ## SVG - Markdown URL: https://shotstack.io/docs/guide/architecting-an-application/svg.md - Web URL: https://shotstack.io/docs/guide/architecting-an-application/svg/ - Description: Create scalable vector graphics using raw SVG markup # SVG The SVG asset lets you add scalable vector graphics to your videos using raw SVG markup. Unlike the [shape asset](/docs/guide/architecting-an-application/shapes) which is limited to basic rectangles, circles, and lines, the SVG asset supports complex paths, polygons, and custom shapes defined using standard [SVG 2 markup](https://www.w3.org/TR/SVG2/). ## Common SVG patterns Before diving into individual properties, here are solutions to common SVG needs: ### Circle with fill color Create a simple filled circle: ![SVG Cirle](/img/svg/svg-circle.png) ```json { "timeline": { "background": "#1a1a2e", "tracks": [ { "clips": [ { "asset": { "type": "svg", "src": "" }, "start": 0, "length": 5 } ] } ] }, "output": { "format": "mp4", "size": { "width": 1280, "height": 720 } } } ``` ### Rectangle with stroke Create a rectangle with a colored border and fill: ![alt text](/img/svg/svg-rectangle.png) ```json { "timeline": { "background": "#000000", "tracks": [ { "clips": [ { "asset": { "type": "svg", "src": "" }, "start": 0, "length": 5 } ] } ] }, "output": { "format": "mp4", "size": { "width": 1280, "height": 720 } } } ``` ### Path-based custom shape Create a custom star shape using a path: ![alt text](/img/svg/svg-path.png) ```json { "timeline": { "background": "#1a1a2e", "tracks": [ { "clips": [ { "asset": { "type": "svg", "src": "" }, "start": 0, "length": 5 } ] } ] }, "output": { "format": "mp4", "size": { "width": 1280, "height": 720 } } } ``` ## Basic usage A minimal SVG asset requires the `type` set to `svg` and a `src` property containing the raw SVG markup as a string: ```json { "asset": { "type": "svg", "src": "" }, "start": 0, "length": 5 } ``` The `src` property accepts any valid SVG markup as a string. The outer `` element must include `xmlns="http://www.w3.org/2000/svg"`, a `viewBox` to define the coordinate system, and `width` and `height` attributes to set the dimensions. The `src` is the markup itself, starting with `` element cannot be used within SVG markup. Use the [rich-text asset](/docs/guide/architecting-an-application/rich-text) for text rendering. - **Animated SVGs are not supported.** SVG animation elements such as `` and `` are not processed. Use [clip animations](/docs/guide/architecting-an-application/animations) to animate the SVG asset as a whole. ::: ## Advanced usage SVG markup can go well beyond simple shapes. You can use filters, gradients, blend modes, and other SVG features to create complex visual effects like this mesh gradient background: ![svg gradient](/img/svg/svg-gradient.png) ```json { "timeline": { "tracks": [ { "clips": [ { "asset": { "type": "svg", "src": "" }, "start": 0, "length": 5 } ] } ] }, "output": { "format": "mp4", "size": { "width": 1000, "height": 500 } } } ``` ## Related topics - [Positioning](/docs/guide/architecting-an-application/positioning) - Position and scale assets on the timeline - [Animations](/docs/guide/architecting-an-application/animations) - Animate clip properties over time --- ## HTML5 - Markdown URL: https://shotstack.io/docs/guide/architecting-an-application/html5.md - Web URL: https://shotstack.io/docs/guide/architecting-an-application/html5/ - Description: Render self-contained HTML, CSS, and JavaScript pages as animated video clips # HTML5 The HTML5 asset renders a self-contained HTML, CSS, and JavaScript page inside a video clip. Use it for animated overlays, motion graphics, data visualisations, and anything you would otherwise build as a small single-page web app. HTML5 replaces the deprecated `html` asset, which only rendered static markup. HTML5 runs in a real browser with a JavaScript runtime, a set of preloaded animation libraries, and deterministic frame capture. Use `html5` for all new work. ## Basic usage A minimal HTML5 asset requires `type` set to `html5` and an `html` string. Add `css` and `js` to style and animate it: ```json { "asset": { "type": "html5", "html": "

{{title}}

", "css": ".card { font-family: 'Inter'; padding: 32px; color: #fff; }", "js": "gsap.to('.card', { x: 200, duration: 1 });" }, "start": 0, "length": 4, "width": 1920, "height": 1080 } ``` **Asset properties:** | Property | Required | Type | Description | | :------- | :------- | :----- | :--------------------------------------------------------------------------------------------------------------------------- | | `type` | Yes | string | Must be set to `html5`. | | `html` | Yes | string | The page markup. Supports [merge fields](/docs/guide/architecting-an-application/merging-data) such as `{{title}}`. Max 1,000,000 characters. | | `css` | No | string | Stylesheet, inlined into the page ``. Max 500,000 characters. | | `js` | No | string | Script, run after the preloaded libraries are ready. Max 500,000 characters. | The clip-level `width` and `height` set the page's pixel dimensions and default to the output's dimensions. Every coordinate inside the page is measured in this pixel space. ## Preloaded libraries Four animation libraries are always available. Do not add any `