Record HTML to video
record_htmlRecord a web page or HTML5 creative (banner, ad, animation, landing page) to a video: MP4 (default), WebM, MOV (ProRes) or GIF. Returns the finished recording with a video link.
Transparency: transparent: true with webm, mov or gif keeps what the page draws without its background (an alpha channel), for stickers, overlays and lower thirds. Give the page no background of its own beyond html and body (those are removed).
Input: either url (a public http(s) page; localhost and private networks are out of reach) or html (a complete single-file HTML document you wrote, published at an unlisted public URL and recorded from there). Set width and height to the creative's size (e.g. 300×250, 728×90, 1080×1920, 1920×1080) and durationSec to how long it runs.
Claude artifacts: a claude.ai/chat link is private, so the recorder can't open it. To record an artifact from this conversation, pass its code as html. An HTML artifact goes in as it is. A React artifact is a component, not a page: wrap it in a complete HTML document with
the component in , rendered with createRoot from react-dom/client into a root div. Replace what only exists inside Claude (@/components/ui imports, window.claude) with plain code. A published artifact (claude.ai/public/artifacts/…) records with Claude's page frame around it, so prefer html.
Waiting: the call waits up to waitSec (default 45 s) for the video. If it isn't done by then, you get the recording with status queued or running; it keeps going. Call get_recording with its id and waitSec to wait for it; don't call record_html again for it.
Retries are safe: identical calls within 10 minutes return the same recording. After changing the creative behind the same url, pass a new revision to record it again. A failed recording is never reused: call again once you've fixed the cause.
Trim markers: by default the video starts at the page's load event and lasts durationSec. To capture exactly the animation, the page can mark it. The recorder defines two global functions in every frame (iframes included):
trim_start(): call it once when the assets are loaded, just before the animation starts. The video starts there.
trim_end(): call it once from the animation's completion callback (e.g. GSAP onComplete, the last animationend). The video ends there and durationSec becomes the maximum. Guard both so the page still works in a normal browser: // assets loaded, just before the animation starts: if (typeof trim_start === 'function') trim_start(); // in the animation's completion callback: if (typeof trim_end === 'function') trim_end(); Never call the two right after each other: that marks an empty clip. With sync "markers" the recording fails instead of falling back to the load event when trim_start() isn't called within markerWaitSec (default 10 s). report.anchor says "trim_start" when the start marker was used.
Play buttons: a page that waits for a click records nothing but its button. For html you write, start the animation on load instead. For a page you can't change, pass actions: [{"type":"click","text":"Play"}] (the button's label, or a selector): the recorder clicks it and the video starts at the click (report.anchor "action").
Links: video.url is signed and valid for 1 hour (get_recording mints a fresh one). pageUrl is the recording's page in the web app, for a person to open later (they sign in).
Credits: each recording uses credits from the account: 1 per started 60 s of video up to 1920×1080 pixels (output size, else crop, else viewport), 5 above that. A failed recording gives its credits back. When the account runs out, the call fails with insufficient_credits: the person adds credits on the web app's Billing page. Limits on length, size, fps and recordings at a time apply too; estimate_recording shows the cost and the balance without recording. Errors say what to change: fix it and call again.
Tool call template
Replace each placeholder with a value matching the input schema. Only required parameters are included; add optional parameters as needed.
{
"id": 1,
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"arguments": {
"width": "<width>",
"height": "<height>",
"durationSec": "<durationSec>"
},
"name": "record_html"
}
}Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| crf | No | Quality for mp4 and webm, from 10 (best, largest) to 35 (smallest). Not together with bitrateMbps; not for gif or mov. | |
| fit | No | How the frame fills outputSize: fill (stretch), contain (letterbox in background) or cover (crop to fill). | |
| fps | No | Frame rate: one of 15, 23.976, 24, 25, 29.97, 30, 59.94, 60 (default 30). For gif, a whole number from 1 to 30. | |
| url | No | Public http(s) URL of the page or creative to record. The recorder runs on the internet: localhost, private networks and pages behind a sign-in (such as claude.ai/chat links) are out of reach. Pass url or html, not both. | |
| crop | No | Record only this region of the viewport: { x, y, width, height } in pixels, even values, inside the viewport. | |
| html | No | A complete HTML document to record instead of a url, e.g. a banner you just wrote or an artifact from this conversation. One file: inline the CSS and JavaScript, and load images, fonts and libraries from public https URLs or data: URIs. It is published at an unlisted public URL and recorded from there. | |
| loop | No | gif only: whether the gif loops. | |
| sync | No | How trim markers are used: auto (default: markers when the page calls trim_start(), else the load event), load (ignore markers) or markers (fail when trim_start() is not called within markerWaitSec). | |
| audio | No | Record the page’s audio too. Not for gif. | |
| width | Yes | Viewport width in CSS pixels: even, 16–7680. Use the creative’s size, e.g. 300 for a 300×250 banner or 1920 for a full HD page. | |
| format | No | mp4 (default, H.264), webm (VP9), mov (ProRes, for video editors) or gif. | |
| height | Yes | Viewport height in CSS pixels: even, 16–7680. | |
| actions | No | Clicks, for a page that waits for a Play or Start button: [{"type":"click","text":"Play"}]. A click finds its element by selector (CSS), text (visible text, aria-label, title or alt; case and symbols such as ▶ ignored) or point ({x, y} in the viewport), afterMs after load or the previous click. The video starts at the first click unless startsVideo is false. An element that is not on screen within 10 s fails the recording. | |
| startAt | No | Where the video starts: "load" (default: the load event, or the first trim_start() call) or that many milliseconds after it (0–120000). | |
| waitSec | No | How long to wait for the finished video, in seconds (0–45, default 45). The recording keeps going after the call returns. | |
| renderer | No | auto (default), cpu or gpu. Leave it on auto. | |
| revision | No | Any label that changes when the creative changes. Identical calls within 10 minutes return the same recording; after you change the creative behind the same url, pass a new revision to record it again. | |
| maxSizeMb | No | Keep the file under this many megabytes (decimal: 0.6 = 600 KB), e.g. a platform’s upload limit or an email gif. The quality is lowered only as much as needed; frames, size and duration stay as requested. Not with bitrateMode cbr or format mov. | |
| background | No | Letterbox colour for fit: contain, as #rrggbb. | |
| outputSize | No | Scale the video to { width, height } (even values); see fit. | |
| bitrateMbps | No | A target bitrate in Mbps (1–100) instead of crf, for platforms with a bitrate spec (e.g. DOOH). Not for gif or mov. | |
| bitrateMode | No | vbr (default) or cbr. Needs bitrateMbps. | |
| durationSec | Yes | Length of the video in seconds (1–300; your account limit applies). With trim_end() it is the maximum. | |
| pixelFormat | No | yuv420p (default; plays everywhere), yuv422p or yuv444p. For mov, yuv444p picks ProRes 4444, otherwise 422 HQ. | |
| transparent | No | Keep what the page draws without its background, as an alpha channel: webm (VP9 with alpha), mov (ProRes 4444) or gif (one transparent colour). Not for mp4. For stickers, overlays, lower thirds and Lottie or Rive style animations; the page’s html and body background is removed. Width up to 3840. | |
| markerWaitSec | No | How long to wait for trim_start() after load, in seconds (0–60, default 10). | |
| markerLatencyMs | No | Overrides the measured delay between a marker call and the frame it marks (0–1000 ms). Rarely needed. |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | ||
| error | Yes | ||
| video | Yes | ||
| report | Yes | ||
| status | Yes | ||
| pageUrl | Yes | ||
| request | Yes | ||
| progress | Yes | ||
| renderer | Yes | ||
| createdAt | Yes | ||
| finishedAt | Yes |