REC
TC 00:00:00:00
TAKE 08 · SC 01 · YOUR DEMO
60 FPS · BYTE-IDENTICAL

foley

The tape is the recording. foley adds the post-production:
camera, titles, sound. The footage is never touched.

A command line tool and Go library that renders terminal demos from scripts.

Read the manual
SC 00 · What foley is

A terminal demo recorder with no screen.

foley turns a script into a finished terminal demo: your real CLI or TUI running in a real terminal, recorded without one ever opening. You write a .tape file (settings, keystrokes, waits), foley runs the app on a real pty and draws every frame itself: gif, mp4, webm, webp or asciicast. Nothing is filmed; the demo is rendered, the way a foley artist recreates sound in the studio instead of keeping the set recording.

  • The README gif. Regenerated by CI on every release instead of re-recorded by hand.
  • Docs and release notes. Demos written as code, reviewed in pull requests, versioned with the app.
  • Visual regression tests. The same tape renders identical bytes, so a golden file catches any change on screen.
  • One take, every venue. The same tape exports video, asciicast or plain text.
SC 01 · What rendering buys you
NO WINDOW.

The terminal exists only inside the renderer. Nothing asks for a screen-recording permission; no stray notification ever walks through a shot.

NO CAPTURE.

Your app runs on a real pty, an embedded terminal engine (libghostty-vt, the brain of Ghostty) keeps the screen, and foley draws every frame itself. Kitty graphics render byte-exactly, because the engine underneath is a real terminal's.

NEVER BLURRY.

Every take renders from a supersampled master, so a camera is just arithmetic: the zoom is a 1:1 crop.

BYTE-IDENTICAL.

The clock is virtual: the same tape produces identical bytes on any machine, and renders faster than real time. A ~32-second script exports in under 5, and demos double as visual regression tests.

SC 02 · Post-production

Six cues, one screen.

Direction rides as # foley: cues in the tape; the pty, the timing and the terminal state stay exactly as recorded. Pick a cue; the screening room plays it.

Title cards and lower thirds composited over a terminal take A region of terminal output highlighted while the frame dims around it A reel of keystrokes rendered under the terminal window A take recorded inside a studio with a fresh HOME The take framed in a noir window chrome The camera pushing onto a region of the terminal and pulling back

Title cards and lower thirds, composited on frame. The bar can't tell it isn't vim.

Point at the output; the frame dims around it.

foley presses the keys, so the track is emitted with exact timing, not captured and guessed at.

One cue builds a fresh HOME per take. Your machine stays off camera.

Chrome for the take: macOS, noir, or none at all. Same take, only the flag changed.

Push onto a region, hold, pull back. The push-in is a 1:1 crop of the supersampled master, which is why the hold stays crisp.

Cue sheet

Direction rides as # foley: comments in the tape. The footage is never touched. Every take is real output from the engine.

SC 03 · The studio

No browser offstage. No display server on set.

Takes render headless anywhere CI runs, and the whole studio builds into a small container.

.tapethe script
ptyyour real app
libghostty-vtthe screen · kitty graphics native
rasterpinned fonts, baked in
gif · mp4 · webm · webp · castevery Output the tape declares

Reel A · Deterministic · default

A virtual clock. Output is attributed to the step that caused it, every run of a tape produces identical bytes, and rendering runs faster than real time.

Reel B · Realtime · -mode realtime

The wall clock, every byte captured as it happened: the clock a continuously animating TUI needs. Honest limits either way: waits synchronize against the real app, so they spend the time they spend.

SC 04 · Roll your own

Every VHS tape is a foley tape.

The grammar is VHS's own, vendored from the pinned release. The upstream example corpus, all 106 tapes, parses and runs under conformance tests; where foley differs on purpose, it says so loudly at run time instead of silently drifting.

Homebrew · macOS + Linux
brew install GH-Jaider/foley/foley

Brings ffmpeg with it, the one thing foley needs at runtime.

Prebuilt binary
tar xzf foley_*.tar.gz
sudo mv foley /usr/local/bin/

Self-contained: engine and fonts baked in. Grab the tarball from the latest release.

First take
foley new demo.tape
foley validate demo.tape
foley demo.tape

A starter tape, the spotting session, and the take. When in doubt, foley doctor.

Agents write tapes too
foley skill > foley-skill.md   # the whole grammar, in one file any agent loads as context