VHS, créé par Charmbracelet, génère des GIFs et vidéos de sessions terminal à partir d'un fichier déclaratif .tape. Plutôt que de filmer votre écran, vous décrivez le scénario (commandes tapées, sorties, pauses, style), et VHS le rejoue pour produire un rendu propre, reproductible et versionnable.
Ce guide s'adresse à toute personne qui documente des outils en ligne de commande. Vous allez installer VHS et ses prérequis, écrire votre premier fichier .tape, générer un GIF, puis automatiser la régénération en CI.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Installer VHS et ses prérequis (
ffmpeg,ttyd). - Écrire un fichier
.tape(instructionsType,Enter,Sleep). - Générer un GIF, une vidéo MP4 ou WebM.
- Personnaliser le rendu avec les directives
Set. - Automatiser la régénération des GIFs en CI/CD.
Prérequis & installation
Section intitulée « Prérequis & installation »Cette section commence par l'installation des prérequis système, puis explique plusieurs méthodes d'installation de VHS. L'objectif est que même un débutant en ligne de commande puisse exécuter une première tape en quelques minutes.
Étape 1 : Installer les prérequis
Section intitulée « Étape 1 : Installer les prérequis »Avant d'installer vhs, assurez-vous que les composants indispensables sont disponibles :
ffmpeg: encode les images successives en GIF / MP4 / WebMttyd: fournit le terminal web que VHS pilote pour composer les images- Un shell : bash / zsh / fish (bash par défaut sur la plupart des distributions)
Les deux binaires sont obligatoires et doivent être présents dans le PATH,
la documentation amont le précise explicitement. Sur Debian, Ubuntu, Fedora et
RHEL, aucun paquet officiel de ttyd n'est tiré par l'installation de VHS :
c'est une étape manuelle.
Installation de ffmpeg
Section intitulée « Installation de ffmpeg »Commandes d'installation selon l'OS :
# Debian / Ubuntusudo apt update && sudo apt install -y ffmpeg curl git
# Fedora / Rocky / Alma / RHELsudo dnf install -y ffmpeg curl git
# Arch Linuxsudo pacman -Syu --needed ffmpeg git curl
# macOS (Homebrew)brew install ffmpeg
# Windows (PowerShell) - installation ffmpeg via wingetwinget install Gyan.FFmpegVérifiez ensuite :
# Version (utile pour diagnostiquer un build manquant de codecs)ffmpeg -versionSi ffmpeg est absent, l'encodage échouera (GIF/vidéo non produits).
Installation de ttyd
Section intitulée « Installation de ttyd »Le projet ttyd publie des binaires statiques directement, sans archive : le
fichier ttyd.x86_64 est l'exécutable lui-même. Chaque release fournit aussi un
fichier SHA256SUMS, ce qui permet de vérifier le téléchargement avant de
l'installer dans le PATH. Cette vérification n'est pas une formalité : elle
détecte aussi bien une altération volontaire qu'un téléchargement tronqué par un
proxy d'entreprise.
# Exemple pour Linux x86_64curl -fsSLO https://github.com/tsl0922/ttyd/releases/latest/download/ttyd.x86_64curl -fsSLO https://github.com/tsl0922/ttyd/releases/latest/download/SHA256SUMS
# Ne vérifier que la ligne correspondant à notre architecturegrep ' ttyd.x86_64$' SHA256SUMS | sha256sum --check -
sudo install -m 0755 ttyd.x86_64 /usr/local/bin/ttydttyd --versionLa commande sha256sum --check doit répondre ttyd.x86_64: Réussi (OK en
anglais). Toute autre réponse impose de supprimer le fichier et de
recommencer. ttyd --version affiche ensuite la version et le hash du commit,
par exemple ttyd version 1.7.7-40e79c7.
Étape 2 : Installer VHS
Section intitulée « Étape 2 : Installer VHS »Choisissez UNE méthode (la plus simple pour vous). Les résultats sont équivalents.
Méthode A – Homebrew (macOS & Linux)
Section intitulée « Méthode A – Homebrew (macOS & Linux) »Homebrew gère la mise à jour et la désinstallation, c'est la voie la plus courte sur macOS. Sur Linux, elle suppose un Homebrew déjà en place, ce qui est rarement le cas sur un serveur.
brew install vhsvhs --versionMéthode B – Dépôt de paquets signé
Section intitulée « Méthode B – Dépôt de paquets signé »C'est la méthode à privilégier sur un poste ou un serveur de long terme : le dépôt Charm est signé, les mises à jour arrivent avec celles du système, et la vérification de signature est faite par le gestionnaire de paquets à chaque installation.
# Debian / Ubuntu : ajout du dépôt Charm signésudo mkdir -p /etc/apt/keyringscurl -fsSL https://repo.charm.sh/apt/gpg.key \ | sudo gpg --dearmor -o /etc/apt/keyrings/charm.gpgecho "deb [signed-by=/etc/apt/keyrings/charm.gpg] https://repo.charm.sh/apt/ * *" \ | sudo tee /etc/apt/sources.list.d/charm.listsudo apt update && sudo apt install -y vhs ffmpegSur Arch Linux, le paquet est directement dans les dépôts officiels :
sudo pacman -Syu vhsDans les deux cas, ttyd reste à installer séparément (voir plus haut).
Méthode C – Windows (winget / Scoop)
Section intitulée « Méthode C – Windows (winget / Scoop) »Les deux gestionnaires installent le binaire officiel et l'ajoutent au PATH.
Comme sur Linux, ffmpeg et ttyd doivent être présents pour que le rendu
aboutisse.
# Wingetwinget install charmbracelet.vhs
# Ou Scoopscoop install vhsMéthode D – Binaire précompilé (rapide)
Section intitulée « Méthode D – Binaire précompilé (rapide) »La méthode la plus universelle, sans gestionnaire de paquets. Chaque release
publie un fichier checksums.txt : téléchargez l'archive et ce fichier, puis
vérifiez l'empreinte avant d'installer quoi que ce soit. Sans cette étape, vous
placez dans votre PATH un binaire dont rien ne garantit l'origine.
VHS_VERSION=0.11.0BASE=https://github.com/charmbracelet/vhs/releases/download/v${VHS_VERSION}
curl -fsSLO "${BASE}/vhs_${VHS_VERSION}_Linux_x86_64.tar.gz"curl -fsSLO "${BASE}/checksums.txt"
grep " vhs_${VHS_VERSION}_Linux_x86_64.tar.gz\$" checksums.txt | sha256sum --check -
tar -xf "vhs_${VHS_VERSION}_Linux_x86_64.tar.gz"sudo install -m 0755 "vhs_${VHS_VERSION}_Linux_x86_64/vhs" /usr/local/bin/vhsvhs --versionAdaptez VHS_VERSION et le nom de l'archive à votre OS et à votre
architecture ; la liste complète est sur la page des releases du projet.
Méthode E – Depuis la source (Go)
Section intitulée « Méthode E – Depuis la source (Go) »La méthode la plus flexible si vous avez Go installé.
go install github.com/charmbracelet/vhs@latestÉtape 3 : Tests post‑installation
Section intitulée « Étape 3 : Tests post‑installation »# Afficher la version installéevhs --version# Générer une tape de test minimale et produire un GIFcat > quick.tape <<'EOF'Output quick.gifSet TypingSpeed 120msType "echo VHS OK"EnterSleep 1sEOFvhs quick.tape && ls -lh quick.gifSi quick.gif est créé sans erreur, l'environnement est opérationnel. Votre fichier .gif devrait ressembler à ceci :

