URL: https://video-to-screenshots.peterbe.com

I made a web app that helps you extract screenshots from a video file. You technically don't "upload" it but you select a video file from your computer into the web app, and the screenshots are generated more or less instantly.

With drop shadow

Canvas Web API

Why did I make this app? Because I wanted to experiment with the Canvas (Web) API and how it can be combined with a video element. I originally typed into some AI prompt and got most of the code from that, but I felt like I didn't understand it. And to be able to quickly iterate and play with it, I ended up making a simple web app so that I can tune it.

How it works

The gist of the code is this:


export function createVideoThumbnail(
  videoFile: File,
  options: Options,
): Promise<string> {
  const { quality = 1.0, captureTime = 0.1, format = "image/jpeg" } = options
  return new Promise((resolve, reject) => {
    const video = document.createElement("video")
    const canvas = document.createElement("canvas")
    const ctx = canvas.getContext("2d")

    if (!ctx) {
      reject(new Error("Failed to get canvas context"))
      return
    }

    video.preload = "metadata"
    video.muted = true
    video.playsInline = true

    const videoUrl = URL.createObjectURL(videoFile)
    video.src = videoUrl

    video.onloadedmetadata = () => {
      const width = video.videoWidth
      const height = video.videoHeight
      canvas.width = width
      canvas.height = height
      video.currentTime = captureTime
    }

    video.onseeked = () => {
      ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
      const dataURI = canvas.toDataURL(format, quality)
      URL.revokeObjectURL(videoUrl)
      resolve(dataURI)
    }

    video.onerror = () => {
      URL.revokeObjectURL(videoUrl)
      reject(new Error("Error loading video file"))
    }

    video.load()
  })
}

See src/create-video-thumbnail.ts on GitHub.

The project

The code for it is here: https://github.com/peterbe/video-to-screenshots

The web app is hosted on Firebase Hosting. The foundation of the code is React with react-router, and uses pico css. I use Bun and Vite to run and build the app.

Conclusion?

It works and it's basic. You can download the JPEGs. It's not very pretty. It's a weekend project and it accomplishes something.

But it doesn't work in Safari. Anybody got any ideas?

Perhaps it would be cool to allow the user to see many many more thumbnails and allow them to specify more exactly which capture times to make screenshots out of. What do you think?

Comments

Your email will never ever be published.

Previous:
A Python dict that can report which keys you did not use June 12, 2025 Python
Related by category:
Parse a CSV file with Bun September 13, 2023 Bun
Switching from Next.js to Vite + wouter July 28, 2023 JavaScript, React
How to SSG a Vite SPA April 26, 2025 Bun, JavaScript, React
An ideal pattern to combine React Router with TanStack Query November 18, 2024 JavaScript, React
Related by keyword:
How to SSG a Vite SPA April 26, 2025 Web Performance, React, JavaScript, Bun
The technology behind You Should Watch January 28, 2023 React, Firebase, JavaScript, You Should Watch
Lazy-load Firebase Firestore and Firebase Authentication in Preact September 2, 2020 Web development, Web Performance, Preact, JavaScript
downloadAndResize - Firebase Cloud Function to serve thumbnails December 8, 2020 Web development, Node, That's Groce!, JavaScript