produce polished terminal demo videos from declarative configs.
tuireel executes scripted terminal interactions in a virtual pty, captures frames, and renders polished videos (mp4, webm, gif) with optional cursor overlays, keystroke hud, and sound effects. no manual recording or editing required.
define steps in a jsonc config (typing, key presses, launches, waits, pauses) and tuireel drives a headless terminal session, captures raw terminal states at configurable captureFps, then encodes the final video at the output fps you want viewers to see.
watch tuireel capture a real opencode TUI flow at 1080p
npm install -g tuireel # or: npx tuireel / bunx tuireel
tuireel init # scaffold a demo config
tuireel record # record your first demothe generated config is a jsonc file describing your demo steps:
deliveryProfile is the profile-first workflow for timing plus readability defaults. preset stays visual-only, so you can stack both when you want a polished look and a named delivery target.
scaffold a new demo config:
tuireel init
tuireel init -o my-demo.tuireel.jsonc
tuireel init --force # overwrite existing configthis creates a .tuireel.jsonc with a $schema for ide autocompletion, a default deliveryProfile, and an optional visual preset if you choose one interactively.
record a demo from config:
tuireel record
tuireel record my-demo.tuireel.jsonc
tuireel record --format webm
tuireel record --watch # re-record on config change
tuireel record --verbose
tuireel record --debugrun a config in a visible terminal without recording:
tuireel preview
tuireel preview my-demo.tuireel.jsonc
tuireel preview --verbosere-render overlays on an existing recording without re-recording:
tuireel composite
tuireel composite -c my-demo.tuireel.jsonc
tuireel composite --format gif
tuireel composite --cursor-size 4
tuireel composite --no-cursor --no-hudcheck a config file for errors without running it:
tuireel validate
tuireel validate my-demo.tuireel.jsonctuireel --help
tuireel --version
tuireel record --help| type | key fields | description |
|---|---|---|
launch |
command |
start a terminal program (e.g. vim, htop, npm) |
type |
text, speed? |
type text into the terminal character by character |
press |
key |
send a key press (enter, tab, ctrl+c, escape, etc.) |
wait |
pattern, timeout? |
wait for text/regex to appear in terminal output |
pause |
duration |
pause for a fixed duration in milliseconds |
scroll |
direction, amount? |
scroll the terminal view up or down |
click |
pattern |
click on matching text in the terminal |
screenshot |
output |
capture a png screenshot at this point |
resize |
cols, rows |
resize the terminal dimensions mid-recording |
set-env |
key, value |
set an environment variable before next command |
all steps can be used with $include to share common setup sequences across configs.
| field | default | description |
|---|---|---|
$schema |
- | json schema url for ide autocompletion |
preset |
- | preset name (polished, minimal, demo, silent) |
deliveryProfile |
- | named timing + readability bundle (for example readable-1080p) |
output |
output.mp4 |
output file path |
format |
mp4 |
output format (mp4, webm, gif) |
theme |
- | terminal color theme (name or inline object) |
sound |
- | sound effect configuration |
cursor |
- | cursor overlay settings |
hud |
- | keystroke hud overlay settings |
fps |
30 |
final output cadence for the rendered video |
captureFps |
- | raw capture cadence for terminal state sampling |
cols |
80 |
terminal width in columns |
rows |
24 |
terminal height in rows |
defaultWaitTimeout |
- | default timeout for wait steps (ms) |
steps |
required | array of step objects |
for projects with multiple demos, use the multi-video format:
{
"defaults": {
"deliveryProfile": "readable-1080p",
"preset": "polished",
"cols": 120,
"rows": 30,
},
"videos": [
{
"name": "install",
"output": "videos/install.mp4",
"steps": [
{ "type": "type", "text": "npm install tuireel" },
{ "type": "press", "key": "Enter" },
],
},
{
"name": "usage",
"output": "videos/usage.mp4",
"steps": [
{ "type": "type", "text": "tuireel record" },
{ "type": "press", "key": "Enter" },
],
},
],
}the defaults object is merged into each video definition. per-video fields override defaults.
| field | default | description |
|---|---|---|
name |
required | video identifier |
output |
required | output file path |
steps |
required | array of step objects |
preset |
inherited | preset override for this video |
format |
inherited | output format override |
theme |
inherited | theme override |
sound |
inherited | sound configuration override |
cursor |
inherited | cursor overlay override |
hud |
inherited | keystroke hud override |
deliveryProfile |
inherited | timing + readability profile override |
fps |
inherited | final output cadence override |
captureFps |
inherited | raw capture cadence override |
cols / rows |
inherited | terminal dimensions override |
presets bundle presentation defaults (theme, sound, cursor, hud) so you don't have to configure each one individually. they do not change timing behavior, so they stack cleanly with deliveryProfile.
| preset | theme | sound effects | cursor | hud |
|---|---|---|---|---|
polished |
Catppuccin | Click + Key | Visible | Visible |
demo |
Dracula | Click + Key | Visible | Visible |
minimal |
Tokyo Night | None | Visible | Hidden |
silent |
Default | None | Hidden | Hidden |
{
"deliveryProfile": "readable-1080p",
"preset": "polished",
"output": "demo.mp4",
"steps": [...]
}preset values can be overridden by explicit fields in your config, and deliveryProfile can independently set default fps, captureFps, and readability-oriented sizing.
tuireel ships with 8 built-in terminal color themes:
draculacatppuccinone-darkmonokaisolarized-darktokyo-nightnordgruvbox-dark
use a theme by name or provide a custom theme object with background, foreground, cursor, and full 16-color ansi palette:
by name:
{ "theme": "catppuccin" }or inline with full ansi palette:
{
"theme": {
"background": "#1E1E2E",
"foreground": "#CDD6F4",
"cursor": "#F5E0DC",
"colors": {
"black": "#45475A",
"red": "#F38BA8",
"green": "#A6E3A1",
// ... full 16-color palette
},
},
}tuireel includes built-in sound effects for key presses and clicks, with 4 variants each. add a background music track with volume control.
{
"sound": {
"effects": {
"click": 1, // built-in variant 1-4, or path to custom .wav/.mp3
"key": 2,
},
"track": "path/to/background-music.mp3",
"trackVolume": 0.3,
"effectsVolume": 0.5,
},
}git clone https://github.com/Microck/tuireel.git
cd tuireel
pnpm install
pnpm build| command | description |
|---|---|
pnpm build |
build all packages (via turbo) |
pnpm dev |
watch mode for all packages |
pnpm test |
run all tests (via vitest) |
pnpm lint |
lint all packages |
pnpm benchmark |
run performance benchmarks |
pnpm publish:smoke |
run publish smoke tests |
turbo build --filter=@tuireel/core |
build only core |
turbo build --filter=tuireel |
build only cli |
three github actions workflows run on every push to main and on pull requests:
- CI - lint, build, type-check, and test
- Video Smoke Tests - records mp4, webm, and gif outputs and validates them with ffprobe
- Release - automated publishing via changesets with npm oidc trusted publishing and post-publish validation
| package | description |
|---|---|
tuireel |
cli interface for recording terminal demos |
@tuireel/core |
recording engine, compositing pipeline, and overlays |
see CONTRIBUTING.md for development setup, code style, and pr process.
inspired by webreel, built on tuistory and ghostty-opentui.
{ "deliveryProfile": "readable-1080p", "preset": "polished", "output": "demo.mp4", "steps": [ { "type": "type", "text": "echo 'Hello, world!'" }, { "type": "press", "key": "Enter" }, { "type": "pause", "duration": 1000 }, ], }