@frapx/tool-render
tool-* — tool A Node command; the page it renders uses @frapx/tool-capture.
Renders long frame sequences without keeping a browser tab open.
frapx-render opens your page in headless Chrome, renders a frame range
through @frapx/tool-capture, and writes the PNGs to a folder. An interrupted
render resumes where it stopped, and the frames can be encoded with ffmpeg at
the end.
pnpm add -D @frapx/tool-renderNeeds Node 20+, an installed Google Chrome (or npx playwright install chromium
with --browser chromium), and ffmpeg on PATH for --encode (plus
ffprobe for --audio).
Register the page
Section titled “Register the page”import { registerRenderTarget } from "@frapx/tool-capture";
registerRenderTarget(fx, { fps: 30, duration: 24, // the default range is frames 0..719 resolution: [1920, 1080], // the default size; --resolution overrides it prepare({ params }) { fx.setAccumulation({ samples: Number(params.spp ?? 64) }); }});prepare({ params, fps, format, resolution }) runs before each render job.
Pass () => fx when the page replaces its instance. Without a declared
resolution or --resolution, the page renders at its own size, which
headless is the 1280 × 720 viewport.
Render
Section titled “Render”vite buildfrapx-render dist --out renders/loop --resolution 3840x2160 --set spp=192 --encode prores# interrupted? continue with the same settings:frapx-render dist --out renders/loop --resolution 3840x2160 --set spp=192 --encode prores --resume# a route of a single-page app:frapx-render dist --path /music --out renders/musicA missing route without a file extension falls back to dist/index.html, so
client-side routes render from the build. For a URL source, put the route in
the URL instead. vite preview listens on localhost only, which may be IPv6:
use the address it prints, or start it with --host 127.0.0.1.
| Option | Default | |
|---|---|---|
--out <dir> |
required | Output directory. |
--path <route> |
/ |
Route to open in a directory, e.g. /music. |
--range <a-b> |
the page’s duration |
Inclusive frame range. |
--fps <n> |
the page’s fps |
|
--resolution <WxH> |
the page’s resolution, else its size (headless: 1280 × 720) |
e.g. 2560x1440, 2048x1080. |
--format <png|png16> |
png |
16-bit needs a float buffer in the page. |
--set <key=value> |
Passed to prepare(); repeatable. |
|
--resume |
Render only the missing frames; the settings must match. | |
--encode <prores|h264> |
ProRes 422 HQ .mov or H.264 .mp4, BT.709. |
|
--audio <file> |
Muxed into each video (PCM 24-bit / AAC 320k), cut to the range. | |
--strict-audio |
Stop when the audio and the page’s duration differ by over a frame. |
|
--browser <chrome|chromium> |
chrome |
|
--headed |
Show the window. | |
--allow-software |
Allow SwiftShader and other software renderers. | |
--timeout <seconds> |
60 |
How long to wait for the page to call registerRenderTarget(). |
-h, --help |
Print the usage text and exit. | |
-v, --version |
Print the version and exit. |
What it guards against
Section titled “What it guards against”- Truncated frames — each PNG is written to a temporary file and renamed.
- Mixed settings —
frapx-render.jsonin the output folder records the source, route, fps, format, resolution and params;--resumerefuses when they differ. - Crashes and lost contexts — the page is opened again and the render continues; the frame written right before a lost context is redone. Three failures in a row without progress stop it.
- Hot reloads — a reload or navigation mid-render stops it, because later frames would come from different code. Render a production build.
- Mismatched audio — with
--audio, the file’s length is compared with the page’sdurationbefore rendering. More than a frame apart warns, or stops with--strict-audio. - Silent software rendering — it prints the WebGL renderer and stops on
SwiftShader or llvmpipe unless you pass
--allow-software.
The CLI is a thin wrapper around render(options, { signal, onEvent }),
exported for scripts. options mirrors the flags, onEvent reports progress,
and it resolves with the frames rendered and any videos encoded.
parseCliArgs(argv) parses frapx-render arguments into those options and
throws UsageError for invalid ones.
Full reference: README on GitHub.