Please bookmark this page to avoid losing your image tool!

PNG To MP4 Video 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, durationStr = "5") {
    // Parse duration input or fallback to default
    const duration = parseFloat(durationStr) || 5;
    const fps = 30;

    // Limit maximum dimensions for performance and encoder compatibility (e.g., 1080p limit)
    let targetW = originalImg.width;
    let targetH = originalImg.height;
    const maxDim = 1920;
    if (targetW > maxDim || targetH > maxDim) {
        const ratio = Math.min(maxDim / targetW, maxDim / targetH);
        targetW = Math.round(targetW * ratio);
        targetH = Math.round(targetH * ratio);
    }
    
    // Video encoders generally require width and height to be even numbers
    const encWidth = Math.floor(targetW / 2) * 2;
    const encHeight = Math.floor(targetH / 2) * 2;
        
    const canvas = document.createElement('canvas');
    canvas.width = encWidth;
    canvas.height = encHeight;
    const ctx = canvas.getContext('2d');
    
    // Base layer layout (fill background with black to handle PNG transparency)
    function paintCanvas() {
        ctx.fillStyle = '#000000';
        ctx.fillRect(0, 0, encWidth, encHeight);
        ctx.drawImage(originalImg, 0, 0, encWidth, encHeight);
    }
    paintCanvas();

    // Helper UI function to display output visually
    function createOutputElement(blob, ext) {
        const container = document.createElement('div');
        container.style.display = 'flex';
        container.style.flexDirection = 'column';
        container.style.alignItems = 'center';
        container.style.gap = '16px';
        container.style.padding = '16px';
        container.style.boxSizing = 'border-box';
        container.style.fontFamily = 'sans-serif';

        const video = document.createElement('video');
        video.src = URL.createObjectURL(blob);
        video.controls = true;
        video.autoplay = true;
        video.loop = true;
        video.style.maxWidth = '100%';
        video.style.borderRadius = '8px';
        video.style.boxShadow = '0 4px 6px rgba(0,0,0,0.1)';
        video.style.backgroundColor = '#000';
        
        const downloadBtn = document.createElement('a');
        downloadBtn.href = video.src;
        downloadBtn.download = `converted_video.${ext}`;
        downloadBtn.textContent = `Download Video (${ext.toUpperCase()})`;
        downloadBtn.style.padding = '10px 20px';
        downloadBtn.style.backgroundColor = '#007bff';
        downloadBtn.style.color = '#fff';
        downloadBtn.style.textDecoration = 'none';
        downloadBtn.style.borderRadius = '4px';
        downloadBtn.style.fontWeight = 'bold';
        downloadBtn.style.transition = 'background-color 0.2s';
        downloadBtn.onmouseover = () => downloadBtn.style.backgroundColor = '#0056b3';
        downloadBtn.onmouseout = () => downloadBtn.style.backgroundColor = '#007bff';

        container.appendChild(video);
        container.appendChild(downloadBtn);

        return container;
    }

    // Fallback approach using standard MediaRecorder for real-time capture
    async function fallbackMediaRecorder() {
        return new Promise((resolve) => {
            const stream = canvas.captureStream(fps);
            let mimeType = '';
            
            const supportedTypes = [
                'video/mp4',
                'video/webm;codecs=h264',
                'video/webm;codecs=vp9',
                'video/webm;codecs=vp8',
                'video/webm'
            ];
            
            for (const type of supportedTypes) {
                if (MediaRecorder.isTypeSupported(type)) {
                    mimeType = type;
                    break;
                }
            }

            const recorder = new MediaRecorder(stream, { mimeType: mimeType || undefined });
            const chunks = [];

            recorder.ondataavailable = e => {
                if (e.data.size > 0) chunks.push(e.data);
            };

            let finalized = false;
            recorder.onstop = () => {
                if (finalized) return;
                finalized = true;
                const ext = (mimeType && mimeType.includes('mp4')) ? 'mp4' : 'webm';
                const blob = new Blob(chunks, { type: mimeType || 'video/webm' });
                resolve(createOutputElement(blob, ext));
            };
            
            recorder.start();

            const totalTimeMs = duration * 1000;
            let start = null;

            function drawFrame(now) {
                if (!start) start = now;
                
                paintCanvas();
                
                // Extremely subtle sub-pixel change to force canvas update stream detection
                ctx.fillStyle = `rgba(0,0,0,${Math.random() * 0.01})`; 
                ctx.fillRect(0, 0, 1, 1);
                
                if (now - start < totalTimeMs) {
                    requestAnimationFrame(drawFrame);
                } else {
                    recorder.stop();
                }
            }
            requestAnimationFrame(drawFrame);
        });
    }

    // Try natively muxing utilizing hardware WebCodecs for exact, genuine .mp4 output files
    try {
        if (!window.VideoEncoder) {
            throw new Error("WebCodecs API not supported by browser environment.");
        }

        const muxerUrl = 'https://unpkg.com/mp4-muxer/build/mp4-muxer.mjs';
        const Mp4Muxer = await import(muxerUrl);

        const muxer = new Mp4Muxer.Muxer({
            target: new Mp4Muxer.ArrayBufferTarget(),
            video: {
                codec: 'avc',
                width: encWidth,
                height: encHeight
            },
            fastStart: 'in-memory',
        });

        const videoEncoder = new window.VideoEncoder({
            output: (chunk, meta) => muxer.addVideoChunk(chunk, meta),
            error: e => { throw e; }
        });
        
        const encoderConfig = {
            codec: 'avc1.42001f', // Baseline profile generic fallback configuration
            width: encWidth,
            height: encHeight,
            bitrate: 2_000_000,
            framerate: fps,
        };

        const support = await window.VideoEncoder.isConfigSupported(encoderConfig);
        if (!support.supported) {
            throw new Error("Target VideoEncoder configuration is restricted.");
        }

        videoEncoder.configure(encoderConfig);

        const totalFrames = Math.floor(duration * fps);
        for (let i = 0; i < totalFrames; i++) {
            const frame = new window.VideoFrame(canvas, {
                timestamp: (i * 1000000) / fps
            });
            const insertKeyFrame = (i % (fps * 2) === 0);
            videoEncoder.encode(frame, { keyFrame: insertKeyFrame });
            frame.close();
            
            // Allow asynchronous dispatching overhead block loop iteration to bypass browser stalling
            if (i % 5 === 0) await new Promise(r => setTimeout(r, 0));
        }

        await videoEncoder.flush();
        muxer.finalize();

        const buffer = muxer.target.buffer;
        const blob = new Blob([buffer], { type: 'video/mp4' });
        
        return createOutputElement(blob, 'mp4');

    } catch (e) {
        console.warn("Falling back to MediaRecorder setup because genuine WebCodecs mp4 encoding collapsed:", e.message);
        return await fallbackMediaRecorder();
    }
}

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 converts static PNG images into MP4 video files. Users can specify a custom duration for the resulting video, which is rendered at a smooth 30 frames per second. The converter automatically handles image scaling for optimal video compatibility and includes a black background fill to ensure transparent PNG elements are displayed correctly in the video format. This tool is useful for social media creators who want to turn static graphics into video clips, or for developers needing to convert image assets into video formats for web or presentation use.

Leave a Reply

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