#!/usr/bin/env bash # Turns any fly-through clip into the WebP frame sequences the scroll scene scrubs through. # desktop: 16:9 1280x720 (landscape screens) # tablet: centre-cropped 3:4 810x1080 (portrait tablets) # mobile: centre-cropped 9:16 608x1080 (portrait phones) # Portrait crops use the full height of a 1080p source, so they're as sharp as the footage allows. # # Usage: scripts/make-frames.sh [frame-count=120] # Then commit public/frames/**. The site reads public/frames/manifest.json. set -euo pipefail IN=${1:?video file} COUNT=${2:-120} OUT="$(cd "$(dirname "$0")/.." && pwd)/public/frames" DUR=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$IN") FPS=$(echo "$COUNT / $DUR" | bc -l) SETS="desktop tablet mobile" for s in $SETS; do mkdir -p "$OUT/$s" find "$OUT/$s" -name '*.webp' -delete done filter() { case $1 in desktop) echo "scale=1280:720:force_original_aspect_ratio=increase,crop=1280:720" ;; tablet) echo "scale=-2:1080,crop=810:1080" ;; mobile) echo "scale=-2:1080,crop=608:1080" ;; esac } quality() { case $1 in desktop) echo 60 ;; *) echo 66 ;; esac } TMP=$(mktemp -d) for s in $SETS; do mkdir -p "$TMP/$s" ffmpeg -loglevel error -y -i "$IN" -vf "fps=$FPS,$(filter $s)" -frames:v "$COUNT" "$TMP/$s/%04d.png" # cwebp (brew install webp), encoded in parallel ls "$TMP/$s"/*.png | xargs -P 8 -I{} sh -c 'f={}; cwebp -quiet -q '"$(quality $s)"' -m 6 "$f" -o "'"$OUT/$s"'/$(basename "$f" .png).webp"' done rm -r "$TMP" N=$(ls "$OUT/desktop" | wc -l | tr -d ' ') printf '{ "count": %s, "desktop": "/frames/desktop/", "tablet": "/frames/tablet/", "mobile": "/frames/mobile/" }\n' "$N" > "$OUT/manifest.json" echo "wrote $N frames -> $OUT" for s in $SETS; do echo " $s: $(du -sh "$OUT/$s" | cut -f1)"; done