framefields renders your project on its own GPUs, straight from git: no GPU, Docker or render farm on your side. Name a render once in framefields.json, then start it from the terminal, from code or from the dashboard. Long films split across several GPUs and come back as one file.
npx framefields render film --cloud --plan # Plan: 4 workers, 1 h 50 min of render work, ready in about 35 min, estimated $1.20.npx framefields render film --cloud # Done in 34 min: output/film.mp4 (412.3 MB), 6620 GPU seconds, $1.18.Before you render
Section titled “Before you render”- Push. The cloud renders the pushed commit. Uncommitted changes are not rendered (the CLI warns), and a commit that isn’t on the remote is refused.
- An API key. Renders bill the account behind
FRAMEFIELDS_API_KEY, the same key as the AI gateway. Create one at framefields.dev/dashboard/keys and add credit under Billing. - Private repos: connect GitHub once under Settings → Connections. Install the framefields GitHub App on all repositories, so repos you create later render without another step. It only gets read access, and each render gets a token for its own repo that expires in an hour. Public repos need nothing.
- A render-ready project: a committed lockfile, assets pinned with
framefields assets(or in Git LFS), and an entry that only builds the film. A project made withframefields initalready is one; the full list is the project checklist. - Run from the project folder (where
framefields.jsonis). In a monorepo, that folder is what renders.
Name your renders
Section titled “Name your renders”framefields.json at the project root names what the project renders. npx framefields init writes film; add an entry for each cut.
{ "components": "src/components", "renders": { "film": { "title": "Launch film", "entry": "src/film.ts", "export": "buildFilm", "workers": "auto", "output": "output/film.mp4" }, "teaser": { "title": "15 s teaser", "description": "Vertical cut for socials", "entry": "src/film.ts", "export": "buildTeaser", "codec": "vp9" } }}| Field | |
|---|---|
entry |
The module that builds the composition, relative to the project root. Required. |
export |
A Composition, or a function returning one. Default buildFilm. |
title, description |
Shown in the dashboard and framefields renders. |
args |
Passed to the export when it is a function. |
select |
When the export returns several compositions, the one to render. |
workers |
1 (default), a number, or "auto" to split long films. |
maxWorkers |
Upper bound for "auto". |
codec |
h264 (default, MP4) or vp9 (WebM). |
quality |
very_low, low, high or very_high (default). |
output |
Where the CLI downloads the video. |
Names use letters, digits, -, _ and .. npx framefields renders lists them; npx framefields check checks every one, npx framefields check teaser one.
From the terminal
Section titled “From the terminal”npx framefields render film --cloud --plan # workers, time and estimated cost; renders nothingnpx framefields render film --cloud # render, then download to the render's outputWithout --cloud, npx framefields render film renders the same named render on this machine, to the same file, and npx framefields preview film plays it in the browser: switching is the flag. Flags override the render’s fields (--workers 4, --codec vp9, --output). With a single named render, npx framefields render --cloud needs no name. A module renders without a manifest too: npx framefields render src/film.ts --cloud --export buildFilm.
--plan probes the film and prints the GPUs, time and estimated cost, then the command that renders it; it renders nothing. While rendering, the CLI prints one line per phase and progress every 10 seconds with the cost so far. --json prints the final status as JSON. Ctrl-C cancels the render; GPU time already used is charged.
From code
Section titled “From code”import { renderCloud } from "framefields/cloud";
const result = await renderCloud({ render: "film", outputPath: "output/film.mp4", onProgress: (s) => console.log(s.status, s.progress.frames),});framefields/cloud needs no GPU. Options passed here override the named render’s; dryRun: true returns the plan and estimate, and signal cancels. A failed render throws a CloudRenderError with the worker’s code.
HTTP API
Section titled “HTTP API”The CLI and renderCloud call the same API, with Authorization: Bearer $FRAMEFIELDS_API_KEY:
curl https://api.framefields.dev/v1/render \ -H "Authorization: Bearer $FRAMEFIELDS_API_KEY" -H "Content-Type: application/json" \ -d '{ "source": { "repo": "https://github.com/acme/launch-film", "ref": "main" }, "target": { "entry": "src/film.ts", "export": "buildFilm" }, "render": { "codec": "h264" }, "workers": "auto", "dry_run": false }'GET /v1/render/:id returns its status (probing, rendering, stitching, then completed with output.url, or planned, failed, cancelled), progress, plan and cost so far. DELETE /v1/render/:id cancels it. Optional source.commit pins a commit and source.workdir a folder; optional name and title label it in the dashboard.
From the dashboard
Section titled “From the dashboard”Connect GitHub once, on all repositories, then Renders → New render: pick a repo and branch, and the dialog lists the renders its framefields.json names, with their titles and descriptions. Picking one applies its GPUs and format. Plan shows GPUs, time and cost before anything renders. Repos without named renders still render: choose Another file in this repo and pick the module and export.
The Renders page lists every render, from the dashboard, the CLI or the API: live progress, cost so far against the estimate, Cancel, and the finished video.
Splitting long films
Section titled “Splitting long films”workers: "auto" measures frame costs on one GPU first, then splits the film into as many chunks as stay efficient: a 5-minute film stays on one GPU, a 3-hour film spreads across up to 10. Each chunk renders on its own GPU, the audio renders once, and the chunks are joined without re-encoding. Effects with state (trails, datamosh, particles) replay earlier frames, so every chunk renders the same picture as a single render.
You pay for GPU seconds actually used. When a render starts, a credit hold is placed on your balance; once the film is planned the hold is raised to the estimate, and when it finishes you are charged the GPU time used and the rest is released. Failed and cancelled renders charge the GPU time they used. --plan (or Plan) costs the few seconds of GPU time the probe takes.
When a render fails
Section titled “When a render fails”The error names the worker’s code. Fix the project, commit, push and render again; network errors, stalled workers and lost machines are retried by the API, and a straggling chunk gets a backup worker.
| Code | Cause | Fix |
|---|---|---|
REPO_NOT_FOUND, GIT_AUTH_FAILED |
A private repo the GitHub App can’t reach | Connect GitHub with access to this repo |
REF_NOT_FOUND, COMMIT_MISMATCH |
The branch or commit isn’t on the remote | git push |
LOCKFILE_MISSING, INSTALL_FAILED |
No committed lockfile, or it doesn’t install | Commit a pnpm, npm or yarn lockfile matching package.json |
ENTRY_NOT_FOUND, EXPORT_NOT_FOUND |
entry or export is wrong |
npx framefields check <name> |
EXPORT_SIDE_EFFECT, COMPOSITION_BUILD_FAILED |
The entry renders or writes while building, or throws | Keep film.ts to building; framefields check reproduces it |
ASSET_NOT_FOUND, LFS_OBJECT_MISSING, LFS_POINTER_UNRESOLVED |
An asset wasn’t committed or its LFS object wasn’t pushed | git lfs ls-files, git lfs push --all origin |
ASSET_NOT_UPLOADED, ASSET_PRIVATE, ASSET_HASH_MISMATCH |
A pinned asset isn’t on the bucket, or the worker can’t read it | Assets: when something fails |
ENGINE_UNSUPPORTED |
The project’s framefields is too old to split | Upgrade framefields, or use --workers 1 |
RENDER_TIMEOUT, RENDER_OOM |
One GPU can’t finish its part | --workers auto, or more workers |
insufficient_credits |
The balance doesn’t cover the hold | Add funds under Billing |
All worker codes: Render on a server.