Please bookmark this page to avoid losing your image tool!

MP4 To PNG Webpage Converter

(Free & Supports Bulk Upload)

Drag & drop your images here or

The result will appear here...
You can edit the below JavaScript code to customize the image tool.
async function processImage(originalImg, targetTimeInSeconds = "0", numberOfFrames = "1", layout = "vertical", customWidth = "") {
    return new Promise((resolve, reject) => {
        const targetTime = parseFloat(targetTimeInSeconds);
        const numFrames = targetTime > 0 ? 1 : Math.max(1, parseInt(numberOfFrames) || 1);
        const src = originalImg.src;

        const video = document.createElement('video');
        video.src = src;
        video.crossOrigin = 'anonymous';
        video.muted = true;
        video.setAttribute('playsinline', '');

        const frames = [];
        let times = [];
        let currentIndex = 0;

        const getTimes = () => {
            const t = [];
            if (targetTime > 0) {
                t.push(targetTime);
                return t;
            }
            const d = video.duration && isFinite(video.duration) ? video.duration : 0;
            if (d === 0) {
                for (let i = 0; i < numFrames; i++) t.push(0);
            } else if (numFrames === 1) {
                t.push(0); 
            } else {
                const segment = d / numFrames;
                for (let i = 0; i < numFrames; i++) {
                    t.push(segment * i + segment / 2);
                }
            }
            return t;
        };

        const captureFrame = () => {
            const w = video.videoWidth;
            const h = video.videoHeight;
            if (w === 0 || h === 0) return;
            const tempCanvas = document.createElement('canvas');
            tempCanvas.width = w;
            tempCanvas.height = h;
            tempCanvas.getContext('2d').drawImage(video, 0, 0, w, h);
            frames.push(tempCanvas);
        };

        const stitchFrames = () => {
            if (frames.length === 0) {
                // Should zero frames be generated due to rendering issue, use the fallback.
                return fallbackToImage();
            }
            const finalCanvas = document.createElement('canvas');
            const w = customWidth ? parseInt(customWidth) : frames[0].width;
            const h = customWidth ? Math.floor(frames[0].height * (w / frames[0].width)) : frames[0].height;

            let cols = 1, rows = frames.length;
            if (layout === 'horizontal') {
                cols = frames.length;
                rows = 1;
            } else if (layout === 'grid') {
                cols = Math.ceil(Math.sqrt(frames.length));
                rows = Math.ceil(frames.length / cols);
            }

            finalCanvas.width = w * cols;
            finalCanvas.height = h * rows;
            const ctx = finalCanvas.getContext('2d');

            frames.forEach((canvas, index) => {
                const col = index % cols;
                const row = Math.floor(index / cols);
                ctx.drawImage(canvas, col * w, row * h, w, h);
            });

            resolve(finalCanvas);
        };

        const seekToNext = () => {
            if (currentIndex < numFrames) {
                let target = times[currentIndex];
                
                // Adjust to ensure we don't seek beyond bounds
                if (video.duration && target > video.duration) target = video.duration - 0.1;
                
                if (Math.abs(video.currentTime - target) > 0.01) {
                    video.currentTime = target;
                } else {
                    captureFrame();
                    currentIndex++;
                    seekToNext();
                }
            } else {
                stitchFrames();
            }
        };

        video.onloadeddata = () => {
            times = getTimes();
            seekToNext();
        };

        video.onseeked = () => {
            captureFrame();
            currentIndex++;
            seekToNext();
        };

        const fallbackToImage = () => {
            if (!originalImg.src) return reject(new Error("No source provided to the image."));
            const fallback = () => {
                try {
                    const finalCanvas = document.createElement('canvas');
                    let cw = originalImg.width || originalImg.naturalWidth;
                    let ch = originalImg.height || originalImg.naturalHeight;

                    if (customWidth) {
                        const tgtW = parseInt(customWidth);
                        ch = Math.floor(ch * (tgtW / Math.max(1, cw)));
                        cw = tgtW;
                    }

                    finalCanvas.width = cw || 800; // Provide defaults in abnormal cases
                    finalCanvas.height = ch || 600;
                    finalCanvas.getContext('2d').drawImage(originalImg, 0, 0, finalCanvas.width, finalCanvas.height);
                    resolve(finalCanvas);
                } catch (e) {
                    reject(e);
                }
            };

            // In case the item loaded originally as a pure image instead of MP4 stream
            if (originalImg.complete && originalImg.naturalWidth > 0) {
                fallback();
            } else {
                originalImg.onload = fallback;
                originalImg.onerror = () => reject(new Error("Failed to load as both a valid video stream and an image format."));
            }
        };

        video.onerror = fallbackToImage;

        // Force sequence execution
        if (src) {
           video.load();
        } else {
           fallbackToImage();
        }
    });
}

Free Image Tool Creator

Can't find the image tool you're looking for?
Create one based on your own needs now!

Description

This tool allows you to convert MP4 video files into PNG images by extracting specific frames. You can choose to capture a single frame at a specific timestamp or extract multiple frames distributed throughout the video. The extracted frames can then be organized into a single composite image using vertical, horizontal, or grid layouts. This is useful for creating video contact sheets, extracting thumbnails for video libraries, or capturing specific moments from a video for use in graphic design and presentations.

Leave a Reply

Your email address will not be published. Required fields are marked *