Here is a very quick description of how to make a time lapse video on a Mac using the command line and two tools: ImageSnap (something I wrote years ago) and FFmpeg, a fabulous open source tools that has been around for ages.

This article assumes familiarity with the command line. If that is 100% new to you, you might want to look around for some tutorials, but if you have some basic knowledge, you will do fine here.

Overall Approach

Here is the big picture. In one Terminal window we are going to get the imagesnap tool running taking a snapshot through a webcam every minute. In another Terminal window we are going to have a loop running in our bash (or zsh) window that takes the images captured so far and create a video with ffmpeg from them every hour. In that way as your experiment (or whatever you are videoing) progresses, you will always have a reasonably up-to-date video of the time lapse project.

Installing ImageSnap and FFmpeg

I am not going to go over installing these tools. Refer to their websites for instructions.

Capturing Images Every Minute

Open Terminal and navigate to a good working directory. Then use imagesnap to enumerate the cameras you have attached to your computer:

$ cd ~/Downloads
$ mkdir timelapse
$ cd timelapse
$ imagesnap -l
Video Devices:
=> Logitech StreamCam
=> Logi Webcam C920e
=> EpocCam
=> FaceTime HD Camera (Built-in)
=> OBS Virtual Camera

In our experiment we are going to use our Logitech C920 camera pointed at some crystals growing on a National Geographic kids’ experiment. After arranging the camera to look as we like, we will kick off imagesnap and tell it to keep taking a picture every 60 seconds. The -d flag requires we provide just a portion of the camera name. I like to use QuickTime Player’s “New Movie Recording” to preview my camera.

$ imagesnap -d 920 -t 60
Capturing image from device "Logi Webcam C920e"...
snapshot-00000.jpg
snapshot-00001.jpg

On some computers and for some reason I cannot explain, I sometimes get a weird error that does not seem to affect anything. If you get something that looks like this when you run imagesnap, I think you can safely ignore it.

imagesnap[21395:3085214] unable to obtain configuration from file:///Library/Preferences/com.apple.ViewBridge.plist due to Error Domain=NSCocoaErrorDomain Code=260 “The file “com.apple.ViewBridge.plist” couldn’t be opened because there is no such file.” UserInfo={NSFilePath=/Library/Preferences/com.apple.ViewBridge.plist, NSUnderlyingError=0x7fd3016105b0 {Error Domain=NSPOSIXErrorDomain Code=2 “No such file or directory”}}

The imagesnap tool will keep snapping pictures as long as it is running. If you have to restart imagesnap, it will pick up with the numbering sequence where it left off.

Make Time Lapse Video

From another Terminal window, navigate to the same folder with the images, and test some ffmpeg parameters:

$ cd ~/Downloads
$ mkdir timelapse
$ cd timelapse
$ ffmpeg -y -framerate 60 -pattern_type glob \
-i "snap*.jpg" -s:v 1920x1080 -c:v libx264 -crf 30 \
-pix_fmt yuv420p timelapse.mp4

If this works you should have a very short video file with a few frames in it.

Now we will set up a script that will run indefinitely.

$ while true; do \
ffmpeg -y -framerate 60 -pattern_type glob \
-i "snap*.jpg" -s:v 1920x1080 -c:v libx264 -crf 30 \
-pix_fmt yuv420p timelapse.mp4 ; \
echo "Video made at $(date) - another in an hour"; \
sleep 3600; \
done

I tend to type something like this on one line, but to help it show up in this blog, I am using the backslash characters to continue the lines.

Done! The imagesnap tool will indefinitely keep taking pictures. The loop will run ffmpeg regularly, and you have a great experiment to watch!

Bonus Content

In other experiments I add a timestamp to the corner of each image. I created another directory within the one we were just working in, and I created a more advanced loop that ran a script that included the following snippet that used the ImageMagick convert tool:

for fin in ../snap*.jpg; do
  fout=$(basename $fin)
  echo -n "$fout "
  if [[ -f "$fout" ]]; then
      echo "... skip"
  else
    echo -n "... converting ..."
    CT=$(GetFileInfo -d "$fin")
    CTshort=${CT:0:16}
  
    convert -pointsize 20 \
      -fill white -annotate +19+39 "${CTshort}" \
      -fill white -annotate +21+39 "${CTshort}" \
      -fill white -annotate +21+41 "${CTshort}" \
      -fill white -annotate +19+41 "${CTshort}" \
      -fill red -annotate +20+40 "${CTshort}" \
      "$fin" "$fout"
    SetFile -m "$CT" "$fout"
    echo "done"
  fi
done