TamboUI can record any application to an asciinema (.cast) file, and can drive the application from a VHS-format tape script. Together they enable fully headless runs: scripted input in, terminal recording out — no TTY, no code changes, no extra dependencies. This is useful for demo capture, bug reports, CI verification, and letting AI agents interact with and verify TUI applications.

Recording a session

Set the tamboui.record system property to the output path:

java -Dtamboui.record=session.cast -jar myapp.jar
# or with jbang
jbang -Dtamboui.record=session.cast myapp.java

The application runs normally; every frame is captured and the cast is written when the process exits (or when the configured duration is reached). Play it back with asciinema play session.cast, or render it to a gif/svg with agg or vhs.

Recording applies regardless of how the backend was created — including applications that construct their own backend and pass it via TuiConfig.builder().backend(…​).

Recording properties

Property Default Description

tamboui.record

unset

Output .cast path; setting it enables recording

tamboui.record.config

unset

VHS-format tape file that drives the application (see below); setting it alone also enables recording, with the output derived from the tape name

tamboui.record.fps

10

Capture rate in frames per second

tamboui.record.duration

10000

Maximum recording duration in milliseconds — raise this for longer sessions

tamboui.record.width / tamboui.record.height

80 / 24

Terminal size the recording reports and captures

The numbers must be positive integers. Any other value is reported on standard error and the default is used instead.

Driving an application with a tape

Supply a tape file with tamboui.record.config and the application is driven by the scripted keystrokes instead of live input:

# explicit output path
java -Dtamboui.record=out.cast -Dtamboui.record.config=script.tape -jar myapp.jar

# single property: naming the tape enables recording, and the output
# is derived from the tape name (script.tape -> script.cast)
java -Dtamboui.record.config=script.tape -jar myapp.jar

No input file is ever read unless explicitly named — there is no automatic tape lookup — and the derived output is written to the directory the tape lives in.

Tape format

Tapes use the VHS syntax. A minimal example:

# drive the app: wait for startup, navigate, quit
Sleep 500ms
Type "hello"
Down 3
Enter
Sleep 1s
Type "q"

Supported commands:

Command Meaning

Type "text"

Type a string (supports \n, \t, \", \\, \xNN escapes)

Type@<speed> "text"

Type a string with a pause between the characters, e.g. Type@50ms "hello"

Enter, Tab, Space, Backspace, Delete, Insert, Escape

Named keys

Up, Down, Left, Right, Home, End, PageUp, PageDown

Navigation keys

F1 …​ F12

Function keys

Ctrl+<key>, Shift+<key>, Alt+<key>

Modifier combinations, also combined and on named and function keys (e.g. Ctrl+C, Shift+F8, Ctrl+Up, Shift+Tab, Ctrl+Alt+a)

<Key> <count>

Press a key several times, e.g. Down 3

Sleep <duration>

Pause, e.g. Sleep 500ms or Sleep 2s

Wait /regex/, Wait+Line /regex/

Wait until a line of the screen matches the regex (at most 15s); Wait@10s /regex/ sets the timeout

Wait+Screen /regex/

Wait until the screen, its lines joined with newlines, matches the regex

Hide, Show

The interactions between them are played but left out of the recording, together with the time they take

Screenshot

Marks a frame of interest in the recording

Source <file>

Include another tape file

<Key>@<delay> <count>

Repeat with delay, e.g. Down@100ms 5 presses Down five times, 100ms apart

Set, Output and Require directives are accepted (for compatibility with tapes shared with the vhs tool) but the recording geometry and output come from the tamboui.record.* properties. Any other line the player does not understand is reported on standard error and skipped, so a typo in a tape does not go unnoticed.

Keys are sent as a terminal sends them, the xterm way: the function, cursor and editing keys as escape sequences with the modifiers as a parameter (Shift+F8 is ESC [ 19 ; 2 ~), and Alt as an ESC prefix. Use the named keys rather than typing escape sequences with Type, which sends the characters one at a time.

Wait makes a tape independent of how long the application takes, for example to wait for data to load or for a slow backend to answer, instead of guessing a Sleep. A Wait that times out is reported on standard error and the tape continues.

Headless verification recipe

Because playback and recording need no TTY, a script or AI agent can run any TamboUI application end-to-end and assert on what happened:

cat > script.tape <<'EOF'
Sleep 500ms
Type "q"
EOF

jbang -Dtamboui.record.config=script.tape myapp.java

# the cast is plain JSON-lines: assert on rendered content
grep -q "Expected screen text" script.cast && echo OK

Each cast line is a timestamped output event containing the ANSI frame data, so expected labels, table rows, or error messages can be checked with ordinary text tools.

Relationship to pilot testing

Pilot testing drives widgets programmatically inside a JVM test with structured assertions — prefer it for unit/integration tests of your own code. Recording works from the outside with zero code access: any application, any backend, input as keystrokes, output as the actual terminal byte stream. Prefer it for black-box verification, demo capture, and reproducing bugs.