Skip to main content
Webreel supports three output formats: MP4 (default), GIF, and WebM. Each has different use cases, quality characteristics, and file size tradeoffs.

Format Overview

FormatExtensionBest ForBrowser Support
MP4.mp4High-quality demos, documentation, social mediaUniversal
GIF.gifGitHub READMEs, chat messages, emailUniversal
WebM.webmWeb embedding, smaller file sizesModern browsers (not Safari on older iOS/macOS)

MP4 Format (Default)

Configuration

MP4 is the default output format. Simply specify a video name:
{
  "videos": {
    "my-demo": {
      "url": "https://example.com",
      "steps": []
    }
  }
}
This produces videos/my-demo.mp4.

Explicit MP4 Output

You can also specify the output path explicitly:
{
  "videos": {
    "my-demo": {
      "url": "https://example.com",
      "output": "demos/feature-walkthrough.mp4",
      "steps": []
    }
  }
}

Encoding Details

MP4 files use H.264 (libx264) encoding with these settings:
  • Codec: libx264
  • Preset: ultrafast (during recording)
  • Pixel format: yuv420p (maximum compatibility)
  • Color space: bt709 (standard HD)
  • Default CRF: 18 (high quality)
  • Frame rate: 60fps (configurable)
  • Audio codec: AAC at 128kbps (when sound effects enabled)
From the source (packages/@webreel/core/src/recorder.ts:97):
this.ffmpegProcess = spawn(
  this.ffmpegPath,
  [
    "-y",
    "-f", "image2pipe",
    "-framerate", String(this.fps),
    "-c:v", "mjpeg",
    "-i", "pipe:0",
    "-c:v", "libx264",
    "-preset", "ultrafast",
    "-crf", String(this.crf),
    "-pix_fmt", "yuv420p",
    "-movflags", "+faststart",
    this.tempVideo,
  ],
  { stdio: ["pipe", "pipe", "pipe"] },
);

Quality Settings

Control output quality with the quality field (0-100):
{
  "videos": {
    "high-quality-demo": {
      "url": "https://example.com",
      "quality": 95,
      "steps": []
    }
  }
}
Quality is converted to ffmpeg CRF (Constant Rate Factor):
  • quality: 100 → CRF 0 (lossless, huge files)
  • quality: 95 → CRF 2 (near-lossless)
  • quality: 80 → CRF 10 (very high quality)
  • quality: 50 → CRF 25 (balanced)
  • quality: 0 → CRF 51 (lowest quality)
Default CRF is 18 (excellent quality, reasonable file size).

Frame Rate

Customize the frame rate:
{
  "videos": {
    "smooth-demo": {
      "url": "https://example.com",
      "fps": 60,
      "steps": []
    }
  }
}
Higher frame rates produce smoother cursor motion but larger files. Default is 60fps.

Sound Effects

Add click and keystroke sounds:
{
  "sfx": {
    "click": 1,
    "key": 1
  },
  "videos": {
    "my-demo": {
      "url": "https://example.com",
      "steps": []
    }
  }
}
Built-in sound packs:
  • 1: Soft, subtle clicks and key presses
  • 2: Mechanical keyboard sounds
  • 3: Clicky, pronounced sounds
  • 4: Deep, bass-heavy sounds
You can also provide custom audio file paths:
{
  "sfx": {
    "click": "./sounds/custom-click.mp3",
    "key": "./sounds/custom-key.mp3"
  }
}

File Size Considerations

SettingSmall FileBalancedHigh Quality
Quality507595
FPS306060
Viewport1080x10801920x10802560x1440
Zoom11.52

GIF Format

Configuration

Set the output field to a .gif extension:
{
  "videos": {
    "readme-demo": {
      "url": "https://example.com",
      "output": "readme-demo.gif",
      "steps": []
    }
  }
}
From the gif-output example:
{
  "$schema": "https://webreel.dev/schema/v1.json",
  "videos": {
    "gif-output": {
      "url": "./web/index.html",
      "viewport": { "width": 1920, "height": 1080 },
      "zoom": 2,
      "output": "gif-output.gif",
      "waitFor": ".primary",
      "steps": [
        { "action": "pause", "ms": 500 },
        { "action": "click", "selector": "a.primary", "delay": 1000 }
      ]
    }
  }
}

Encoding Details

GIF encoding uses ffmpeg’s palettegen/paletteuse filters for optimal quality:
  • Frame rate: Fixed at 15fps (GIF standard)
  • Palette: Generated from video content (256 colors)
  • Dithering: Enabled for smoother gradients
  • Scaling: Uses Lanczos algorithm for quality
From the source (packages/@webreel/core/src/media.ts:226):
export function finalizeGif(
  ffmpegPath: string,
  tempVideo: string,
  outputPath: string,
  width: number,
): void {
  runFfmpeg(ffmpegPath, [
    "-y",
    "-i",
    tempVideo,
    "-vf",
    `fps=15,scale=${width}:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse`,
    outputPath,
  ]);
}

Use Cases

Best for:
  • GitHub README demonstrations
  • Slack/Discord messages
  • Email campaigns
  • Blog posts (works everywhere)
  • Quick previews
