docs: new README demo GIF, recorded from a reproducible VHS tape (#1068)

* docs: new README demo GIF, recorded from a reproducible VHS tape

The old demo.gif was 856x705 at about 2 fps over 46 s, and predates most
of the current TUI.

The new one is 1527x841 at 20 fps over 31 s and is smaller (1.30 MB vs
1.50 MB). It tells one story with 1.1.16: the default ranked list, a
search for the newly added zai-org/GLM-5.3 (too tight on the recording
machine), hardware simulation turning it into a fit, the detail view, the
plan view, then theme cycling.

assets/demo.tape and scripts/record_demo.sh make it re-recordable for
future releases. The script encodes the GIF itself rather than letting
VHS do it: dither=none keeps flat terminal colours clean (dithering is
what makes terminal GIFs grainy, and it bloats them), and 20 fps is an
exact GIF delay so playback speed is right. It also works around two VHS
0.12 problems hit while recording: its encode step fails silently against
ffmpeg 9, and its frame output fails silently when the destination is on
a different filesystem from its temp dir.

* docs: move the demo above Features and start it on the loaded TUI

The GIF sat at the end of the Sister projects section, where it read as
belonging to those projects. It now sits directly above Features, with
descriptive alt text, and the Features heading gets the blank line it was
missing.

The recording opened with the prompt and then a 'Loading: Detecting system
hardware...' screen until about 2.9 s, which is dead time in a looping
README GIF. The launch now happens inside the tape's hidden setup, so the
first visible frame is the loaded TUI. 29 s, was 31 s.

* docs(demo): make record_demo.sh work on macOS and clean up on early exit

Review follow-ups on #1068:
- `mktemp --suffix` is GNU-only, so the script died on macOS, the platform
  its `brew install vhs` instruction points at. The palette now lives in
  the temp working directory.
- The EXIT trap is registered as soon as that directory exists, so a
  missing dependency or a failed recording leaves nothing behind, and the
  tape's throwaway config dir moves under it too.

---------

Co-authored-by: Alex Jones <axjns@example.com>
This commit is contained in:
Alex Jones
2026-09-20 07:50:49 +01:00
committed by GitHub
co-authored by Alex Jones
parent 2ac77f5e0d
commit 78f05e7bfd
4 changed files with 122 additions and 2 deletions
+3 -2
View File
@@ -23,6 +23,9 @@
Find out which open-source Large Language Models (LLMs) your hardware can comfortably run. `llmfit` inspects your CPU, system RAM, GPU(s), VRAM, and accelerator configuration to recommend models across popular quantizations.
**📊 New: benchmark & share — real numbers from your machine, better estimates for everyone.** Download a model, serve it, and measure real tok/s on your hardware — then contribute the results back to the project as a PR, straight from the TUI. No `gh` CLI, no third-party account. Every run is saved locally first, your own measurements replace estimates in the fit table, and each merged submission ships in the next release: anyone on identical hardware gets measured `✓` numbers before they ever run a benchmark. [Follow the step-by-step benchmarking guide →](docs/benchmarking.md)
![llmfit demo: searching for a model, simulating different hardware, and planning a deployment](assets/demo.gif)
## Features
- **Hardware Auto-Detection**: Detects CPU cores, system RAM, available discrete/integrated GPUs, VRAM, and unified memory architecture (NVIDIA CUDA, Apple Silicon, AMD ROCm, Intel OneAPI).
@@ -45,8 +48,6 @@ Ships with an interactive TUI (default) and a classic CLI mode. Supports multi-G
- [llama-panel](https://github.com/AlexsJones/llama-panel) — a native macOS app for managing local llama-server instances.
- [llmfit-gui](https://github.com/raiyyan729-cloud/llmfit-gui) — a Windows desktop GUI (PowerShell + WinForms) for llmfit: browse recommendations, download into LM Studio/Ollama, and benchmark, all point-and-click.
![demo](assets/demo.gif)
---
## Documentation
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

+62
View File
@@ -0,0 +1,62 @@
# README demo for llmfit. Render with: scripts/record_demo.sh
#
# Story (~29s): default ranked list -> search for a newly added model that is
# too big for this machine -> simulate bigger hardware and watch it fit ->
# detail view -> plan view -> back to the list, cycle themes.
#
# Frames are written as PNGs and encoded by the script rather than by VHS, so
# the GIF can be quantised without dithering (flat TUI colours need none, and
# dithering is what makes terminal GIFs look grainy).
Output demo-frames/
Require llmfit
Set Shell "bash"
Set FontSize 14
Set Width 1560
Set Height 860
Set Padding 12
Set Framerate 24
Set TypingSpeed 80ms
Set Theme "Catppuccin Mocha"
Set CursorBlink false
# Hidden setup:
# - a throwaway config dir (under the script's temp working directory, so it
# is cleaned up with it), so the recording shows default sort, filters and
# theme instead of whatever the person recording has saved;
# - the launch itself. Startup shows a "Loading: Detecting system hardware..."
# screen for a second or two, which is dead time in a looping README GIF, so
# the first visible frame is the loaded TUI. Keep this sleep comfortably
# longer than startup on the recording machine.
Hide
Type "export XDG_CONFIG_HOME=$PWD/cfg && clear && llmfit" Enter
Sleep 4s
Show
# Hold on the default ranked list.
Sleep 2s
# 1. Find a newly added model: too big for this machine.
Type "/" Sleep 500ms
Type "zai-org/glm-5.3" Sleep 1.4s
Enter Sleep 2.4s
# 2. Simulate bigger hardware (1 TB RAM, 640 GB VRAM) and watch it fit.
Type "S" Sleep 1.3s
Ctrl+U Type "1024" Sleep 500ms
Tab Sleep 400ms
Ctrl+U Type "640" Sleep 1.1s
Enter Sleep 3.2s
# 3. Detail, then plan, on the simulated machine.
Enter Sleep 3.6s
Enter Sleep 500ms
Type "p" Sleep 3.6s
Escape Sleep 600ms
# 4. Back to the full list, cycle themes.
Type "/" Sleep 200ms Ctrl+U Sleep 200ms Enter Sleep 1.4s
Type "t" Sleep 1.3s
Type "t" Sleep 2.4s
+57
View File
@@ -0,0 +1,57 @@
#!/usr/bin/env bash
# Re-record assets/demo.gif from assets/demo.tape.
#
# Needs: vhs (brew install vhs), ffmpeg, and the llmfit you want to show on PATH.
# Run from the repository root: scripts/record_demo.sh
#
# VHS renders PNG frames (a text layer and a cursor layer); this script
# composites them and builds the GIF itself because:
# - dither=none keeps flat terminal colours clean. Dithering is what makes
# terminal GIFs grainy, and it also bloats them.
# - 20 fps is exactly 5 centiseconds, the GIF delay unit, so playback speed
# is exact (24 fps would round to 25 and run 4% fast).
# - VHS 0.12's own encode step fails silently against ffmpeg 9.
set -euo pipefail
TAPE="$PWD/assets/demo.tape"
OUT="assets/demo.gif"
# VHS renders into its temp dir and renames the frames into place, which
# fails silently across filesystems (tmpfs /tmp -> the repo). Run it from a
# temp working directory so the rename stays on one filesystem.
WORK="$(mktemp -d)"
# Registered before anything can fail, so an early exit leaves nothing behind.
# The tape keeps its throwaway config dir under $WORK too.
trap 'rm -rf "$WORK"' EXIT
FRAMES="$WORK/demo-frames"
FPS=20
BG=0x1e1e2e # Catppuccin Mocha background, matches the tape's theme
PAD=12 # matches `Set Padding` in the tape
command -v vhs >/dev/null || { echo "vhs not found: brew install vhs" >&2; exit 1; }
command -v ffmpeg >/dev/null || { echo "ffmpeg not found" >&2; exit 1; }
[ -f "$TAPE" ] || { echo "run from the repository root" >&2; exit 1; }
echo "Recording with $(llmfit --version)"
(cd "$WORK" && vhs "$TAPE" >/dev/null 2>&1)
[ -f "$FRAMES/frame-text-00001.png" ] || { echo "vhs produced no frames" >&2; exit 1; }
SIZE="$(ffprobe -v error -select_streams v:0 -show_entries stream=width,height \
-of csv=p=0:s=x "$FRAMES/frame-text-00001.png")"
W="${SIZE%x*}"
H="${SIZE#*x}"
BASE="[0:v][1:v]overlay=format=auto,pad=$((W + 2 * PAD)):$((H + 2 * PAD)):${PAD}:${PAD}:color=${BG},fps=${FPS}"
IN=(-framerate 24 -i "$FRAMES/frame-text-%05d.png" -framerate 24 -i "$FRAMES/frame-cursor-%05d.png")
PALETTE="$WORK/palette.png" # not `mktemp --suffix`: BSD mktemp (macOS) lacks it
# One palette across the whole clip (it spans three TUI themes), then
# quantise without dithering.
ffmpeg -v error -y "${IN[@]}" \
-filter_complex "${BASE},palettegen=stats_mode=full:max_colors=256:reserve_transparent=0[p]" \
-map "[p]" "$PALETTE"
ffmpeg -v error -y "${IN[@]}" -i "$PALETTE" \
-filter_complex "${BASE}[v];[v][2:v]paletteuse=dither=none:diff_mode=rectangle" \
-loop 0 "$OUT"
ffprobe -v error -select_streams v:0 -show_entries stream=width,height,duration \
-of default=nw=1 "$OUT" | tr '\n' ' '
echo; ls -la "$OUT" | awk '{printf "%s %.2f MB\n", $9, $5/1048576}'