Signatur
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 übersrcObject.
Ü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
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
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);
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));
// Wichtig · Fallstricke
- Canvas mit fremden Inhalten (tainted): Wurden Bilder oder Videos aus anderen Origins ohne CORS-Freigabe gezeichnet, wirft
captureStream()einenSecurityError. 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 oderrequestFrame()nutzen. - Hintergrund-Tabs:
requestAnimationFramewird in inaktiven Tabs pausiert, dadurch stockt der Stream. Für Aufnahmen im Hintergrund andere Timer oder einen Worker mitOffscreenCanvasverwenden. - 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.
frameRequestRatesinnvoll begrenzen. - Kompatibilität: Wird von allen modernen Browsern unterstützt. Safari kann erst ab Version 14.1 mit
MediaRecorderaufnehmen, und die unterstützten MIME-Typen unterscheiden sich (vorher mitMediaRecorder.isTypeSupported()prüfen). Ältere Firefox-Versionen kannten nurmozCaptureStream(). - Tracks beenden, wenn sie nicht mehr gebraucht werden:
stream.getTracks().forEach(t => t.stop()).