From npm:
npm install lazy-frames
npx lazy doctor
Agents need the Lazy Frames CLI on your machine and the raw skill instructions. Install the package first:
npm install lazy-frames
npx lazy doctor
Then use your agent's skill-from-URL option, or ask it to read and follow this raw Markdown skill:
https://lazy-frames.cosmicstack.ai/skill.md
Open the raw skill · View the human-readable version
Example agent request:
Read and follow the Lazy Frames skill at
https://lazy-frames.cosmicstack.ai/skill.md
Then make a promo video for https://example.com.
From source (for development):
git clone https://github.com/cosmicstack-labs/lazy-frames.git
cd lazy-frames
npm install && npm run build
npx lazy doctor
| Dependency | Required for | Check |
|---|---|---|
| Node.js ≥ 20 | CLI + engine | node --version |
| ffmpeg + ffprobe | encoding + audio | ffmpeg -version |
| Google Chrome | headless rendering | lazy doctor |
| Python 3 | audio sidecar | python3 --version |
macOS say | TTS narration | say -v '?' |
Run npx lazy doctor to verify all providers.
Website promo:
npx lazy capture https://example.com projects/acme
npx lazy snapshot projects/acme --update
npx lazy check projects/acme
npx lazy preview projects/acme # review in browser
npx lazy render projects/acme # after approval
Cinematic clip:
npx lazy gen image -p projects/cine --seed 21 --style ridge --name r01
# write spec.json referencing assets/gen/r01.png + r01.depth.png
npx lazy render projects/cine
Captures a website: screenshots @2x, palette, copy, fonts, logo. Writes a starter spec.
lazy capture <url> [project] [--json]
Generates media with local providers.
lazy gen image -p <project> --seed 21 --style ridge --name r01
lazy gen music -p <project> --mood calm --bpm 90 --bars 12 --seed 21
lazy gen tts -p <project> --text "Hello world" --voice Samantha --rate 165
Image styles: ridge, dune, nebula. Generates <name>.png + <name>.depth.png.
Drafts an editable narration script from on-screen scene content. With --apply, it writes narration.md and scene-linked narration beats into spec.json.
lazy script <project> [--provider say] [--voice Samantha]
lazy script projects/cine --provider elevenlabs --voice VOICE_ID --apply
Each beat uses sceneId and offsetMs. Lazy Frames measures the synthesized audio and rejects narration that would overrun its scene or the final timeline.
Plugins are reviewed, capability-scoped adapters. Install records the exact version and manifest fingerprint in lazy-plugins.json, plus a separate user-local approval for that project. A committed lockfile cannot grant itself permission. Arbitrary package URLs and install scripts are not accepted.
lazy plugin search [tts|script|storytelling]
lazy plugin install elevenlabs -p projects/cine
lazy plugin list -p projects/cine
lazy plugin remove elevenlabs -p projects/cine
ElevenLabs setup:
export ELEVENLABS_API_KEY="your-key"
lazy gen tts -p projects/cine --provider elevenlabs --voice VOICE_ID \
--text "Every frame follows the story." --name narration-01
The credential stays in the environment and is never stored in the spec or lockfile. See the plugin store and its machine-readable registry.
Validates: environment, spec schema, semantic checks, and two blocking gates (snapshot regression + seek determinism).
lazy check <project> [--json] [--skip-gates]
Creates or updates the snapshot regression baseline.
lazy snapshot <project> [--update]
Run --update once in a new project or after intentional spec changes.
Renders the composition to MP4. Generates audio (if in spec), muxes, probe-verifies, prints SHA-256.
lazy render <project> [-o <path>] [--fps N] [--parallel N] [--fast] [--json]
Serves a scrubbable timeline at localhost:4173.
lazy preview <project> [-p <port>]
Reports Node/Python versions, memory, hardware tier, and provider availability.
lazy doctor [--json]
{
"specVersion": 1,
"meta": { "id": "my-video", "width": 1920, "height": 1080, "fps": 24 },
"style": {
"tokens": {
"palette": ["#0B0F19", "#C49A5C", "#E2E0DC"],
"fontDisplay": "Space Grotesk",
"fontBody": "Inter"
},
"grade": "none"
},
"scenes": [ ... ],
"audio": { ... },
"outputs": [{ "format": "mp4", "path": "out/video.mp4", "codec": "h264" }]
}
Palette convention: index 0 = background, 1 = accent, 2 = foreground.
Width/height must be even (h264 yuv420p requirement).
| Type | What it does |
|---|---|
typography | Text reveals: titles, quotes, lockups (4 reveal styles) |
stat-hit | Count-up numbers, labels, bars |
browser-frame | Screenshot in a browser chrome mockup with cursor |
ui-callout | Screenshot with dimmed mask, hotspot spotlight, label |
atmosphere | Drifting gradient blobs — ambient backdrops |
parallax | 2.5D camera move over a still (flat or depth-map mode) |
video-layer | Footage playback with trim, speed, grade |
three-scene | Deterministic 3D: cube/ico/grid/particles (canvas 2D) |
| Type | Effect |
|---|---|
cut | Instant |
fade / dissolve / dip-to-black | Opacity in/out |
whip-pan | Horizontal slide with quick ease |
light-leak | Hold + fade (flash-like) |
luma-wipe | Vertical slide wipe |
All transitions: { "type": "...", "ms": N }. Must be shorter than scene duration.
All audio is declarative — generated at render time, cached by content hash.
"audio": {
"narration": [{ "text": "...", "startMs": 3600, "voice": "Samantha", "rate": 165 }],
"music": { "mood": "calm", "bpm": 90, "bars": 12, "seed": 21, "gainDb": -14 },
"sfx": [{ "kind": "whoosh", "atMs": 2400, "seed": 1, "gainDb": -6 }]
}
SFX kinds: whoosh, hit, rise, boom.
Stage grade (style.grade): CSS filter on the composition root. Presets: none, contrast, vivid, muted, monochrome.
Output LUT (outputs[0].lut): 3D LUT .cube file applied via ffmpeg lut3d. Bundled: teal-orange, noir-film, faded-vintage.
Same spec + same machine = byte-identical MP4. Enforced by:
--jitless — V8 interpreter only (no JIT nondeterminism)--use-angle=swiftshader — software rendering (no GPU variance)putImageData for canvas scenes — no skia compositing nondeterminism+bitexact, fixed threadsSnapshot regression: renders keyframes, hashes pixels, compares against snapshots/baseline.json. Drift = error with scene + timestamp.
Seek determinism: renders 5 timestamps twice in one session, compares hashes. Catches in-session nondeterminism.