pavelp

How to record a screencast?

I like how u/xenodium does screencasts and want to have something automated – press a button (okay, M-x make-screencast magic in Emacs) and get a gif file with screencast as result.

Let's consider some options.

  • LICEcap.app: works fine, but manual setup of the frame needed, I'll keep using it for everything except Emacs
  • Maybe asciinema is an option?: no, as it's an external service and only works for terminals, but Emacs is a GUI app
  • Where to host gifs?: Save in assets folder with tag "screencast".

On <2023-12-07 Thu> I read a post Recording Screencasts with Emacs | Meta Redux and set up gif-screencast package.

Let's check if we have all utils needed:

which screencapture
which mogrify
which convert
which gifsicle

The package makes a screenshot per movement (specifically, per Emacs command execution – probably adding a hook), on macOS using built-in screencapture tool.

mogrify and convert come from imagemagick package that I had installed previously (I believe PDFTools package of Emacs installed it as a dependency, but not sure). mogrify used to crop (resize) all the screenshots to smaller size, convert to turn them into a gif file, then gifsicle optimizes the gif size. gifsicle needs separate installation, for me it was sudo port install gifsicle.

Some problems came with the default setup.

First, mogrify incorrectly sets croping sizes on HD screens, resulting only a left quarter of screenshot left after cropping. Details here - HiDPI monitors with scaling make cropping region incorrect (#14, unresolved as of <2023-12-07 Thu>, so I disabled cropping by setting (setq gif-screencast-cropping-program "foo") which fails executable-p predicate so no cropping.

Second, I wanted to automatically record screencast of Emacs window only, nothing else. I don't want the entire screen nor manually position the window somewhere. Unfortunately, screencapture utility doesn't give an easy way to make a screenshot of currently active (Emacs) window, but requires explicit windowid. I found a recipe how to find window id on macos - How do I find the windowid to pass to screencapture -l? - Ask Different post, by using osascript. The caveat is that window id changes for the same app, I don't know exactly when, but likely on every resizing/movement. So, the routine that obtains window id should be run prior to making the screencast. It's also quite slow, like couple of seconds.

So, I ended up with defining my own function that I start with M-x my-screencast.

(use-package gif-screencast
  :ensure t
  :commands (gif-screencast)
  :config
  (define-key gif-screencast-mode-map (kbd "<f5>") #'gif-screencast-stop)
  (defun my-screencast ()
    "Runs `gif-screencast' on the current EmacsMac window."
    (interactive)
    ;;(setq gif-screencast-args `("-x"))
    (let* ((gif-screencast-program "screencapture")
           (gif-screencast-want-optimized t)
           (window-id (thread-first
                        "osascript -e 'tell app \"EmacsMac\" to id of window 1'"
                        shell-command-to-string
                        string-trim))
           (gif-screencast-args `("-x" ,(format "-l%s" window-id)))
           (gif-screencast-cropping-program "foo") ;; to prevent mogrify of incorrect cropping
           (gif-screencast-capture-format "ppm"))
      (call-interactively #'gif-screencast))))

Output saved to ~/Videos/emacs directory.

Finally, let's record a screencast!

screencast

Thoughts? Leave a comment