Generate beautiful screenshots of your terminal, from your terminal. Supports running commands, capturing existing output, tmux pane capture, and use as a Go library.
go install github.com/mr-pmillz/termshot/cmd/termshot@latestPre-compiled binaries for Darwin and Linux are available on the Releases page.
Prefix any command with termshot to capture its output as a PNG:
termshot ls -aThis generates out.png in the current directory.
Use -- to separate termshot flags from command flags, and wrap piped commands in quotes:
termshot --show-cmd -- "ls -1 | grep go"Use --raw-read to screenshot content from a file or stdin without running a command. This is useful in CI/CD pipelines where the output has already been generated:
# From a file
termshot --raw-read build-output.log -f build-screenshot.png
# From stdin
cat output.txt | termshot --raw-read - -f screenshot.png
# Pipe any command's output
my-build-script 2>&1 | termshot --raw-read - --columns 120 -f ci-output.pngWhen running inside tmux, use --tmux to capture the current pane with its colors and formatting intact:
# Capture the current pane
termshot --tmux -f pane-screenshot.png
# Capture a specific pane by target
termshot --tmux-pane %1 -f other-pane.png
# Combine with styling options
termshot --tmux --no-decoration --no-shadow -f clean-capture.pngThe pane width is auto-detected for correct column wrapping. You can override it with --columns.
You can also pipe tmux capture output manually:
tmux capture-pane -e -p | termshot --raw-read - -f screenshot.png
tmux capture-pane -e -p -t %2 | termshot --raw-read - --columns 120 -f pane2.pngUse --light for a light-themed screenshot, or override individual colors:
# Light mode
termshot --light -- ls -la
# Custom background and foreground
termshot --bg-color="#002B36" --fg-color="#839496" -- ls -la
# Nerd Font for icon/glyph support
termshot --nerd-font -- ls -laUse --highlight-cmd with --show-cmd to draw a red box around the command line. This is standard practice in penetration testing reports:
# Live capture with highlighted command
termshot --light -c --highlight-cmd -- nmap -sV 10.10.10.1
# After-the-fact: screenshot saved output without re-running the command
nmap -sV 10.10.10.1 > /tmp/nmap-output.txt 2>&1
termshot --light -c --highlight-cmd \
--raw-read /tmp/nmap-output.txt \
-f nmap-screenshot.png \
-- nmap -sV 10.10.10.1In the after-the-fact workflow, --raw-read provides the content and the args after -- are used only as display text — the command is not re-executed.
Use --highlight-tight to fit the box snugly around the command text instead of spanning the full width:
# Tight box — ends where the command text ends
termshot --light -c --highlight-cmd --highlight-tight -- nmap -sV 10.10.10.1
# Full-width box (default)
termshot --light -c --highlight-cmd -- nmap -sV 10.10.10.1Use --highlight-color to override the box color:
termshot -c --highlight-cmd --highlight-color="#FFA500" -- nuclei -t cves/ -u target| Flag | Short | Default | Description |
|---|---|---|---|
--show-cmd |
-c |
false |
Include the command in the screenshot |
--columns |
-C |
auto | Fixed column count for line wrapping |
--margin |
-m |
48 |
Space around the window |
--padding |
-p |
24 |
Space inside the window |
--no-decoration |
false |
Hide window buttons | |
--no-shadow |
false |
Hide window shadow | |
--clip-canvas |
-s |
false |
Remove transparent margins |
| Flag | Short | Default | Description |
|---|---|---|---|
--light |
-l |
false |
Light color theme |
--bg-color |
Override background color (hex, e.g. #FFFFFF) |
||
--fg-color |
Override foreground/text color (hex, e.g. #333333) |
||
--nerd-font |
false |
Use ZedMono Nerd Font (broader glyph/icon support) | |
--highlight-cmd |
false |
Draw a box around the command (use with -c) |
|
--highlight-tight |
false |
Fit the highlight box tightly around the command text | |
--highlight-color |
#FF0000 |
Override highlight box color |
| Flag | Short | Default | Description |
|---|---|---|---|
--filename |
-f |
out.png |
Output file path |
--clipboard |
-b |
false |
Copy to clipboard (macOS only) |
| Flag | Short | Default | Description |
|---|---|---|---|
--raw-read |
Read from file or stdin (-) instead of running a command |
||
--raw-write |
Write raw text to file instead of creating a screenshot | ||
--tmux |
false |
Capture the current tmux pane | |
--tmux-pane |
Capture a specific tmux pane by target (e.g. %1) |
||
--tmux-lines |
0 |
Capture last N lines from tmux scrollback (0 = visible pane only) |
| Flag | Short | Description |
|---|---|---|
--edit |
-e |
Edit content in $EDITOR before generating screenshot |
--version |
-v |
Print version |
The pkg/termshot package lets you render terminal screenshots from Go code. This is useful for generating documentation images in CI/CD pipelines, test reports, or any workflow where you need to convert ANSI terminal output to PNG.
go get github.com/mr-pmillz/termshotpackage main
import (
"os"
"strings"
"github.com/mr-pmillz/termshot/pkg/termshot"
)
func main() {
f, _ := os.Create("screenshot.png")
defer f.Close()
input := strings.NewReader("\x1b[32m$ go test ./...\x1b[0m\nok mypackage 0.42s")
termshot.Render(f, input, termshot.WithColumns(80))
}RenderCommand runs a command, captures its output, and renders a screenshot in one call. The command is executed once via sh -c and is never re-run. This is the easiest way to integrate termshot into Go programs like pentesting tools or CI/CD pipelines:
package main
import (
"os"
"github.com/mr-pmillz/termshot/pkg/termshot"
)
func main() {
f, _ := os.Create("nmap-screenshot.png")
defer f.Close()
termshot.RenderCommand(f, "nmap -sV 10.10.10.1",
termshot.WithLightMode(),
termshot.WithHighlightCommand(true), // red box around command
termshot.WithHighlightTight(true), // box fits the command text
termshot.WithColumns(120),
)
}When you've already captured command output (e.g. from a pipeline), use Render with WithCommand to add the command display text without re-executing it:
// output was captured earlier, command is NOT re-run
output, _ := os.ReadFile("/tmp/nuclei-results.txt")
f, _ := os.Create("nuclei-screenshot.png")
defer f.Close()
termshot.Render(f, bytes.NewReader(output),
termshot.WithCommand("nuclei", "-t", "cves/", "-u", "target"),
termshot.WithHighlightCommand(true),
termshot.WithLightMode(),
termshot.WithColumns(120),
)Generate images that fit a 7-inch content width in Microsoft Word:
f, _ := os.Create("report-output.png")
defer f.Close()
termshot.Render(f, reader,
termshot.WithColumns(80),
termshot.WithTargetWidthInches(7.0, 150), // 1050px at 150 DPI
termshot.WithDecorations(false),
termshot.WithShadow(false),
termshot.WithMargin(0),
)f, _ := os.Create("tmux-capture.png")
defer f.Close()
termshot.Render(f, nil,
termshot.WithTmux(),
termshot.WithTargetWidth(1200),
termshot.WithDecorations(false),
)Recorder is an io.Writer that buffers everything written to it and renders a PNG on demand. Use Tee to keep output visible in the terminal while recording. This is useful for logging pipelines, test harnesses, or any code that produces output through an io.Writer:
package main
import (
"fmt"
"os"
"os/exec"
"github.com/mr-pmillz/termshot/pkg/termshot"
)
func main() {
rec := termshot.NewRecorder(
termshot.WithColumns(120),
termshot.WithLightMode(),
).Tee(os.Stdout) // also print to terminal
// Route command output through the recorder
cmd := exec.Command("go", "test", "./...")
cmd.Stdout = rec
cmd.Stderr = rec
_ = cmd.Run()
// Render everything that was written
_ = rec.RenderToFile("test-results.png")
}Recorder is goroutine-safe, so multiple writers can use it concurrently. Use Reset() to clear the buffer and reuse it.
StartCapture redirects os.Stdout and os.Stderr at the file-descriptor level, so all output is captured automatically — including output from third-party libraries, child processes, and fmt.Println. Output is tee'd to the original terminal so it remains visible during capture. Call Done via defer to restore the original file descriptors and render the PNG:
package main
import (
"fmt"
"os"
"github.com/mr-pmillz/termshot/pkg/termshot"
)
func main() {
capture, err := termshot.StartCapture("output.png",
termshot.WithColumns(80),
termshot.WithLightMode(),
)
if err != nil {
panic(err)
}
defer capture.Done()
fmt.Println("This is automatically captured!")
fmt.Fprintf(os.Stderr, "Errors are captured too.\n")
// When main returns, Done() renders everything to output.png
}CaptureSession is supported on Linux and macOS (uses dup2 syscall). It is not goroutine-safe — other goroutines writing to stdout/stderr will also be captured. For goroutine-safe capture, use Recorder instead.
You can also use CaptureSession inside any function with defer to screenshot just that function's output:
func runScan(target string) error {
capture, err := termshot.StartCapture(
fmt.Sprintf("scan-%s.png", target),
termshot.WithColumns(120),
termshot.WithHighlightCommand(true),
)
if err != nil {
return err
}
defer capture.Done()
// Everything printed in this function is captured
cmd := exec.Command("nmap", "-sV", target)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}Use WithTmuxLines(n) to capture the last N lines from a tmux pane's scrollback buffer instead of just the visible area:
f, _ := os.Create("scrollback.png")
defer f.Close()
termshot.Render(f, nil,
termshot.WithTmuxLines(50), // last 50 lines (implies WithTmux)
termshot.WithColumns(120),
)| Option | Description |
|---|---|
WithColumns(n) |
Fixed column count for line wrapping |
WithTargetWidth(pixels) |
Scale output to exact pixel width |
WithTargetWidthInches(inches, dpi) |
Scale to physical size (e.g. 7.0, 150) |
WithMargin(n) |
Margin around window (default: 48) |
WithPadding(n) |
Padding inside window (default: 24) |
WithDecorations(bool) |
Window buttons (default: true) |
WithShadow(bool) |
Window shadow (default: true) |
WithClipCanvas(bool) |
Remove transparent edges (default: false) |
WithCommand(args...) |
Prepend styled command prompt |
WithHighlightCommand(bool) |
Draw a colored box around the command |
WithHighlightTight(bool) |
Fit the highlight box tightly around the command text |
WithHighlightColor(hex) |
Override highlight box color (default: #FF0000) |
WithLightMode() |
Light color theme |
WithBackgroundColor(hex) |
Override background color |
WithForegroundColor(hex) |
Override text color |
WithNerdFont() |
Use ZedMono Nerd Font |
WithTmux() |
Capture current tmux pane (reader is ignored) |
WithTmuxPane(target) |
Capture specific tmux pane |
WithTmuxLines(n) |
Capture last N lines from tmux scrollback (implies WithTmux) |
WithQuiet() |
Suppress informational stderr messages during rendering |
Note: This project is work in progress. Although a lot of ANSI sequences can be parsed, there are commands that create output which cannot be parsed correctly yet. Commands that reset the cursor position are known to create issues.