Generate video from Canvas

Will generate a video of the recorded animation in the canvas. If the drawImage is set to true, the Image will be used as a background for the canvas using drawImage. In this case the video generation will fail and produce a 0 byte file only.

by keyboardsamurai

HTML

<html lang="en">

<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>

<div id="panel">

    <div id="recording_status" style="color: white; background-color: green; width: 150px;" class="panel-item">
        Not recording
    </div>

    <label for="toggle" class="panel-item">
        <input type="checkbox" checked onclick="toggleDrawImage();" id="toggle">
        <span>Use drawImage() = </span>
        <span id="imageState">undefined</span>
    </label>

    <div class="panel-item">
        <button onclick="renderVideo();">Render Video</button>
    </div>

    <div class="panel-item">
        <span id="panel-result-size"></span>
    </div>
</div>
<canvas id="drawing_canvas" width="1280" height="720"></canvas>
<script></script>
</body>
</html>

CSS

body {
  font-family: 'Arial', cursive;
}

#panel {
  display: inline-block;
  width: 100%;
  margin-bottom: 20px;
}

.panel-item {
  float: left;
  margin-left: 50px;
}

JavaScript

let drawImage = true;
    document.getElementById("imageState").innerText = drawImage;

    const image = new Image();
    image.src = 'https://66.media.tumblr.com/84d332cafeb1052c477c979281e5713b/tumblr_owe3l0tkCj1wxdq3zo1_1280.jpg';

    let allChunks, canvas, recorder;

    function renderVideo() {
        if (recorder && recorder.state === 'recording') {
            console.log('Wait until recorder is inactive');
            return;
        }
        document.getElementById('panel-result-size').innerText = '';

        // during rendering canvas becomes tainted, so we have to recreate the canvas each time
        document.body.removeChild(document.getElementById("drawing_canvas"));
        const c = document.createElement('canvas');
        c.width = 1280;
        c.height = 720;
        c.id = 'drawing_canvas';
        document.body.appendChild(c);

        allChunks = [];
        window.requestAnimationFrame(animation);
        canvas = document.getElementById('drawing_canvas');

        recorder = initMediaRecorder(canvas);
        recorder.start();

        setTimeout(function (e) {
            console.log("Video ended");
            recorder.stop();
        }, 5000);
    }

    function toggleDrawImage() {
        drawImage = !drawImage;
        document.getElementById("imageState").innerText = drawImage
    }

    function initMediaRecorder(canvasElement) {
        const stream = canvasElement.captureStream(60);
        const recorder = new MediaRecorder(stream, {mimeType: 'video/webm'});
        recorder.ondataavailable = function (e) {
            console.log("data handler called");
            if (e.data) {
                console.log("data available: " + e.data.size)
                if (e.data.size > 0) {
                    console.log("data added");
                    allChunks.push(e.data);
                }
            } else {
                console.error("Data handler received no data in event: " + JSON.stringify(e))
            }
       ...