Render your first reel¶
By the end of this you'll have a real 1080×1920 H.264 mp4 on disk, built by Keryx from a storyboard you wrote. About fifteen minutes.
There's no narration and no music in this one, and no AI generation of any kind. That means no API keys and nothing to pay for — which makes it the fastest way to see whether Keryx is doing what you expect before you point money at it. Adding voice, music and generated card art comes later, and the how-to guides cover each.
There's also no video software to install. Keryx renders with FFmpeg compiled to
WebAssembly, fetched on first use and cached, so the whole reel is assembled by the
keryx binary itself.
Before you start¶
You'll need:
- Go 1.26 or newer, if you're installing from source.
- A network connection for the first render. Keryx fetches its render engine (about 6 MB) the first time it needs it, checks its signature, and caches it. Later renders don't re-download it.
- A directory to work in. Keryx keeps no state of its own; everything lives in the project directory you run it from, so a scratch folder is fine for now.
You do not need ffmpeg installed. If you already have one you'd rather use, the
last section shows how to switch to it.
Install Keryx¶
That puts a keryx binary in $(go env GOPATH)/bin. Check it:
Pre-built archives are attached to each release on GitLab if you'd rather not build from source.
Write the configuration file¶
Keryx refuses to run without a configuration file. Every command fails the same way until one exists:
ERRO failed to load configuration: no config file found hints="Run 'keryx init' to create a configuration."
So do that:
$ keryx init
INFO Initialising configuration
INFO configuration initialised path=/home/you/.keryx/config.yaml
That writes ~/.keryx/config.yaml with sensible defaults — every posting platform
disabled, alerting off, no themes. On a terminal it also offers to walk you through
credential set-up; you don't need any of that today, and keryx init --skip-ai
--skip-login --skip-key skips the prompts entirely.
Have a look at what it wrote. It ships with an example avatar and voice registered
under the name matt, which are the author's own — harmless, but not yours. Delete the
avatars: and voices: blocks if you find them confusing.
Add a theme, because there isn't one¶
Keryx has no built-in themes at all. Palette, fonts, card treatment, music tone and voice settings are configuration, never code — so nothing is seeded and the first generation you attempt will say so:
no theme configured — add one with `keryx theme add <keyword>` (the first theme added becomes the default)
Add a minimal theme with two palette roles and a card treatment:
keryx theme add starter \
--set 'palette.charcoal=#1A1D21' \
--set 'palette.cream=#F4F1EA' \
--set 'card.mode=block'
The first theme added becomes the default, so you won't have to name it again. card.mode=block means each card is one line of text over a solid palette
colour — the treatment that needs no illustration, which is exactly what you want with
no image provider configured.
Check it landed:
It went into ~/.keryx/themes.yaml, so it's available to every project on this
machine. A project can carry its own themes in a .keryx.yaml at its root instead.
Create a reel workspace¶
Move into your working directory and create one:
That's a directory holding everything for one reel:
workspace.yaml is what makes the directory a workspace — it records the reel's
settings, and the commands below find their work through it. storyboard.json starts
empty, vo/, cards/ and music/ hold the generated media, and .cache/ is
disposable and kept out of git by the .gitignore. The full list of what goes where is
in the workspace layout reference.
The slug has to be lowercase kebab-case — hello-keryx is fine, Hello Keryx is not.
Write a three-card storyboard¶
The storyboard is the creative seed: an ordered JSON array, one object per card. Open
reels/hello-keryx/storyboard.json — reel new left it as an empty array — and
replace it with this:
[
{ "text": "Keryx turns a post", "bg": "charcoal", "fg": "cream", "dur": 2.0 },
{ "text": "into a short reel.", "bg": "charcoal", "fg": "cream", "dur": 2.0 },
{ "text": "keryx.phpboyscout.uk", "bg": "cream", "fg": "charcoal", "mono": true, "dur": 2.5 }
]
Four things worth knowing about those fields:
bgandfgare palette role names, not colours. They have to match roles you defined on the theme —charcoalandcreamabove. A role the theme doesn't define is a validation error, which is the point: change the theme and every card follows.duris seconds on screen, and it's only used when there's no narration. Once a card has a voice-over clip, the clip's length drives the timing andduris ignored.monoswitches that card to the theme's monospace font — the convention for a closing URL card.- There's no
titleordurationfield. The full set is in the storyboard reference.
Render it¶
$ keryx reel build --workspace hello-keryx --silent
rendered 3 cards (5.7s) -> reels/hello-keryx/reel-hello-keryx.mp4
assets: 0 selected, 0 locked
--silent says "video only, no narration and no music", and it's what makes each
card fall back to its dur. Without it, Keryx expects promoted voice-over clips and
will name any card that has neither a clip nor a dur.
On an interactive terminal you'll see a rendering… NN% line while it works. It's
written to stderr, so piping the output somewhere leaves you with just the result.
The reel is 5.7 seconds rather than the 6.5 you might expect from adding up the dur
values. Cards crossfade into each other by 0.4 s, and two transitions cost 0.8 s.
Play it. Three cards, a crossfade between each, no sound.
If you happen to have FFmpeg installed anyway, you can confirm the shape from the command line — this is the one step in the tutorial that needs it, and skipping it costs you nothing:
$ ffprobe -v error -show_entries stream=width,height,codec_name \
-of default=nw=1 reels/hello-keryx/reel-hello-keryx.mp4
codec_name=h264
width=1080
height=1920
Change something and watch it change¶
The build is cheap and repeatable, so this is the loop you'll spend most of your time in. Swap the palette roles on the last card:
Run the same build again. It overwrites the mp4 in place — nothing accumulates, and nothing else in the workspace is touched.
Get a palette role wrong and the build stops before rendering anything:
$ keryx reel build --workspace hello-keryx --silent
ERRO invalid storyboard:
- card 1: bg references undefined palette role "nosuchrole"
You can also see what a build would do without doing it:
$ keryx reel plan --workspace hello-keryx
reel plan: hello-keryx [silent fallback (storyboard dur — no promoted VO)]
card dur start vo_delay source
1 2.000 0.000 0.500 storyboard
2 2.000 1.600 2.100 storyboard
3 2.500 3.200 3.700 storyboard
total: 5.700s music bed: 35.000s (35000ms)
assets: 0 selected, 0 locked
5 pre-build gap(s):
- [missing_vo] card 1: no VO — silent fallback
- [missing_vo] card 2: no VO — silent fallback
- [missing_vo] card 3: no VO — silent fallback
- [missing_cover] no cover.png
- [no_music] no music bed
The start column shows the crossfade at work — card 2 starts at 1.6 s, not 2.0 s.
The gaps are expected here; every one of them is something you haven't generated yet.
The 35 s music bed is the fallback length used when no narration exists to size a bed
against.
Rendering with your own ffmpeg instead¶
The render you just did used the WebAssembly engine, which is why you didn't install anything. If you already have an FFmpeg build you'd rather use — a tuned one, or one with codecs the published engine doesn't carry — switch to it:
That shells out to ffmpeg on your PATH, so ffmpeg -version needs to print
something before it'll work.
One capability only the system backend has. VO take screening (voice takes
--screen, voice pick --best) uses FFmpeg's silencedetect to find the take with the
fewest internal pauses. The published WebAssembly module is built lean and doesn't
include that filter, so on the default engine screening ranks takes on length alone
and tells you it's doing so:
That doesn't matter for this tutorial — there's no narration in it. It starts to matter when you're picking between five reads of the same line.
How fast the default engine is depends on your machine. Where a native driver is published for your platform, Keryx fetches that and renders in seconds. Where one isn't, it falls back to running the engine in WebAssembly, which is dramatically slower — minutes rather than seconds for a short reel, on a typical laptop.
The gap is not fixed. Building a reel is encoding work, which is the hardest thing for the WebAssembly engine — it runs on one core and without the CPU's video instructions. The native engine uses every core you have, so fewer cores narrow the gap and more cores widen it. Keryx tells you which engine it used, so a long render is never a mystery.
Native drivers exist for linux/amd64 today, with more planned.
Where to go next¶
- Make a reel, stage by stage — add narration, a music bed and generated card art, taste-gating each stage.
- Configuration keys — every key, its default, and what happens when it's wrong.
- What Keryx does not do — worth ten minutes before you build a workflow on it.