making-kinescript-videos · v0.2.0 · 2026-11-07 · sha256 00ef0111c091d86e
making-kinescript-videos v0.2.0A
Immutable. This exact content never changes and is served at /api/v1/blob/00ef0111c091d86e.
--- name: making-kinescript-videos description: "Make an MP4 from a JSON scene file with Kinescript (drknowhow/kinescript, the `kine` CLI): text, shapes, charts, SVG, images, video clips, particles and 3D models on cue-based timing, with a checker that lints layout, contrast, reading time and the sound mix, and narration spoken by Kokoro TTS with word-level cues and captions. Covers setup in a Linux container (CPU backend, Python 3.12 venv for Kokoro), the write → voice → check → look → render loop, and controlling Kokoro's pronunciation, stress, pauses, pace and voice blends. Use for 'kine', 'Kinescript', 'kinescript scene', 'kine voice', 'narrated promo', 'explainer video', 'motion graphics video', 'video with voice-over and captions', 'Kokoro pronunciation', 'TTS says a name wrong', 'make the narrator emphasise', 'video thumbnail is blank', 'poster frame', or any video built from type, shapes and captured footage." metadata: version: 0.2.0 --- # Making videos with Kinescript Kinescript renders a JSON scene to video with Skia and ffmpeg. Layers start and end on cues (`"at": "title.out+0.2"`) rather than on hand-summed seconds, and every frame is a pure function of the scene and the frame number. The `kine` CLI also lints the scene (`check`), draws labelled contact sheets (`look`) and speaks narration (`voice`). Its author is Dimitri (GitHub `drknowhow`). Oskar Austegard, who maintains this skill collection, contributes as a collaborator. Muninn is the name of the Claude agent that works for him. This file was last checked against Kinescript 1.6.1 (2026-11-01), and upstream releases often. **`kine docs` is the authoritative reference for the installed version.** Read it before writing a scene, and don't rely on feature lists remembered from earlier sessions, this file included. ## When NOT to use this skill | Situation | Use instead | |---|---| | Real web UI in a phone frame, arbitrary DOM/CSS layout | `making-remotion-videos` | | AI-generated footage (Veo, Gemini video) | `creating-video` | | Trim, transcode or caption existing footage | `processing-video` | | Hand-drawn or p5.js character animation | `animating-with-code` | ## Setup in a Linux container ```bash sudo apt-get install -y ffmpeg libegl1 libgl1 libfontconfig1 # skia needs EGL even on the CPU backend pip install kinescript "skia-python>=144" kine doctor ``` - Pin skia-python ≥ 144. `pyproject.toml` allows ≥ 87, but 138.0 crashes. - `canvas.poster` (step 6) is merged upstream (drknowhow/kinescript#124) but not on PyPI in 1.7.1. Until a later release, install with `pip install git+https://github.com/drknowhow/kinescript`; `kine docs` mentions `poster` when the installed version has it. - With no GPU, `KINE_BACKEND=cpu` is used. Expect 3–7 fps at 1080p. A 55 s, 1650-frame promo (the games promo in `oaustegard/experiments/games-promo`, cited below) took 231 s at `-j 4`. - **In `oaustegard/claude-workspace` sessions**, that repo's rules require third-party code to run inside its sandbox, `scripts/jail.sh` (no network, no secrets). Install from PyPI outside it and run `kine` through it. ### Kokoro for `kine voice` `kine voice` needs a voice plugin. Kokoro's (`kinescript-kokoro`) needs Python 3.10–3.12. If the system Python is newer, install the plugin only in its own venv (here with `uv`) and point `KINE_VOICE_CMD` at it; `kine` stays in the system Python: ```bash uv venv -p 3.12 ~/kokoro-env VIRTUAL_ENV=~/kokoro-env uv pip install --index-strategy unsafe-best-match \ --extra-index-url https://download.pytorch.org/whl/cpu torch kinescript-kokoro export KINE_VOICE_CMD="$HOME/kokoro-env/bin/python -m kinescript_kokoro" # misaki downloads spaCy's en_core_web_sm through uv on first use; if that fails with # "No virtual environment found", run it once with VIRTUAL_ENV=~/kokoro-env set ``` The CPU torch index avoids a multi-GB CUDA download. The first `kine voice` run downloads the model (327 MB) and spaCy's small English model. ## Procedure 1. **Read `kine docs`** for layer types, cue syntax, motions and the sound rules. `kine new` writes a starter scene. 2. **Storyboard in writing**: scenes, their order, and the one thing the viewer must take from each. For anything longer than a few layers, generate the scene from a Python builder that holds one timeline array (the games promo's `kine/build_scene.py` is an example). Don't hand-edit 500 lines of JSON. 3. **Narration**: in the scene's `audio` list, give each voice sound a `text`, a `speaker` and a `rate`, then `kine voice scene.json`. It re-speaks only sounds whose text, speaker or rate changed. It writes the audio and a words file of word timings. Mark a word with `[[go]]` before it to cue a layer from it (`"at": "vo1:go"`). Before speaking anything, read [references/kokoro-speech.md](references/kokoro-speech.md), which covers names, numbers, stress, pauses, pace and blends. 4. **`kine check scene.json`**: fix every error, read every warning, run it again. The warnings for tiny text and reading time are worth acting on: they pushed the games promo's type to ≥ 44 px and its sublines to five words or fewer. 5. **`kine look scene.json --boxes`** (and `--audio` once there is sound): read the contact sheet image before rendering. `kine still` renders one frame. `kine probe --at T` gives every layer's resolved box. 6. **Poster frame**: set `canvas.poster` to a moment that shows the title, e.g. `"poster": "title.in+0.5"` (seconds, a marker, a cue or a word). Feeds, players and a `<video>` without a poster show frame 0 before play, and a scene that fades in from its background shows up there as a blank rectangle. With `poster` set, the render draws that frame as frame 0 (one frame, 1/fps s, before the opening; audio unchanged). `kine check` warns `blank-first-frame` when frame 0 is empty and no poster is set. For a page, `kine still scene.json --at poster -o poster.jpg` gives the `<video poster>` image. `--no-poster` keeps the real frame 0 for a master. 7. **Render**: `kine render scene.json -o out.mp4 -q high -j 4`. Long renders belong in a background job. The result's `"poster"` names the frame used. 8. **Verify** the file: `ffprobe` for size, fps and duration, a frame strip (`ffmpeg -vf "select='not(mod(n\,90))',scale=270:-1,tile=8x2"`) whose first tile is the poster, and a listen to the voice track. ## Narration - **Pace.** Oskar wants narration at 165–180 wpm, energetic, with varied intonation. Punctuation pauses slow real narration well below the 200–240 wpm a bare sentence gives at rate 1: lines of two or three sentences in the 2:1 fable/lewis blend measured 139 wpm at rate 0.78 and 179 at 0.95. Start near `"rate": 0.95`, measure words ÷ speaking time from the words files after `kine voice`, and scale the rate. - **Mix.** Give sounds a `role` and Kinescript levels them: voice at −18 LUFS, music at −16 and ducked 14 dB under a voice, with no manual mixing. - **Cue from words, not from the file start.** A Kokoro clip opens with 0.3–0.4 s of silence. - **Kokoro cannot raise its pitch for a question or act on tags such as `[laughs]`.** For a line that needs either, set that sound's `backend` to `elevenlabs` or `chatterbox` (see `kine docs`, SOUND). - Muninn's voice is `"speaker": "bm_fable,bm_fable,bm_lewis"`, a 2:1 blend of fable's rasp and lewis's low register. ## Gotchas seen in use The version in brackets is the one where each was seen. Check it against `kine docs` and the CHANGELOG before working around it. - **Group children are placed from the group's anchor, not its top-left corner** (1.4.0). A centre-anchored group needs a child at `(-w/2, -h/2)` to fill it. `kine check` reports a misplaced child as off-screen. - **`loop: true` does not lengthen a video layer** (1.4.0). Give the layer a `dur`. - **Video and shader layers default to a top-left anchor** even when `x` is given (1.3.1, by design). Set `anchor` explicitly. - **Capturing a web page as footage** frame by frame with Playwright's fake clock: call `page.clock.install()`, reload, then pause the clock and step it. Installing it after load stalls most games. WebGPU pages also need the GPU queue drained before each screenshot. The games promo's `capture/lib.cjs` does both. - The Kinescript repo's `CLAUDE.md` mandates the maintainer's `c3_*` MCP tools. Where they are not installed, its first line says to use the usual tools. ## Contributing upstream Work on a branch of `drknowhow/kinescript` and open a PR. Dimitri merges his own work as PRs too, so never commit to its `main`. Run `python -m pytest` first. A visual change also needs `kine check` and `kine look --boxes`.