diff --git a/packages/qr-code-scanner/src/qr-code-scanner.js b/packages/qr-code-scanner/src/qr-code-scanner.js
index 642fef718bad6bedd103348a5d89267efbab56a0..d4d79d9b8806c1fef62f8bc22a01a5d3862fac20 100644
--- a/packages/qr-code-scanner/src/qr-code-scanner.js
+++ b/packages/qr-code-scanner/src/qr-code-scanner.js
@@ -86,6 +86,7 @@ export class QrCodeScanner extends ScopedElementsMixin(DBPLitElement) {
 
         this.clipMask = false;
         this._devices = new Map();
+        this._requestID = null;
     }
 
     static get scopedElements() {
@@ -192,13 +193,20 @@ export class QrCodeScanner extends ScopedElementsMixin(DBPLitElement) {
             video.play();
             that.videoRunning = true;
             qrContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
-            requestAnimationFrame(tick);
+            if (that._requestID !== null) {
+                cancelAnimationFrame(that._requestID);
+                that._requestID = null;
+            }
+            console.assert(that._requestID === null);
+            that._requestID = requestAnimationFrame(tick);
         }).catch((e) => { console.log(e); that.askPermission = true;});
 
         let lastVideoTime = -1;
         let lastCode = null;
 
         function tick() {
+           that._requestID = null;
+
            if (that.sourceChanged) {
                 video.srcObject.getTracks().forEach(function(track) {
                     track.stop();
@@ -340,7 +348,8 @@ export class QrCodeScanner extends ScopedElementsMixin(DBPLitElement) {
                 qrContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
                 scroll = true;
             }
-            requestAnimationFrame(tick);
+            console.assert(that._requestID === null);
+            that._requestID = requestAnimationFrame(tick);
         }
     }