Not ideal for:
  • Long recordings (file size explodes)
  • High-quality demos (limited to 256 colors)
  • Videos with smooth gradients or photos
  • Complex animations

Limitations

  • No sound: GIF format doesn’t support audio
  • Lower frame rate: Fixed at 15fps vs 60fps for MP4
  • Color limitations: 256 colors per frame
  • File size: Can be larger than equivalent MP4 for long videos
  • No quality control: GIF encoding quality is fixed

Optimization Tips

GIFs grow quickly with duration. Aim for:
  • Under 5 seconds: Excellent
  • 5-10 seconds: Good
  • 10-20 seconds: File size may be large
  • Over 20 seconds: Consider MP4 instead
GIF file size is directly proportional to pixel count:
{
  "videos": {
    "compact-gif": {
      "url": "https://example.com",
      "viewport": { "width": 1080, "height": 720 },
      "output": "demo.gif",
      "steps": []
    }
  }
}
The 256-color palette struggles with:
  • Smooth gradients
  • Photographs
  • Subtle shadows
  • Color-rich interfaces
Flat design and high-contrast interfaces work best.

WebM Format

Configuration

Set the output field to a .webm extension:
{
  "videos": {
    "web-demo": {
      "url": "https://example.com",
      "output": "web-demo.webm",
      "steps": []
    }
  }
}
From the webm-output example:
{
  "$schema": "https://webreel.dev/schema/v1.json",
  "videos": {
    "webm-output": {
      "url": "./web/index.html",
      "viewport": { "width": 1920, "height": 1080 },
      "zoom": 2,
      "output": "webm-output.webm",
      "waitFor": ".dashboard",
      "steps": [
        { "action": "pause", "ms": 500 },
        { "action": "click", "selector": ".menu-item", "delay": 1000 }
      ]
    }
  }
}

Encoding Details

WebM files use VP9 encoding:
  • Video codec: libvpx-vp9
  • CRF: 30 (good quality, smaller files)
  • Bitrate: Variable (VBR, b:v 0)
  • Pixel format: yuv420p
  • Audio codec: Opus at 128kbps (when sound effects enabled)
From the source (packages/@webreel/core/src/media.ts:157):
runFfmpeg(ffmpegPath, [
  "-y",
  "-i",
  tempVideo,
  "-c:v",
  "libvpx-vp9",
  "-crf",
  "30",
  "-b:v",
  "0",
  "-pix_fmt",
  "yuv420p",
  silentWebm,
]);

Use Cases

Best for:
  • Web documentation sites
  • Embedded video players
  • Smaller file sizes than MP4
  • Modern web applications
Not ideal for:
  • Social media (MP4 has better support)
  • Maximum compatibility (Safari support is limited on older versions)
  • iOS apps

Browser Support

BrowserSupport
ChromeFull support
FirefoxFull support
EdgeFull support
Safari (macOS 14.1+)Full support
Safari (older)Limited or no support
iOS Safari (17.1+)Full support
iOS Safari (older)Limited or no support

File Size Comparison

For a typical 10-second demo at 1920x1080:
  • MP4 (CRF 18): ~5-8 MB
  • WebM (CRF 30): ~3-5 MB
  • GIF (15fps): ~8-15 MB
WebM generally produces the smallest files while maintaining good quality.

Format Selection Guide

  • You need universal compatibility
  • Posting to social media (Twitter, LinkedIn, etc.)
  • Creating high-quality demos
  • You want sound effects
  • Maximum quality is important
  • You’re unsure (it’s the safest default)

Multiple Formats from One Recording

You can generate multiple formats by defining separate video entries:
{
  "videos": {
    "demo-mp4": {
      "url": "https://example.com",
      "output": "demo.mp4",
      "include": ["./steps/demo-steps.json"],
      "steps": []
    },
    "demo-gif": {
      "url": "https://example.com",
      "output": "demo.gif",
      "include": ["./steps/demo-steps.json"],
      "steps": []
    }
  }
}
Create steps/demo-steps.json:
[
  { "action": "pause", "ms": 500 },
  { "action": "click", "text": "Get Started" }
]
This records the same steps twice, producing both demo.mp4 and demo.gif.

Thumbnails

All formats automatically generate a PNG thumbnail from the first frame:
videos/
  my-demo.mp4
  my-demo.png      # Thumbnail

Custom Thumbnail Time

Extract the thumbnail from a specific timestamp:
{
  "videos": {
    "my-demo": {
      "url": "https://example.com",
      "thumbnail": {
        "time": 2.5
      },
      "steps": []
    }
  }
}
This captures the frame at 2.5 seconds.

Disable Thumbnails

{
  "videos": {
    "my-demo": {
      "url": "https://example.com",
      "thumbnail": {
        "enabled": false
      },
      "steps": []
    }
  }
}

Next Steps

Advanced Actions

Learn drag-and-drop, scrolling, and complex interactions

Troubleshooting

Common issues and solutions

Configuration Reference

Complete configuration options

Build docs developers (and LLMs) love