URI:
       feat(cli): add webcam-gb capture script with palette options - webgbcam - [fork] gameboy webcam
  HTML git clone git://src.adamsgaard.dk/webgbcam
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 629639e1bd6fd47632d7fceee831587def57d552
   DIR parent e49591b1954c1eae6ff4fe280185a475bb8180e2
  HTML Author: Anders Damsgaard <anders@adamsgaard.dk>
       Date:   Sat, 14 Feb 2026 16:49:40 +0100
       
       feat(cli): add webcam-gb capture script with palette options
       
       Diffstat:
         A webcam-gb                           |     249 +++++++++++++++++++++++++++++++
       
       1 file changed, 249 insertions(+), 0 deletions(-)
       ---
   DIR diff --git a/webcam-gb b/webcam-gb
       @@ -0,0 +1,249 @@
       +#!/bin/sh
       +
       +set -eu
       +
       +usage() {
       +        cat <<'EOF'
       +Usage: ./webcam-gb [options]
       +
       +Capture webcam video with a webgbcam-like filter.
       +
       +Options:
       +  -o, --output FILE        Output file (default: webgbcam-YYYYmmdd-HHMMSS.mp4)
       +  -d, --device DEVICE      Video device
       +                           macOS: avfoundation input (default: 0:none)
       +                           Linux: v4l2 path (default: /dev/video0)
       +  -t, --duration SECONDS   Record duration (default: until interrupted)
       +  -p, --palette NAME       Color palette (default: crtgb)
       +      --fps FPS            Capture framerate (default: 30)
       +      --input-size WxH     Camera capture size (default: 640x480)
       +      --scale N            Integer upscale factor from 128x112 (default: 6)
       +      --list-palettes      List available palettes and exit
       +      --list-devices       List available camera devices and exit
       +  -h, --help               Show this help and exit
       +EOF
       +}
       +
       +need_arg() {
       +        if [ $# -lt 2 ]; then
       +                echo "missing value for $1" >&2
       +                exit 1
       +        fi
       +}
       +
       +require_ffmpeg() {
       +        if ! command -v ffmpeg >/dev/null 2>&1; then
       +                echo "ffmpeg not found in PATH" >&2
       +                exit 1
       +        fi
       +}
       +
       +list_palettes() {
       +        cat <<'EOF'
       +Available palettes:
       +  crtgb
       +  amber-crtgb
       +  ayy4
       +  spacehaze
       +  kirby-sgb
       +  cherrymelon
       +  pumpkin-gb
       +  purpledawn
       +  royal4
       +  grayscale
       +  virtual
       +  love-love
       +EOF
       +}
       +
       +set_palette() {
       +        case "$1" in
       +                crtgb)
       +                        C0=0x060601 C1=0x0B3E08 C2=0x489A0D C3=0xDAF222
       +                        ;;
       +                amber-crtgb)
       +                        C0=0x0D0405 C1=0x5E1210 C2=0xD35600 C3=0xFED018
       +                        ;;
       +                ayy4)
       +                        C0=0x00303B C1=0xFF7777 C2=0xFFCE96 C3=0xF1F2DA
       +                        ;;
       +                spacehaze)
       +                        C0=0x0B0630 C1=0x6B1FB1 C2=0xCC3495 C3=0xF8E3C4
       +                        ;;
       +                kirby-sgb)
       +                        C0=0x2C2C96 C1=0x7733E7 C2=0xE78686 C3=0xF7BEF7
       +                        ;;
       +                cherrymelon)
       +                        C0=0x012824 C1=0x265935 C2=0xFF4D6D C3=0xFCDEEA
       +                        ;;
       +                pumpkin-gb)
       +                        C0=0x142B23 C1=0x19692C C2=0xF46E16 C3=0xF7DB7E
       +                        ;;
       +                purpledawn)
       +                        C0=0x001B2E C1=0x2D757E C2=0x9A7BBC C3=0xEEFDED
       +                        ;;
       +                royal4)
       +                        C0=0x521296 C1=0x8A1FAC C2=0xD4864A C3=0xEBDB5E
       +                        ;;
       +                grayscale)
       +                        C0=0x282828 C1=0x686868 C2=0xA8A8A8 C3=0xFCFCFC
       +                        ;;
       +                virtual)
       +                        C0=0x020000 C1=0x410000 C2=0x7F0000 C3=0xFF0000
       +                        ;;
       +                love-love)
       +                        C0=0xB01030 C1=0xFF60B0 C2=0xFFB8E8 C3=0xFFFFFF
       +                        ;;
       +                *)
       +                        echo "unknown palette: $1" >&2
       +                        list_palettes >&2
       +                        exit 1
       +                        ;;
       +        esac
       +}
       +
       +FPS=30
       +INPUT_SIZE=640x480
       +SCALE=6
       +DURATION=
       +LIST_DEVICES=0
       +LIST_PALETTES=0
       +PALETTE=crtgb
       +OUTPUT_FILE="webgbcam-$(date +%Y%m%d-%H%M%S).mp4"
       +DEVICE=
       +
       +while [ $# -gt 0 ]; do
       +        case "$1" in
       +                -o|--output)
       +                        need_arg "$@"
       +                        OUTPUT_FILE=$2
       +                        shift 2
       +                        ;;
       +                -d|--device)
       +                        need_arg "$@"
       +                        DEVICE=$2
       +                        shift 2
       +                        ;;
       +                -t|--duration)
       +                        need_arg "$@"
       +                        DURATION=$2
       +                        shift 2
       +                        ;;
       +                -p|--palette)
       +                        need_arg "$@"
       +                        PALETTE=$2
       +                        shift 2
       +                        ;;
       +                --fps)
       +                        need_arg "$@"
       +                        FPS=$2
       +                        shift 2
       +                        ;;
       +                --input-size)
       +                        need_arg "$@"
       +                        INPUT_SIZE=$2
       +                        shift 2
       +                        ;;
       +                --scale)
       +                        need_arg "$@"
       +                        SCALE=$2
       +                        shift 2
       +                        ;;
       +                --list-palettes)
       +                        LIST_PALETTES=1
       +                        shift
       +                        ;;
       +                --list-devices)
       +                        LIST_DEVICES=1
       +                        shift
       +                        ;;
       +                -h|--help)
       +                        usage
       +                        exit 0
       +                        ;;
       +                *)
       +                        echo "unknown option: $1" >&2
       +                        usage >&2
       +                        exit 1
       +                        ;;
       +        esac
       +done
       +
       +if [ "$LIST_PALETTES" -eq 1 ]; then
       +        list_palettes
       +        exit 0
       +fi
       +
       +set_palette "$PALETTE"
       +
       +OS_NAME=$(uname -s)
       +
       +case "$OS_NAME" in
       +        Darwin)
       +                INPUT_FORMAT=avfoundation
       +                if [ -z "$DEVICE" ]; then
       +                        DEVICE="0:none"
       +                fi
       +                ;;
       +        Linux)
       +                INPUT_FORMAT=v4l2
       +                if [ -z "$DEVICE" ]; then
       +                        DEVICE="/dev/video0"
       +                fi
       +                ;;
       +        *)
       +                echo "unsupported OS: $OS_NAME (expected Darwin or Linux)" >&2
       +                exit 1
       +                ;;
       +esac
       +
       +if [ "$LIST_DEVICES" -eq 1 ]; then
       +        case "$INPUT_FORMAT" in
       +                avfoundation)
       +                        require_ffmpeg
       +                        ffmpeg -hide_banner -f avfoundation -list_devices true -i "" 2>&1 || true
       +                        ;;
       +                v4l2)
       +                        if command -v v4l2-ctl >/dev/null 2>&1; then
       +                                v4l2-ctl --list-devices
       +                        else
       +                                ls /dev/video* 2>/dev/null || echo "no /dev/video* devices found"
       +                        fi
       +                        ;;
       +        esac
       +        exit 0
       +fi
       +
       +FILTER_GRAPH='
       +[0:v]fps='"$FPS"',scale=128:112:flags=lanczos,format=gray,eq=contrast=1.5:gamma=1.0,unsharp=5:5:0.75:3:3:0.0[cam];
       +color=c='"$C0"':s=4x1:d=1[pal0];
       +[pal0]drawbox=x=1:y=0:w=1:h=1:color='"$C1"':t=fill,drawbox=x=2:y=0:w=1:h=1:color='"$C2"':t=fill,drawbox=x=3:y=0:w=1:h=1:color='"$C3"':t=fill,scale=256:1:flags=neighbor,format=rgb24[pal];
       +[cam][pal]paletteuse=dither=bayer:bayer_scale=2,scale=iw*'"$SCALE"':ih*'"$SCALE"':flags=neighbor,setsar=1
       +'
       +
       +set -- -hide_banner -y
       +
       +case "$INPUT_FORMAT" in
       +        avfoundation)
       +                set -- "$@" -f avfoundation -framerate "$FPS" -video_size "$INPUT_SIZE" -i "$DEVICE"
       +                ;;
       +        v4l2)
       +                set -- "$@" -f v4l2 -framerate "$FPS" -video_size "$INPUT_SIZE" -i "$DEVICE"
       +                ;;
       +esac
       +
       +if [ -n "$DURATION" ]; then
       +        set -- "$@" -t "$DURATION"
       +fi
       +
       +set -- "$@" \
       +        -an \
       +        -filter_complex "$FILTER_GRAPH" \
       +        -c:v libx264 \
       +        -pix_fmt yuv420p \
       +        -movflags +faststart \
       +        "$OUTPUT_FILE"
       +
       +require_ffmpeg
       +echo "recording from $INPUT_FORMAT device '$DEVICE' with palette '$PALETTE' -> $OUTPUT_FILE"
       +ffmpeg "$@"