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.
The marketplace is generated from independent plugins/<id>/manifest.json folders. ElevenLabs is included by default; all other entries are installable. Install records the exact version and fingerprint in lazy-plugins.json, plus separate user-local approval. A committed lockfile cannot grant itself permission.
lazy plugin search [tts|script|storytelling]
lazy plugin info <id>
lazy plugin install elevenlabs -p projects/cine
lazy plugin list -p projects/cine
lazy plugin remove elevenlabs -p projects/cine
Scaffold entries can be installed and evaluated, but cannot execute until a reviewed adapter ships. Arbitrary package URLs, lifecycle scripts, and undeclared permissions are rejected.
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 Marketplace 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.