The verdicts
EffectCraft
After Effects clone with a CLI. Every beat and the film finish, built inside the tool.
HyperFrames
Lint and snapshots are fast, but the page can't read its own pixels, so halation is faked.
Natron
x86 only. One frame took 4 minutes under Rosetta.
Grain at 1080p
Grain at output resolution, weighted to the midtones, encoded with -tune grain.
Study 1
EXPOSURE, 12 s
The same brief for every tool: 1920×1080, 24 fps, 288 frames. These are web copies at 6.5 Mbps.
EffectCraft 0.3.1
All 8 beats done5 builds · full render in 34 s · a check frame in ~130 ms. Text animators with an expression selector, precomps for the RGB split, Add Grain, Lumetri. Two snags: gradients can't be set from a script, and keyframes snap to whole frames.
HyperFrames 0.8.127
All beats; tracking approximated3 check runs · full render in 24 s. GSAP stagger, a clip-path gate, seeded per-frame grain on canvas. Lint forbids animating letter spacing, and halation is a blurred copy of the title.


The brief
| Time | Beat | EffectCraft | HyperFrames |
|---|---|---|---|
| 0–1.5 s | Dust motes drift in black | 60 shapes with wiggle expressions | Seeded canvas |
| 1.5–3 s | 2 px line draws out, with bloom | Scale keys + stacked Glow | scaleX + box-shadow |
| 3–4.5 s | Gate opens to 2.39:1 | Keyed rect + radial gradient | clip-path tween |
| 4.2–6.5 s | Letters build in, tracking tightens | Expression selector on textIndex | Stagger; glyphs slide in x |
| 6.5–8.5 s | Light leak + RGB split | Shift Channels ×3, ADD blend | 3 colored copies, screen blend |
| 8.5–10.5 s | Subtitle + underline | Done | Done |
| 10.5–12 s | Gate closes to a dot | Done | Done |
| Finish | Halation, grade, grain, vignette | Glow, Lumetri, Add Grain (luma bands) | Faked: blurred title copy, soft-light grain |
Tool matrix
Who got a turn
| Tool | Status | Render | Notes |
|---|---|---|---|
| EffectCraft | Done | 34 s | The most controllable. Its CLI, JSX object model and MCP server all read back every property. |
| HyperFrames | Done | 24 s | Today's default. Weak wherever an effect has to read pixels. |
| Natron | Stopped | 4 min/frame | Linear OCIO, SeGrain and Shadertoy are the right ideas, but there's no ARM build. Headless footguns: an injected frame global, Glow won't load, text isn't centered. |
| FilmCraft | Finisher only | 4.4 s / 5 s | Premiere clone, 303 effects, JSONL command runner. Its Noise lifts the blacks. |
| Diffusion Studio | Skipped | — | Browser canvas only, effects are mostly blur, free tier is watermarked. |
| Resolve 21 (free) | Blocked | — | No external scripting in the free version. |
| Osmo film emulation | Skipped | — | Web app and paid plugins. No CLI or API. |

Study 2
Grain bake-off
5 s from close-quarters, upscaled and graded to 1080p. Each tool added only the grain, then every result went through our 12 Mbps share encode. Shown at 100%.
| Variant | Master | Mid σ · master | after 12 Mbps | after 6 Mbps | Shadow / mid | Black lift |
|---|---|---|---|---|---|---|
G0 today's look.sh | 22.8 MB | 4.73 | 4.91 | 4.51 | 0.52 | −0.54 |
| G1 fixed ffmpeg | 89.7 MB | 5.28 | 5.24 | 4.52 | 0.35 | −0.22 |
| G3 FilmCraft Noise | 93.1 MB | 5.11 | 5.06 | 4.47 | 0.70 | +1.65 |
| G4 AV1 grain synthesis | — | — | 3.46 at 1.9 MB (3 Mbps) | 0.36 | −0.38 | |
| G2 EffectCraft Add Grain | 56.0 MB | Couldn't be measured: with no effect applied, EffectCraft's own passthrough already changes the plate by σ 10.6. | ||||
| G5 Natron SeGrain | — | Couldn't be measured: Natron's video reader returned 251 frames at the wrong width for a 120-frame clip. | ||||
σ is grain strength in 8-bit code values, measured on a high-passed difference from the plate. Shadow/mid: real film grain fades in the shadows, so lower is more film-like. Black lift: mean change in pure black.
Why our grain has looked bad
- Wrong resolution.
look.shadds 1-pixel noise at H3's 1344×768. When the film plays at 1080p, the grain is blown up into soft blotches. - Same strength everywhere. It adds as much noise in the shadows as in the midtones. Real film grain fades in the shadows.
- Grain eats the bitrate. The grained HyperFrames render was 75 MB against 3 MB clean at the same quality, so any re-encode that caps bitrate starves it.
- The registry overlay loops. HyperFrames'
grain-overlayis one texture cycled 20 times a second, laid on top in normal blend, so it lifts the blacks.
Recommended: use the G1 recipe, run it after upscaling to 1080p, and always encode with -tune grain. AV1 grain synthesis keeps grain at a quarter of the bits, but the player has to draw it, so check Safari and iOS first.
Next round
Other headless tools to try
Blender compositor + Geometry Nodes
Native on Apple Silicon and already set up through blenderbox. Covers what Natron was meant to: a linear compositor with glare, particles and procedural type.
VapourSynth
Python frame server with mature grain plugins. The best candidate for a reusable grain and finishing step.
Manim / Motion Canvas
Code-first animation in Python or TypeScript. Strong for diagrams and explainers, weak for film VFX.
After Effects via aerender
Same scripting language as EffectCraft, so the scripts carry over and we get a direct "real versus clone" comparison.
Lottie through Skia
EffectCraft exports Lottie, so this is a cheap route to vector motion that also plays on the web.