startPreview serves a page that loads your composition’s module and renders it live with WebGPU in the browser — not a video stream. The server only hands over the bundle, your assets, and the soundtrack mixed by the export engine.
import { startPreview } from "framefields";
const session = await startPreview( { entry: new URL("./film.ts", import.meta.url), export: "buildFilm" }, { title: "Launch film" },);console.log(`Preview at ${session.url}`);await session.closed; // "idle" | "replaced" | "closed"process.exit(0);export names a function returning the Composition (may be async) or a Composition itself.
Options
Section titled “Options”| Option | Default | Notes |
|---|---|---|
port |
derived from the project directory | one stable URL per project; a new run replaces the old server and open tabs reload in place |
host |
127.0.0.1 |
never leaves the machine |
open |
false |
open the system browser; otherwise show session.url where you are |
title |
— | page header |
audio |
true |
mix and play the soundtrack |
idleCloseMs |
5000 |
stop after the last tab closes; false keeps serving until close() |
root |
project of the entry | extra folders the page may load assets from |
log |
stderr | where page errors and lifecycle lines go; false silences |
notesDir |
.framefields/preview-notes |
false turns notes off |
Assets must resolve in the browser
Section titled “Assets must resolve in the browser”The browser runs film.ts itself. Build asset paths from the module URL, not process.cwd():
import path from "node:path";import { fileURLToPath } from "node:url";
const HERE = path.dirname(fileURLToPath(import.meta.url));const FONTS = path.resolve(HERE, "../assets/fonts");node:path, fileURLToPath and synchronous fs reads work through shims; writes are skipped.
For agents
Section titled “For agents”- Run the preview as a background command and read its output: it reports when the page opened, each distinct page error (build failure, a frame that threw, missing font, no WebGPU), and why it stopped.
- After editing, run it again — same URL, and the open tab reloads at the same moment.
- Deep link:
#t=12.5(seconds) or#frame=750. - Script it:
window.framefieldsPreviewhasseek(seconds),seekFrame(n),play(),pause(),notes(),state().seekresolves once the frame is drawn — screenshot after it.<html data-preview="ready">marks the first drawn frame.
Notes pinned on the picture
Section titled “Notes pinned on the picture”Paused, a person clicks a spot (or drags an area) and writes what should change. Each note is printed in the preview output and saved:
.framefields/preview-notes/ notes.json [{ id, frame, time, x, y, w?, h?, text, done }] note-<id>.jpg the frame with the spot markedTo act on notes: read notes.json, open each open note’s picture, find the layer from the moment and the spot, edit, rerun the preview. Set "done": true yourself or let the person tick them off.
Playback
Section titled “Playback”Play runs in real time on the soundtrack’s clock and skips frames a slow device can’t draw; the page shows the frame rate it reaches. Paused, it draws the exact frame. Requires WebGPU (current Chrome, Edge or Safari).