Capturing web page video with a couple of bookmarklets

June 3rd, 2024. Tagged: bookmarklets, JavaScript


I recently saw someone sharing a blog post on social media using a video that just scrolls through the blog post. I wondered if a video like this can be created easily and automatically. Using a simple bookmarklet. Turns out yes! I ended up with two bookmarklets because they do different and independent things: one captures video (not only of a web page, could be the whole screen) and one scrolls the page stopping at every h* tag. All in JavaScript, all in a bookmarklet, using web APIs. Here's what the end result looks like when running on a post in this here blog:

Bookmarklet 1: capture

Using MediaDevices part of the Media Stream API we can get access to the user's screen, or window, or a tab within a browser window.

We combine this with the MediaStream Recorder API which lets us take the captured data and display it in a hidden <video> element.

The last step is the ole a href where the href points to the video's src. And we add a download attribute and an auto-click to the link.

May sound complicated but it's in fact not too much code. Here it is in its entirety:

const video = document.createElement('video');
video.style = 'display: none';
document.body.append(video);

navigator.mediaDevices.getDisplayMedia({
    video: true,
    selfBrowserSurface: "include",
  }).then(stream => {
    go(stream);
  }
);

function go(stream) {
  const mediaRecorder = new MediaRecorder(stream);

  const chunks = []
  mediaRecorder.addEventListener('dataavailable', (e) => {
      chunks.push(e.data)
  })

  mediaRecorder.addEventListener('stop', () => {
    const blob = new Blob(chunks, {
        type: chunks[0].type
    });
    video.src = URL.createObjectURL(blob);

    const a = document.createElement('a');
    a.href = video.src;
    a.download = 'video.webm';
    a.click();
  })

  mediaRecorder.start();
}

This code is general purpose, you can capture any old screen. But for my purpose I wanted to scroll through a page. So..

Bookmarklet 2: the heading scroller

This one is easier to explain, even though it ended up just as long (in terms of lines of code) as the first one. Here we want to select all heading elements. Then scroll to each one, waiting for 2 seconds before moving on. When done, scroll to the bottom of the page. Then scroll back to the top. Here's the code:

const headers = Array.from(document.querySelectorAll('h1, h2, h3, h4, h5, h6'));
let currentHeaderIndex = 0;

const chillFor = 2000; // wait this long before moving on

function scrollDown() {
  const currentHeader = headers[currentHeaderIndex];
  const top = currentHeader.offsetTop;

  window.scrollTo({
    top,
    behavior: 'smooth'
  });

  currentHeaderIndex++;

  if (currentHeaderIndex < headers.length) {
    setTimeout(scrollDown, chillFor); 
  } else {
    setTimeout(() => {
      window.scrollTo({
        top: document.body.scrollHeight,
        behavior: 'smooth'
      });
      setTimeout(() => {
        window.scrollTo({
          top: 0,
          behavior: 'smooth'
        });
      }, chillFor);
    }, chillFor);
  }
}

scrollDown();

Downloads and usage

Drag these links to your bookmarks:

Then go to a page you like, click the first bookmarklet to start capturing. Click the second to auto-scroll. Stop capturing and the video should be in your downloads.

Parting words

This should work x-browser, although I tested only Firefox and Chrome. The browsers behave differently, for example Firefox includes the browser's bookmarks toolbar and devtools (if you have them open) while Chrome doesn't.

I didn't futz about with codecs and such, left it to the browser to decide. Seems like both browsers decide WEBM as a default format. If your destination of choice doesn't support WEBM, you may need to convert the video to e.g. MP4. I personally use HandBrake for this purpose.

Like 'em bookmarks? Want to improve them? LMK!

Tell your friends about this post on Facebook and Twitter

Sorry, comments disabled and hidden due to excessive spam.

Meanwhile, hit me up on twitter @stoyanstefanov