Audio recording and screen capture

This guide explains different approaches for recording audio and video from a tab, window, or screen using APIs such as chrome.tabCapture or getDisplayMedia().

Screen recording

For screen recording, call getDisplayMedia(), which triggers the dialog box shown below. This provides the user with the ability to select which tab, window or screen they wish to share and provides a clear indication that recording is taking place.

Screen share dialog for example.com
Screen share dialog for example.com.

The following example requests access to record both audio and video.

const stream = await navigator.mediaDevices.getDisplayMedia({ audio: true, video: true });

If called within a content script, recording will automatically end when the user navigates to a new page. To record in the background and across navigations, use an offscreen document with the DISPLAY_MEDIA reason.

Tab capture based on user gesture

Calling getDisplayMedia() results in the browser showing a dialog which asks the user what they would like to share. However, in some cases the user has just clicked on the action button to invoke your extension for a specific tab, and you would like to immediately start capturing the tab without this prompt.

Record audio and video in the background

Starting in Chrome 116, you can call the chrome.tabCapture API in a service worker to obtain a stream ID following user gesture. This can then be passed to an offscreen document to start recording.

In your service worker:

chrome.action.onClicked.addListener(async (tab) => {
  const existingContexts = await chrome.runtime.getContexts({});

  const offscreenDocument = existingContexts.find(
    (c) => c.contextType === 'OFFSCREEN_DOCUMENT'
  );

  // If an offscreen document is not already open, create one.
  if (!offscreenDocument) {
    // Create an offscreen document.
    await chrome.offscreen.createDocument({
      url: 'offscreen.html',
      reasons: ['USER_MEDIA'],
      justification: 'Recording from chrome.tabCapture API',
    });
  }

  // Get a MediaStream for the active tab.
  const streamId = await chrome.tabCapture.getMediaStreamId({
    targetTabId: tab.id
  });

  // Send the stream ID to the offscreen document to start recording.
  chrome.runtime.sendMessage({
    type: 'start-recording',
    target: 'offscreen',
    data: streamId
  });
});

Then, in your offscreen document:

chrome.runtime.onMessage.addListener(async (message) => {
  if (message.target !== 'offscreen') return;
  
  if (message.type === 'start-recording') {
    const media = await navigator.mediaDevices.getUserMedia({
      audio: {
        mandatory: {
          chromeMediaSource: "tab",
          chromeMediaSourceId: message.data,
        },
      },
      video: {
        mandatory: {
          chromeMediaSource: "tab",
          chromeMediaSourceId: message.data,
        },
      },
    });

    // Continue to play the captured audio to the user.
    const output = new AudioContext();
    const source = output.createMediaStreamSource(media);
    source.connect(output.destination);

    // TODO: Do something to recording the MediaStream.
  }
});

For a full example, see the Tab Capture - Recorder sample.

Record audio and video in a new tab

Prior to Chrome 116, it was not possible to use the chrome.tabCapture API in a service worker or to consume a stream ID created by that API in an offscreen document. Both of these are requirements for the approach above.

Instead, you can open an extension page in a new tab or window, and directly obtain a stream. Set the targetTabId property to capture the correct tab.

Start by opening an extension page (perhaps in your popup or service worker):

chrome.windows.create({ url: chrome.runtime.getURL("recorder.html") });

Then, in your extension page:

chrome.tabCapture.getMediaStreamId({ targetTabId: tabId }, async (id) => {
  const media = await navigator.mediaDevices.getUserMedia({
    audio: {
      mandatory: {
        chromeMediaSource: "tab",
        chromeMediaSourceId: id,
      },
    },
    video: {
      mandatory: {
        chromeMediaSource: "tab",
        chromeMediaSourceId: id,
      },
    },
  });

  // Continue to play the captured audio to the user.
  const output = new AudioContext();
  const source = output.createMediaStreamSource(media);
  source.connect(output.destination);
});

Alternatively, consider using the screen recording approach which allows you to record in the background using an offscreen document, but shows the user a dialog to select a tab, window or screen to record from.

Record audio in a popup

If you only need to record audio, you can directly obtain a stream in the extension popup using chrome.tabCapture.capture. When the popup closes, recording will be stopped.

chrome.tabCapture.capture({ audio: true }, (stream) => {
  // Continue to play the captured audio to the user.
  const output = new AudioContext();
  const source = output.createMediaStreamSource(stream);
  source.connect(output.destination);

  // TODO: Do something with the stream (e.g record it)
});

If you need the recording to persist across navigations, consider using the approach described in the previous section.

Other considerations

For more information on how to record a stream, see the MediaRecorder API.