Web Player
@termless/web-player mounts an xterm.js terminal in the browser and replays termless recordings:
.castfiles replay recorded terminal output exactly..tapefiles replay a browser-safe input timeline with local echo. Use.castwhen visitors need to see exact command output, or wireonInputto a live backend if the page should drive a remote process.
Install
bash
bun add @termless/web-playerImport xterm.js CSS once in the page or app:
typescript
import "@xterm/xterm/css/xterm.css"Embed a Cast
typescript
import "@xterm/xterm/css/xterm.css"
import { createTermlessPlayer } from "@termless/web-player/browser"
const source = await fetch("/demos/km.cast").then((response) => response.text())
const player = createTermlessPlayer(document.querySelector("#terminal")!, source, {
filename: "km.cast",
})
await player.play()Add Controls
The player returns a small controller API:
typescript
play.onclick = () => player.play()
pause.onclick = () => player.pause()
resume.onclick = () => player.resume()
restart.onclick = () => player.play({ startAtMs: 0, reset: true })
seek.oninput = () => player.seek(Number(seek.value))Use player.state() to read { status, currentTimeMs, durationMs }.
Embed a Tape
typescript
import { createTermlessPlayer } from "@termless/web-player/browser"
const tape = await fetch("/demos/km.tape").then((response) => response.text())
const player = createTermlessPlayer(document.querySelector("#terminal")!, tape, {
filename: "km.tape",
defaultTypingSpeed: 30,
})For .tape, Type, key commands, Sleep, Hide, Show, Set Width, and Set Height become playback events. Source and Require are reported as warnings because browser playback has no filesystem or shell.
Timeline API
For custom renderers, compile first and provide your own terminal sink:
typescript
import { compilePlaybackSource, createPlaybackController } from "@termless/web-player"
const playback = compilePlaybackSource(source, { filename: "demo.cast" })
const controller = createPlaybackController(playback, {
reset: () => term.reset(),
resize: (cols, rows) => term.resize(cols, rows),
write: (data) => term.write(data),
})
await controller.play({ speed: 2 })The compiled timeline is plain data, so docs sites can pre-load, inspect, or transform recordings before mounting the terminal.