Étape 4 : Dépannage rapide
Section intitulée « Étape 4 : Dépannage rapide »Les quatre pannes ci-dessous couvrent la quasi-totalité des premiers essais. Elles ont un point commun : VHS échoue tard, au moment de l'encodage, et le message d'erreur ne pointe pas toujours la vraie cause. La directive Require est la parade la plus efficace, elle fait échouer la tape avant le rendu si une dépendance manque.
- GIF vide ou non généré : vérifier
ffmpeg -version - Erreur de dépendance : ajouter la directive
Require ffmpegen tête de la tape pour échouer tôt - Thème non appliqué : s'assurer que la directive
Set Themeest placée avant toute commandeType - Commande lente imprévisible : remplacer pause fixe par
Waitaprès la commande
Écriture d'une tape VHS
Section intitulée « Écriture d'une tape VHS »Le cœur de VHS : un fichier texte avec extension .tape décrivant la session.
Chaque ligne est une instruction.
Structure générale :
Output ./demo.gifSet FontSize 16Set TypingSpeed 120msRequire ffmpegType "echo 'Bonjour VHS'"EnterSleep 500msType "uname -s"EnterPrincipales instructions
Section intitulée « Principales instructions »Output <chemin>: définit le fichier de sortie. Format selon extension (.gif,.mp4,.webm,.png-> séquence)Set <Paramètre> <Valeur>: personnalisation (police, thème, vitesse, curseur, dimensions)Type "texte": simule la frappe humaine d'une chaîneEnter: envoie la touche EntréeSleep <durée>: pause (ex:500ms,2s)Wait: suspend la frappe jusqu'à ce qu'un motif apparaisse à l'écran. Sans argument, le motif par défaut est le retour du prompt (/>$/) et le délai maximal est de 15 secondesRequire <binaire>: vérifie la présence d'une dépendance avant exécutionHide/Show: masquer temporairement une portion pour ne pas l'afficherSource <fichier.tape>: inclure un sous-script (modularisation)
Bonnes pratiques structure
Section intitulée « Bonnes pratiques structure »La première règle n'est pas une préférence de style, c'est une contrainte du langage : à l'exception de TypingSpeed, toute directive Set placée après une commande (Type, Enter, Sleep...) est silencieusement ignorée. C'est l'explication la plus fréquente d'un thème ou d'une taille de police qui « ne prend pas ». Même logique pour Require, qui doit précéder toute commande pour jouer son rôle de garde-fou.
- Grouper les
Seten début de fichier - Toujours définir explicitement
Output - Utiliser
Requirepour des dépendances critiques (ffmpeg,git) - Préférer
Sleepcourts +Waitplutôt que des pauses longues arbitraires - Versionner le
.tape, ignorer les rendus (.gitignore)
Enregistrement actif : vhs record
Section intitulée « Enregistrement actif : vhs record »Si vous préférez partir d'une capture réelle, VHS propose :
# Enregistrer une session interactive et générer un fichier .tapevhs record > session.tapeVous tapez vos commandes normalement, puis terminez avec Ctrl+D. Le fichier
généré peut être nettoyé pour :
- Retirer les fautes de frappe
- Ajuster les temporisations (
Sleep) - Ajouter des
Setcohérents (police, thème, vitesse de frappe)
Cette approche hybride (record puis édition) combine vitesse et précision.
Génération du GIF / vidéo
Section intitulée « Génération du GIF / vidéo »Une fois la tape prête :
# Génère le média selon l'extension Outputvhs demo.tapeFormats supportés :
.gif: diffusion web / README.mp4: taille réduite, réseaux sociaux.webm: optimisation web moderne.png(dossier) : spritesheet potentielle / post‑traitement
Changez simplement l'extension dans l'instruction Output pour obtenir un autre
format, sans modifier le reste du scénario.
Paramètres Set disponibles
Section intitulée « Paramètres Set disponibles »Résumé des principaux réglages supportés (à placer avant toute commande
non‑Set / non‑Output).
Set Shell <bash|zsh|fish|...>: shell utilisé pour exécuter les commandesSet Width <px>/Set Height <px>: dimensions du terminal renduSet FontSize <px>: taille des caractèresSet FontFamily "Nom": police monospacée (doit être installée côté système ou embarquée)Set LetterSpacing <px>: espacement horizontal entre lettresSet LineHeight <ratio>: interligne (ex:1.4)Set TypingSpeed <durée>: délai entre frappes (ex:120ms,0.1secondes)Set Theme { JSON }ouSet Theme "Nom": thème 16 couleurs + foreground/background/cursorSet Padding <px>: espace interne autour de la zone terminalSet Margin <px>: marge externe du cadre vidéoSet MarginFill "#RRGGBB": couleur de fond de la margeSet WindowBar <Colorful|ColorfulRight|Rings|RingsRight|None>: barre décorativeSet BorderRadius <px>: arrondi des coins (souvent avec marge + couleur)Set Framerate <fps>: fréquence de capture (ex:30,60)Set PlaybackSpeed <facteur>: accélération ou ralentissement de lecture finale (ex:1.2)Set LoopOffset <frames|%>: frame de début de boucle GIF (thumbnails plus dynamiques)Set CursorBlink <true|false>: clignotement du curseurSet WindowBarSize <px>: hauteur de la barre décorativeSet WaitTimeout <durée>/Set WaitPattern <regex>: réglages par défaut de la commandeWait
Astuce : regrouper tous les Set et Require en tête de fichier pour éviter
qu'ils ne soient ignorés.
Paramètres d'affichage utiles
Section intitulée « Paramètres d'affichage utiles »Ce bloc constitue un point de départ raisonnable pour de la documentation : 1000 x 600 pixels reste lisible une fois l'image réduite dans une page web, et une frappe à 120 ms laisse le temps de lire la commande. Copiez-le en tête de vos tapes, puis n'ajustez qu'une valeur à la fois, le rendu final se juge à l'oeil sur le GIF produit.
Set Width 1000Set Height 600Set FontSize 18Set Theme DraculaSet TypingSpeed 120msSet CursorBlink falsevhs themes liste les thèmes reconnus par votre version : passer un nom inconnu
à Set Theme fait échouer la tape.
Publication et partage
Section intitulée « Publication et partage »VHS propose un hébergement intégré :
# Génération + upload immédiat si --publish est présentvhs demo.tape --publishOu après génération :
# Publication d'un fichier existantvhs publish demo.gifLa commande retourne une URL partageable (https://vhs.charm.sh/...).
Avantages :
- Pas besoin de committer des fichiers lourds
- Rendu optimisé (hébergement Charm)
- Mise à jour automatisable (CI)
Alternative : héberger dans un dépôt GitHub (assets release) ou un CDN (Cloudflare R2 / S3) pour contrôle complet.
Automatisation & intégration CI (GitHub Actions)
Section intitulée « Automatisation & intégration CI (GitHub Actions) »Automatiser la régénération garantit que vos GIFs restent synchronisés avec le code source. Exemple de workflow minimal :
name: Generate VHS assetson: push: paths: - "demo/*.tape" workflow_dispatch:
permissions: {}
jobs: build: runs-on: ubuntu-24.04 permissions: contents: read env: VHS_VERSION: "0.11.0" steps: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false
- name: Install dependencies run: | sudo apt-get update sudo apt-get install -y ffmpeg
- name: Install ttyd run: | curl -fsSLO https://github.com/tsl0922/ttyd/releases/latest/download/ttyd.x86_64 curl -fsSLO https://github.com/tsl0922/ttyd/releases/latest/download/SHA256SUMS grep ' ttyd.x86_64$' SHA256SUMS | sha256sum --check - sudo install -m 0755 ttyd.x86_64 /usr/local/bin/ttyd
- name: Install VHS run: | base="https://github.com/charmbracelet/vhs/releases/download/v${VHS_VERSION}" archive="vhs_${VHS_VERSION}_Linux_x86_64.tar.gz" curl -fsSLO "${base}/${archive}" curl -fsSLO "${base}/checksums.txt" grep " ${archive}\$" checksums.txt | sha256sum --check - tar -xf "${archive}" sudo install -m 0755 "vhs_${VHS_VERSION}_Linux_x86_64/vhs" /usr/local/bin/vhs
- name: Generate GIFs run: | for f in demo/*.tape; do vhs "$f"; done
- name: Upload artifacts uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: vhs-gifs path: demo/*.gifTrois choix de ce workflow méritent une explication. Les actions sont
épinglées par empreinte SHA parce qu'une étiquette de version peut être
redéplacée vers un autre commit, ce qui suffirait à exécuter du code arbitraire
sur le runner. permissions: {} retire tous les droits par défaut et le job ne
récupère que contents: read, dont il a réellement besoin. Enfin, chaque
binaire téléchargé est vérifié par son empreinte SHA-256 publiée par le
projet, plutôt que d'être passé directement à tar : sans cette étape, une
release compromise se propagerait silencieusement dans vos images.
Bonnes pratiques CI :
- Ne pas stocker les GIFs générés dans Git (poids, diffs inutiles)
- Versionner uniquement les
.tape - Publier via
--publishou artifacts compressés - Ajouter un job périodique (
schedule) si dépendances externes évoluent
Astuces & bonnes pratiques
Section intitulée « Astuces & bonnes pratiques »- Définir une vitesse de frappe modérée :
Set TypingSpeed 90ms(lisible) - Utiliser
Waitaprès des commandes réseau ou build - Réduire le prompt pour la lisibilité : VHS n'a pas de directive dédiée, on
passe par un segment masqué (
Hide/Type "export PS1='$ '"/Enter/Show) - Masquer secrets / tokens : segment sous
Hide...Show - Externaliser des blocs récurrents (
Source ./fragments/init.tape) - Documenter les choix dans des commentaires en amont du fichier
.tape(lignes commençant par#) - Contrôler la police : préférer une police monospacée lisible (JetBrains Mono, Fira Code)
- Vérifier le poids final des GIFs (optimisation possible via
gifsicle)
Optimisation du poids des GIFs
Section intitulée « Optimisation du poids des GIFs »Réduire la taille des GIFs améliore les performances de chargement dans la documentation.
- Limiter les dimensions : éviter > 1200px de largeur si non nécessaire
- Réduire la durée : supprimer les pauses excessives (
Sleep> 2s) - Ajuster
Framerate: 30 fps suffisent souvent, 24 fps acceptable pour commandes lentes - Palette optimisée : certains thèmes très colorés augmentent la palette → préférer un thème sobre
- Utiliser
PlaybackSpeed:1.1ou1.2fluidifie sans rendre illisible - Post‑traitement :
gifsicle -O3 demo.gif -o demo-opt.gif - Format alternatif : proposer aussi
.webmou.mp4(nettement plus léger) et garder GIF comme fallback - Découper : plusieurs courtes démonstrations plutôt qu'un long scénario monolithique
Exemple pipeline local :
# Optimisation après génération GIFvhs demo.tapegifsicle -O3 demo.gif -o demo-opt.gifOrganisation d'un répertoire demo/
Section intitulée « Organisation d'un répertoire demo/ »Le préfixe numérique n'est pas décoratif : il fixe l'ordre de lecture pour un humain et permet à une boucle for f in demo/*.tape de traiter les scénarios dans un ordre stable. Le sous-répertoire fragments/ regroupe ce qui n'est jamais rendu seul, uniquement inclus par Source.
demo/ 00-intro.tape 10-installation.tape 20-flux-base.tape fragments/ palette.tape header.tapeExemple de modularisation
Section intitulée « Exemple de modularisation »Source insère le contenu d'une autre tape à l'endroit de l'appel. L'intérêt est de factoriser les réglages : la charte visuelle vit dans un seul fichier, et changer la police de toutes les démonstrations devient une modification à un seul endroit. Attention à l'ordre, le fichier inclus contient des Set, il doit donc rester avant la première commande Type.
# Fichier principalOutput ./workflow.gifSource ./fragments/header.tapeSource ./fragments/palette.tapeType "make build"EnterWaitType "./bin/outillage --help"EnterSécurité & qualité documentaire
Section intitulée « Sécurité & qualité documentaire »- Éviter de montrer des chemins utilisateurs privés (
/home/user) → utiliser comptes génériques - Purger l'historique de commandes sensibles
- Indiquer dans la doc la commande pour régénérer :
vhs chemin.tape - Ajouter un badge ou une phrase : "Ce GIF est généré automatiquement via CI"
À retenir
Section intitulée « À retenir »- VHS génère des GIFs de terminal à partir d'un fichier déclaratif
.tape, reproductible et versionnable. - Les prérequis sont
ffmpeg(encodage) etttyd(terminal rendu). - Une tape enchaîne des instructions :
Output,Set,Type,Enter,Sleep,Require. vhs validatevérifie la syntaxe avant de lancer un rendu potentiellement long.- En CI, on versionne les
.tapeet on régénère les GIFs à chaque changement.
Prochaines étapes
Section intitulée « Prochaines étapes »Plus d'infos
Section intitulée « Plus d'infos »- Documentation et dépôt officiel : VHS
- Publication hébergée (option
--publish) : article Charm - Intégration CI : Action GitHub officielle vhs-action
FAQ : questions fréquentes
Section intitulée « FAQ : questions fréquentes »Ces quatre questions sont celles qui reviennent avant l'adoption : ce qu'est réellement VHS, la commande minimale pour produire un GIF, la liste exacte des prérequis, et la faisabilité en CI/CD. Les réponses reprennent l'essentiel du guide sous une forme condensée.
.tape. On décrit le scénario (commandes tapées, pauses, style) et VHS le rejoue pour produire un rendu propre, reproductible et versionnable, sans filmer son écran..tape avec des instructions comme Output demo.gif, Type, Enter et Sleep, puis on lance vhs demo.tape. Le format de sortie (GIF, MP4, WebM) est déterminé par l'extension indiquée dans la directive Output. La commande vhs validate vérifie la syntaxe avant le rendu.ffmpeg pour encoder les images en GIF ou vidéo, et de ttyd pour fournir le terminal rendu. Un navigateur headless est utilisé en interne pour la capture des images. Les paquets de la plupart des distributions installent souvent ttyd automatiquement.ffmpeg et le binaire VHS (ou l'action officielle) dans le job, puis on boucle sur les fichiers .tape avec vhs. La bonne pratique est de versionner uniquement les .tape et de régénérer les GIFs en CI, pour qu'ils restent synchronisés avec le code.