---
name: grid-filler
version: 1.0.0
description: Slide a ruled two-column table into view, then fill it row by row, a short lead cell first and its matching proof cell right after, ending on a calm hold. 1920x1080 HyperFrames video, 2-5 rows, optional synthesised cue sounds.
---
# Grid Filler

A parameterized renderer (HTML/SVG + GSAP, rendered with HyperFrames), not an open-ended prompt-to-film tool. You write a short JSON brief; `scripts/create.mjs` turns it into an editable project and `scripts/render.mjs` renders it. Anything beyond one two-column table needs new authoring and new checks.

Read [direction](references/direction.md) for the beats, [input](references/input.md) for the brief contract and [sound](references/sound.md) for the cue map. [third-party](references/third-party.md) lists the bundled libraries and their licenses.

## Use it
Needs Node 22+, ffmpeg/ffprobe and a Chrome that HyperFrames can drive. No API key; the only network call is `npx` fetching the pinned renderer.

```sh
node scripts/create.mjs --brief brief.json --out ./project [--palette cobalt-paper|cinder|lilac-field] [--palette-file tokens.json] [--audio on|off]
node scripts/render.mjs ./project ./project/film.mp4 [--silent]
```

`render.mjs` runs `hyperframes check`, renders strictly, then writes `film.mp4` (H.264 plus a 48 kHz stereo AAC track) and `film.video-only.mp4` (same picture, no audio stream). With `--silent`, `--audio off` or `"sound": {"enabled": false}` only a picture-only file is produced.

After editing `cues.json` or the sound options you can rebuild only the audio with `node scripts/render.mjs ./project ./project/film.mp4 --remux` (it reuses `film.video-only.mp4`).

Brief fields: `tableCaption` (optional), `pairs` (2-5 of `{lead, proof, mark?}`), `drift` (`left` or `right`), `demoNote`, `lang`, `palette`, `sound`. Mark invented content with `demoNote` such as "Demo data" so it is not read as fact.

## What to keep
- Order: caption, table drifts sideways into place with its rules already visible, then per row the lead cell wipes in, the proof cell follows after a short beat, the next row waits for a reading pause that grows with the proof length. Rows never move once placed. Everything settles into a hold of at least 1.6 s with no ambient motion.
- Text is fitted to the row height: leads (up to three lines) from 54 px down to 28 px, proofs from 42 px down to 24 px. If a line cannot fit, `create.mjs` stops with a message naming the field instead of shrinking below that floor.
- Sound is synthesised in `scripts/sound.mjs` from the events in `cues.json`: a sideways sweep for the drift, a dry tick plus a low note for each lead cell, a two-note confirm for each proof cell (the pitch climbs by row), and a soft chord when the edges settle. No samples, no recordings. Call it "synthesised cues", not a soundtrack, and never say someone listened unless a person did.

## Check before delivering
Confirm 1920x1080, 30 fps and the expected duration, then look at real frames at the first lead cell, each proof cell and the final hold. Compare the text on screen with the brief and check nothing is clipped. Measure the audio (ffprobe, integrated loudness, true peak) and compare each cue time with the frame where its cell first changes. Chinese briefs switch to Noto Sans SC automatically; check them as their own case.
