Screenshots in, a launch film out. Drop #011 of Openware · replaces Clueso.
Describe your product, drop in a few screenshots, and flowy writes the script against your own model, animates it into a beat-locked motion-graphics film, and renders the 1080p MP4 on your machine. No account, no credits, no export minutes, no watermark.
cp .env.example .env # add a model, or leave it and use the built-in outline
docker compose up
# http://localhost:4327Read this part first.
- The render engine is Remotion, which is source-available, not MIT. It is free for individuals, non-profits, and for-profit companies with up to 3 employees. A larger for-profit needs a company licence from Remotion. flowy's own code is MIT; the engine underneath it is not, and no amount of self-hosting changes that. It is the one place in this repo where the thing you cloned still has a pricing page behind it. Everything else here — the script, the score, the type, the render — is yours.
- 16:9 only. No vertical, no square. The engine's board grammar is composed for a wide frame and would need real work, not a config flag, to do anything else.
- It does not record your screen. You bring screenshots; it makes motion graphics out of them. If you want a cursor moving through your product, this is the wrong tool.
- There is no narration. Films are scored, not spoken. The engine has the timing
machinery for per-scene voiceover —
audio-planalready ducks the music around it — but nothing in this drop produces the audio, and shipping an untested integration against a paid API I could not exercise would be worse than saying so. The script the model writes still contains the spoken lines, so wiring a voice in later is a route and a file, not a redesign. - Rendering is CPU-bound, and slower in Docker than you will expect. Measured on a
12-core M-series Mac: a 32.6-second film in 83 seconds natively and 195 seconds
in the container — Docker on macOS runs this workload about two and a half times
slower than the host. There is no queue to wait behind, but there is no render farm
either. Native is
npm run dev. - The three background worlds and the stock stills are fixed assets. flowy composes and scores your film; it does not generate new footage.
- One render at a time, and a restart loses the one in flight. The queue lives in the web process, because a queue that needs its own service is a second command. An interrupted render is marked failed on the next boot rather than left spinning.
- Your model writes the script. One call, over the same
LLM_BASE_URL/LLM_MODEL/LLM_API_KEYcontract every drop in this repo uses. Ollama, OpenRouter, Groq, Cerebras, anything OpenAI-compatible. - Or nothing does. The built-in outline builds a complete, usable script from the brief alone, with no model involved. It is a button you press on purpose, not a degraded mode you get silently when a key is wrong.
- A real film, not a slideshow. Beat-locked cuts, camera moves, morph carries between
boards, kinetic type, a screenshot collage cold open, and a stat scene that counts up to
its number with the digits grouped, so a million reads as
1 000 000while it rolls rather than as a wall of zeroes. Write the stat as1,000,000, not1M—1Mparses as the number one with an "M" after it, and counting from zero to one looks static. - Pace is a dial.
film.pacemultiplies every board's length: 1 is the composed pacing, 0.55 is a hard cut roughly every 1.8 seconds, which is what a vertical feed wants. The end card's floor scales with it — at the composed pacing it was 40% of a ten-second cut. Accents are clamped back inside their board, so shortening one never fires a bloom over the next. - An original score. Two tracks, composed offline by
npm run synth:tracks— supersaw pads, sidechain pump, drum bus, ping-pong plucks, Schroeder reverb — at an exact 118 BPM. The film's beat grid is derived from the track, so every cut lands on the music rather than near it. - Live preview before you render. The browser plays the actual composition, fully animated, at full quality. Editing a scene changes the preview immediately. The MP4 is the same thing, encoded.
- Everything on disk. One
data/directory: the database, your screenshots, your renders. Back it up by copying a folder.
demoflow reads .env beside the compose file. All of it is optional.
| Variable | Default | What it does |
|---|---|---|
LLM_BASE_URL |
http://localhost:11434/v1 |
Any OpenAI-compatible endpoint |
LLM_MODEL |
qwen2.5-coder:7b |
The model that writes the script |
LLM_API_KEY |
— | Empty for a local model |
RENDER_CONCURRENCY |
2 in Docker, Remotion's choice otherwise |
Frames rendered at once |
RENDER_TIMEOUT_MS |
600000 |
Ceiling on one render step. A deadlock bound, not a budget |
With no model configured at all, the health strip on the home page says so in the words you would need to fix it, and the outline button still produces a film.
npm install
npm run dev # http://localhost:4327
npm test # 76 tests
npm run studio # Remotion Studio, for working on the engine itselfThe first render outside Docker builds the composition bundle once (about twenty
seconds) and caches it in data/bundle. The container builds it into the image instead,
so a containerised render never pays for it.
| Piece | Where | Note |
|---|---|---|
| The film engine | src/remotion/film/ |
Beat grid, board plan, camera, transitions, stations. Pure TypeScript, 42 tests, no React in the planners |
| Script writing | src/lib/script.ts |
Model or outline. Scene durations are computed in code — a language model asked for frame counts returns a film that is eight seconds or four minutes long |
| Rendering | src/lib/render.ts · src/lib/queue.ts |
@remotion/renderer in-process, one at a time |
| Storage | src/lib/store.ts |
SQLite, three tables, no user_id on anything |
| Typography | public/fonts/ |
Self-hosted. See below |
The engine used to load Poppins, Geist, Inter and Instrument Serif through
@remotion/google-fonts, which ships no font files at all — it injects @font-face
rules pointing at fonts.gstatic.com, 54 of them for Poppins alone. Every render fetched
its typography from Google, and on a machine with no route to the internet the film
rendered in fallback system fonts without failing, because a render resolves whether or
not the font arrived. The only symptom was that the type looked wrong.
All five families are now committed as woff2, latin subset, 220 KB total, under the SIL
Open Font License. next/font/local serves the app chrome from the same files, so the
build does not need the network either.
The film goes one step further and embeds its eight faces as data URIs
(font-data.ts, regenerate with
npx tsx scripts/inline-fonts.ts). Remotion keeps more browser pages open than it
renders with, Chrome throttles resource loading in a page that is not in the foreground,
and an idle page would start eight font requests and finish none of them — holding a
delayRender handle open until it aged past the timeout and killed a render that was
otherwise going perfectly. Nothing to fetch, nothing to stall.
Built on Remotion — see the licence note at the top. The score is
synthesized by scripts/compose-tracks.ts. The three
background loops and the stock stills were generated for this project; provenance is in
public/film/README.md. Fonts are OFL — see
public/fonts/LICENSE.md.
flowy's own code is MIT.