# Cadence
<p align="center">
<img src="brand/logo.svg" alt="Cadence" width="320" />
</p>
<p align="center">
<strong>Programmatic video from Lua.</strong> No browser. No React scaffold.<br/>
One <code>.lua</code> file → headless LÖVE → ffmpeg → mp4.
</p>
<p align="center">
<a href="https://github.com/shinyobjectz/cadence">GitHub</a> ·
<a href="CHECKS.md">Verification</a> ·
<a href="skills/cadence/SKILL.md">Agent skill</a> ·
<code>npx skills add shinyobjectz/cadence</code>
</p>
---
Cadence is a seek-not-playback motion framework: compositions are pure functions of time. Agents author `.lua` files; the renderer evaluates any frame in any order; verification runs in milliseconds before you ever encode.
## Watch: how Cadence works
<p align="center">
<a href="docs/media/how-cadence-works.mp4">
<img src="docs/media/how-cadence-works.jpg" alt="How Cadence Works: play the 2:40 video" width="100%" />
</a>
</p>
**Start here.** A 2:40 lesson that goes one layer deeper at each step: a comp as a function of time,
seeking any frame, declaring motion instead of drawing it, determinism and frame hashes, then the
fact log, perception and edits written back into the Lua. It was rendered by Cadence from
[`comps/lesson/how-cadence-works.lua`](comps/lesson/how-cadence-works.lua).
[▶ Play the video](docs/media/how-cadence-works.mp4)
## Watch: facts on real footage
<p align="center">
<a href="docs/media/facts-on-footage.mp4">
<img src="docs/media/facts-on-footage.jpg" alt="Facts on footage: play the 1:46 video" width="100%" />
</a>
</p>
What the fact log looks like on a real clip, and how an agent works with it. A stock shot of a
bottle changing hands is perceived into facts, and every overlay is drawn from one of them:
tracker boxes, the third of the frame an object is in, its direction and speed, and the moment of
release. Then an agent edits a comp from the log alone: it asks when the release happens, asserts
a caption there, and proves the edit by hashing every frame (100 of 358 changed, in exactly two
windows). Rendered from [`comps/agent/facts-on-footage.lua`](comps/agent/facts-on-footage.lua);
the grammar is in [`docs/FACTS.md`](docs/FACTS.md).
[▶ Play the video](docs/media/facts-on-footage.mp4)
## Quick start
```bash
git clone https://github.com/shinyobjectz/cadence.git && cd cadence
cd desktop && pnpm install && cd ..
# environment check
bin/cadence doctor
# instant compile (wasmoon, ~30ms)
cd evals/projects/launch-spot
../../bin/cadence verify comps/launch.lua --wasm-only
# render
../../bin/cadence render comps/launch.lua -o out.mp4
```
Install the agent skill (Cursor, Claude Code, Codex, Copilot, …):
```bash
npx skills add shinyobjectz/cadence
```
## CLI
| Command | What it does |
|---------|----------------|
| `cadence verify comp.lua --json` | Compile → lint → check pipeline |
| `cadence verify … --wasm-only` | Instant tier-0 compile feedback |
| `cadence doctor` | Probe love, ffmpeg, wasmoon, project layout |
| `cadence feedback comp.lua` | Agent-readable summary + next steps |
| `cadence lint / check` | Static + pixel verification (`--json`) |
| `cadence render` | Offline encode to mp4 |
All verification commands emit **`cadence.result/v1`** JSON — structured findings agents can parse without reading stack traces.
## Vision MCP
`bin/cadence-vision` is an MCP server (registered in `.mcp.json`) that shows a
comp, clip or image to any vision or video model in the shape it reads best:
per-model profiles, keyframes, labeled contact sheets, numbered marks bound to
the comp's own node ids, scene text, diffs, and Depth Anything 3 depth and
multi-view geometry with rendered top/iso views. `bin/vision-setup` builds its
venv; `bin/cadence-vision call TOOL k=v` runs any tool from the shell.
Design and measurements: `docs/VISION.md`.
## Terminal
<p align="center">
<img src="docs/images/doctor.gif" alt="cadence doctor" width="100%" />
</p>
<p align="center">
<img src="docs/images/verify.gif" alt="cadence verify" width="100%" />
</p>
<p align="center">
<img src="docs/images/feedback.gif" alt="cadence feedback" width="100%" />
</p>
## Composition sketch
```lua
local e = require("cadence")
return e.comp {
width = 1080, height = 1920, duration = 6, fps = 30,
background = "#0B0D12",
scene = function(s)
local title = s:text { x = 540, y = 960, text = "cadence", size = 120,
color = "#5EEAD4", anchor = "center", opacity = 0 }
s:script(function(t)
t:tween(title, 0.8, { opacity = 1, size = 140 }, "backOut")
end)
end,
}
```
## Why Cadence
- **Deterministic by construction** — no clocks, no I/O in comps; RNG seeded
- **Agent-first** — lint/check/verify JSON, skill on [skills.sh](https://skills.sh)
- **~10MB engine** — vendored LÖVE fork, not headless Chrome
- **Three hosts, one API** — native encode, wasmoon preview, Rust helpers
## Renderer
Frames are painted by `cadence-scene` (vello_cpu + parley): one rasterizer for
text, shapes, images, html, vector, masks, blends, shadows, colour effects, the
fx chain and video, with no GPU readback. World (wgpu), perspective and the
GLSL escape hatches (`shadertoy`, `worley`, `s:draw`) render through love into
image slots inside the same frame. `CADENCE_SCENE=0` selects the old love
canvas path. Details, coverage and measurements: [docs/SCENE.md](docs/SCENE.md).
`bin/golden capture|compare` holds per-frame hashes across renderer changes.
## Layout
| Path | Purpose |
|------|---------|
| `bin/cadence` | CLI entry (render, verify, doctor, …) |
| `lib/cadence/` | Host-free authoring API |
| `runtime/` | LÖVE offline host (`scene.lua` = bridge to the rasterizer) |
| `scene/` | `cadence-scene`: the vello_cpu + parley rasterizer |
| `desktop/` | Tauri + React editor (optional) |
| `skills/cadence/` | Official agent skill |
| `evals/` | Public visual suite |
## Ellua → Cadence
This project was formerly **ellua**. The Lua module alias `require("ellua")` remains for compatibility; new work should use `cadence` naming. The archived repo: [github.com/shinyobjectz/ellua](https://github.com/shinyobjectz/ellua).
## License
MIT — see [LICENSE](LICENSE) if present, otherwise check repo root.