Start · Sprachen · JavaScript · Referenz · HTMLCanvasElement.captureStream()

HTMLCanvasElement.captureStream()

Methode

Liefert einen <code>MediaStream</code> mit einem <code>CanvasCaptureMediaStreamTrack</code>, der den Canvas-Inhalt als Live-Video bereitstellt.

seit JavaScript Baseline: alle modernen Browser (Chrome 51+, Firef Kategorie: webapi

Signatur

HTMLCanvasElement.prototype.captureStream(frameRequestRate)

Beschreibung

captureStream() macht aus einem <canvas>-Element eine Videoquelle. Die Methode gibt einen MediaStream zurück, der genau einen Video-Track vom Typ CanvasCaptureMediaStreamTrack enthält. Alles, was auf die Canvas gezeichnet wird (2D-Kontext, WebGL, WebGPU), erscheint in diesem Stream.

Typische Einsatzfälle sind:

  • Aufnahme von Animationen, Spielen oder Zeichnungen mit MediaRecorder (z. B. als WebM-Datei),
  • Übertragung per WebRTC (RTCPeerConnection.addTrack()), etwa für bearbeitete Kamerabilder mit Filtern oder Overlays,
  • Vorschau in einem <video>-Element über srcObject.

Über den optionalen Parameter frameRequestRate steuert man, wie oft Frames erfasst werden. Ohne Angabe wird bei jeder Änderung der Canvas ein neues Frame erzeugt. Mit dem Wert 0 werden Frames nur noch manuell über track.requestFrame() erfasst – praktisch, wenn man genau kontrollieren will, wann ein Bild in den Stream geht.

Der Stream enthält nur Video. Wer Ton braucht, fügt einen Audio-Track hinzu, zum Beispiel aus getUserMedia() oder einem MediaStreamAudioDestinationNode der Web Audio API.

Parameter

Name Typ Default Beschreibung
$frameRequestRate number Höchste Bildrate in Frames pro Sekunde (Gleitkommazahl ist erlaubt). Ohne Angabe wird bei jeder Änderung der Canvas ein Frame erfasst. 0 heißt: nur manuelle Erfassung über requestFrame(). Negative Werte lösen einen NotSupportedError aus.

Rückgabewert

Typ
MediaStream
Beschreibung
Ein neuer MediaStream mit einem CanvasCaptureMediaStreamTrack (Video), der den Inhalt der Canvas live wiedergibt.

Beispiele

Canvas-Animation mit MediaRecorder als WebM aufnehmen

const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');

// Einfache Animation: wandernder Kreis
let x = 0;
const draw = () => {
  ctx.fillStyle = '#fff';
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  ctx.fillStyle = 'tomato';
  ctx.beginPath();
  ctx.arc(x, canvas.height / 2, 20, 0, Math.PI * 2);
  ctx.fill();
  x = (x + 2) % canvas.width;
  requestAnimationFrame(draw);
};
draw();

const stream = canvas.captureStream(30); // max. 30 fps
const recorder = new MediaRecorder(stream, { mimeType: 'video/webm' });
const chunks = [];

recorder.ondataavailable = (e) => chunks.push(e.data);
recorder.onstop = () => {
  const blob = new Blob(chunks, { type: 'video/webm' });
  const a = document.createElement('a');
  a.href = URL.createObjectURL(blob);
  a.download = 'animation.webm';
  a.click();
  console.log(`Aufnahme fertig: ${blob.size} Bytes`);
};

recorder.start();
setTimeout(() => recorder.stop(), 5000); // 5 Sekunden aufnehmen
Aufnahme fertig: 412345 Bytes

Live-Vorschau in einem video-Element

const canvas = document.querySelector('#zeichenflaeche');
const video = document.querySelector('#vorschau');

const stream = canvas.captureStream();
video.srcObject = stream;
await video.play();

const [track] = stream.getVideoTracks();
console.log(track.kind, track.readyState);
video live

Frames manuell erfassen mit requestFrame()

const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');

// 0 = keine automatische Erfassung
const stream = canvas.captureStream(0);
const [track] = stream.getVideoTracks();

const renderSlide = (text) => {
  ctx.fillStyle = '#222';
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  ctx.fillStyle = '#fff';
  ctx.font = '32px sans-serif';
  ctx.fillText(text, 20, 60);
  track.requestFrame(); // genau dieses Bild in den Stream geben
};

renderSlide('Folie 1');
setTimeout(() => renderSlide('Folie 2'), 2000);

Canvas per WebRTC übertragen, mit Mikrofon-Ton

const canvas = document.querySelector('canvas');
const canvasStream = canvas.captureStream(25);

const mic = await navigator.mediaDevices.getUserMedia({ audio: true });
const combined = new MediaStream([
  ...canvasStream.getVideoTracks(),
  ...mic.getAudioTracks(),
]);

const pc = new RTCPeerConnection();
combined.getTracks().forEach((t) => pc.addTrack(t, combined));
console.log(combined.getTracks().map((t) => t.kind));
["video", "audio"]

// Wichtig · Fallstricke

  • Canvas mit fremden Inhalten (tainted): Wurden Bilder oder Videos aus anderen Origins ohne CORS-Freigabe gezeichnet, wirft captureStream() einen SecurityError. Abhilfe: crossOrigin="anonymous" setzen und passende CORS-Header auf dem Server.
  • Keine Änderung, kein Frame: Ohne Neuzeichnen entstehen keine neuen Frames. Manche Empfänger (z. B. MediaRecorder) erzeugen dann kurze oder hängende Videos. Bei statischen Inhalten regelmäßig neu zeichnen oder requestFrame() nutzen.
  • Hintergrund-Tabs: requestAnimationFrame wird in inaktiven Tabs pausiert, dadurch stockt der Stream. Für Aufnahmen im Hintergrund andere Timer oder einen Worker mit OffscreenCanvas verwenden.
  • Nur Video: Ton muss als eigener Track ergänzt werden.
  • Leistung: Große Canvas-Flächen mit hoher Bildrate belasten CPU/GPU und Encoder stark. frameRequestRate sinnvoll begrenzen.
  • Kompatibilität: Wird von allen modernen Browsern unterstützt. Safari kann erst ab Version 14.1 mit MediaRecorder aufnehmen, und die unterstützten MIME-Typen unterscheiden sich (vorher mit MediaRecorder.isTypeSupported() prüfen). Ältere Firefox-Versionen kannten nur mozCaptureStream().
  • Tracks beenden, wenn sie nicht mehr gebraucht werden: stream.getTracks().forEach(t => t.stop()).