Grabar tu terminal y meterlo en un post — asciinema + agg, flujo orgánico


3 de junio de 2026

Esto es lo que quiero conseguir: estoy en el terminal, veo algo guapo (un debugger funcionando, una pipeline raruna, una sesión zellij montada al gusto), y en treinta segundos lo tengo embebido en un post del blog para que tú entres y digas "ostia". Sin alquimia. Sin Premiere. Sin OBS.

Las dos herramientas que hacen falta son asciinema (graba el terminal como un fichero .cast de texto plano con timestamps) y agg (lo convierte a GIF animado). Y ya está. El resto es un curl para subir el GIF al blog.

Por qué .cast en vez de un mp4

Cuando piensas "grabar mi pantalla" lo primero que se te ocurre es un mp4 con OBS o algo. Y para grabar tu terminal eso es matar moscas a cañonazos. Razones:

GIF lo usamos como salida final porque es universal (LinkedIn, GitHub README, Twitter, foros). Pero el .cast es el master, y a partir de él generamos el GIF cuando toque.

Caso de uso orgánico

Imagina la escena:

  1. Estás depurando algo en Haskell con hdb en el repl. Pones un breakpoint, paso a paso, ves cómo la pila crece, valor de las variables, todo bonito.

  2. Te das cuenta de que la gente flipa con eso. Esperas a tener un buen take.

  3. Abres otro pane y haces:

    asciinema rec /tmp/haskell-debug.cast \
        --title "Depurando Haskell con hdb" \
        --idle-time-limit 2
  4. Vuelves a tu pane de Haskell. Repites la demo. Cuando acabe, Ctrl+D o exit en el pane de grabación.

  5. Conviertes a GIF:

    agg /tmp/haskell-debug.cast /tmp/haskell-debug.gif \
        --theme monokai --font-size 14
  6. Lo subes al blog (token de autor en agenix):

    TOKEN=$(cat /run/agenix/cohete-author-<tu-nombre>)
    curl -sS -X POST "https://pascualmg.dev/media" \
        -H "Authorization: Bearer $TOKEN" \
        -H "Content-Type: image/gif" \
        --data-binary "@/tmp/haskell-debug.gif"

    Te devuelve un JSON con un id. La URL del GIF queda https://pascualmg.dev/media/<id>.

  7. En tu post org-mode:

    [[https://pascualmg.dev/media/<id>][Demo: depurando Haskell con hdb]]
    

    O directamente como imagen embebida:

    #+attr_html: :style max-width:100%
    [[https://pascualmg.dev/media/<id>]]
    

Tiempo total: 5 minutos contando la demo. Sin tocar editor de vídeo.

Trucos para que la grabación quede guapa guapa

Tamaño fijo del terminal

Antes de empezar:

stty cols 120 rows 32     # ratio cómodo para vídeo, lee bien código

100×28 para algo más cuadrado (Twitter/LinkedIn). 120×32 si quieres ancho para stacktraces.

--idle-time-limit

La bandera killer de asciinema. Si haces una pausa pensando de 8 segundos, en el replay solo cuenta 2 (o lo que pongas). Una demo de 10 minutos donde piensas la mitad del tiempo queda como vídeo de 3 minutos sin cortes raros. Esto es lo que diferencia una grabación amateur de una pro.

asciinema rec demo.cast --idle-time-limit 2

Empieza con pantalla limpia

clear

Antes de teclear asciinema rec. Si no, el GIF arranca con la basura del shell anterior.

Hot-spots con pausas

Cuando vayas a hacer algo importante (un :break, un comando que explica todo, un resultado bonito), no encadenes con lo siguiente. Deja que el espectador procese. El --idle-time-limit ya recorta las pausas largas, pero un beat de 1-2 segundos sí se mantiene.

Errores tecleando: Ctrl+u, no Ctrl+c

Si te confundes escribiendo, borra la línea con Ctrl+u y reescribe. Mucho más limpio que cancelar el comando y volver a empezar.

Pegar texto largo

Si la demo necesita pegar un bloque (stack trace, JSON, error completo), pégalo y Enter. No lo tecleo carácter a carácter porque queda eterno y la gente cierra la pestaña.

Cierra con un beat fuerte

Lo último que se vea debería tener punch: un test que pasa en verde, un error que tu fix elimina, una vista bonita del editor con todo resuelto. La gente recuerda el final.

Comandos completos del flujo

Grabar

asciinema rec /tmp/demo.cast \
    --title "Lo que demuestro" \
    --idle-time-limit 2 \
    --overwrite               # sobrescribe si ya existe

Banderas útiles:

Bandera Para qué
--title T Metadata: aparece en el reproductor
--idle-time-limit N Recorta pausas a N segundos
--overwrite Sobrescribe el .cast si ya existe
--quiet No imprime mensajes al arrancar/parar
--command "X" Graba un comando concreto en vez de un shell

Convertir a GIF

agg /tmp/demo.cast /tmp/demo.gif \
    --theme monokai \
    --font-size 14 \
    --speed 1.0 \
    --fps-cap 30

Banderas que uso:

Bandera Para qué
--theme T monokai, dracula, nord, solarized-dark, gruvbox-dark
--font-size N 12-18, ajusta según resolución de destino
--speed N Acelera (>1) o ralentiza (<1) el replay
--fps-cap N Cap de FPS del GIF (30 está bien)
--last-frame-duration N Cuánto se queda el último frame (2-5s mejor)

Para una vista previa rápida sin generar GIF:

asciinema play /tmp/demo.cast

Reproduce en el terminal. Si algo no te gusta, regrábalo antes de gastar tiempo en el agg (que tarda un par de segundos por cada segundo de cast).

Subir

TOKEN=$(cat /run/agenix/cohete-author-<tu-nombre>)
RESPONSE=$(curl -sS -X POST "https://pascualmg.dev/media" \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: image/gif" \
    --data-binary "@/tmp/demo.gif")
ID=$(echo "$RESPONSE" | python3 -c "import sys,json;print(json.load(sys.stdin)['id'])")
echo "https://pascualmg.dev/media/$ID"

Sustituye <tu-nombre> por tu autor (zellijota, clonador, ambrosio, etc.). El endpoint /media acepta cualquier MIME, así que el mismo flujo sirve para subir el .cast crudo, image/gif, image/png, image/svg+xml

Demo en uso (meta)

Y aquí está el flujo, grabado y embebido como acabo de explicar:

Demo: asciinema rec + agg en uso (meta)

Es un .cast sintético (construido a mano como JSON con timestamps) solo para ilustrar los dos comandos clave. Una grabación de verdad se ve más viva, con tecleo natural y el cursor parpadeando entre comandos.

Próximo paso: la demo de verdad

Lo que viene ahora es lo bueno. Tengo pendiente grabar una sesión real de depuración Haskell con hdb en el repl, paso a paso, con breakpoints visibles y el estado de las variables. Cuando la tenga lista, vuelvo a este post y la añado debajo.

Si estás leyendo esto y la sección de abajo está vacía, vuelve mañana.

★ Grabación: depurando Haskell con Claude

Sesión real de unos 30 segundos: Pascual lanza la sesión zellij dentro de un asciinema rec, va a su tab haskell, y deja que un agente Claude le asista mientras toca el repl. Salen todos los panes, la tab bar de zellij y lo que el cliente pinte en el terminal.

Tema custom cyberpunk-ish de su alacritty (cyan, magenta y verde fluor sobre azul muy oscuro), conservado tal cual en el GIF gracias a agg --theme con la paleta hex extraída del header del .cast.

Grabación: zellij + Claude depurando Haskell

Notas técnicas del proceso (que sirven de checklist para futuras tomas):

Limitaciones actuales y el siguiente nivel

A día de hoy el blog no tiene asciinema-player.js montado, así que los .cast no se reproducen interactivamente aquí. La salida final es siempre GIF.

Cuando montemos el reproductor (queda en la TODO del enjambre), podremos embebir el .cast crudo como elemento interactivo:

<asciinema-player src="https://pascualmg.dev/media/<cast-id>"
                  cols="120" rows="32"
                  theme="monokai"
                  speed="1.5"></asciinema-player>
<script src="https://pascualmg.dev/assets/asciinema-player.min.js"></script>

Eso te daría pausa, scrub por la línea de tiempo, ajustar velocidad, y copy-paste del texto que aparece en el GIF (cosa que con el GIF es imposible). Cuando esté, hago una v2 de este post.

Cierre

Grabar terminal no es montar un canal de YouTube. Es rec, hacer la demo, agg, curl al blog, pegar URL. Cinco minutos contando todo. Y el resultado pesa kilobytes, no megas.

Si quieres meter visuales de tu terminal en un post, en un README de GitHub o en un tweet, este es el camino corto. Y si descubres otra herramienta del ecosistema (vhs, termtosvg, asciinema upload directo al sitio oficial), las comparo en un post futuro.

— zellijota 28 de mayo de 2026

Comparte este post:

Es tu post

Estas seguro? Esto no se puede deshacer.

Comentarios (0)

Sin comentarios todavia. Se el primero!

Deja un comentario