// A generic game, subclass and add specific initialisation and features
class Game {
    constructor() {
        // Physics updates run at a fixed frequency, independent of rendering
        // frame rate.
        this.physicsPerSecond = 25;
        this.physicsFrameTime = 1000 / this.physicsPerSecond;
        this.framesBeforeDrop = 5;

        this.startTime = performance.now();
        this.lastTime = this.startTime;
        this.physicsFrameTimeAccumulator = this.startTime;

        // Performance metrics
        this.skippedUpdates = 0;
        this.multiUpdates = 0;
        this.maxMultiUpdate = 0;

        // world
        this.physicsSystem = new PhysicsSystem({minX: 0, minY:0, maxX:100, maxY:100});
    }

    run() {
        window.requestAnimationFrame(this.gameloop.bind(this));
    }

    end() {
    }

    update(physicsFrameTime) {
    }

    render(interpolate) {
    }

    gameloop(time) {
        const timeSinceStart = time - this.startTime;

        if (timeSinceStart <= 60000) {
            // run for one minute
            window.requestAnimationFrame(this.gameloop.bind(this));
        } else {
            console.log(`skipped updates (good) = ${this.skippedUpdates}`);
            console.log(`multi updates (bad) = ${this.multiUpdates}`);
            console.log(`max multi update = ${this.maxMultiUpdate}`);    
            this.end();
            return;
        }

        const frameTime = time - this.lastTime;
        this.lastTime = time;

        let loops = 0;
        while (performance.now() > this.physicsFrameTimeAccumulator && loops < this.framesBeforeDrop) {
            // using a frameTime less than the full physicsFrameTime to get the 
            // hands behaving properly when an absurdly low physics fps is set.
            // Wouldn't bother in a proper game loop with a high physics fps.
            this.update(this.physicsFrameTime);
            this.physicsFrameTimeAccumulator += this.physicsFrameTime;
            loops ++;
        }

        // Log some performanc metrics
        if (loops === 0) this.skippedUpdates++;
        else if (loops > 1) {
            this.multiUpdates++;
            if (loops > this.maxMultiUpdate) this.maxMultiUpdate = loops;
        }

        // Run rendering update, pass the interpolate value, which indicates
        // (using a value between 0 and 1) how far between physics updates we are.
        this.render((performance.now() + this.physicsFrameTime - this.physicsFrameTimeAccumulator) / this.physicsFrameTime);
    }
}

@@ -710,16 +710,45 @@
 Y:100%7D);
+%0A%0A        this.abort = false;
 %0A    %7D%0A%0A
@@ -937,24 +937,57 @@
 oop(time) %7B%0A
+        if (this.abort) return;%0A%0A
         cons
@@ -1538,24 +1538,42 @@
 me = time;%0A%0A
+        try %7B%0A    
         let 
@@ -1591,16 +1591,20 @@
         
+  
+  
 while (p
@@ -1703,16 +1703,20 @@
         
+    
 // using
@@ -1776,16 +1776,20 @@
 et the %0A
+    
         
@@ -1872,16 +1872,20 @@
         
+    
 // Would
@@ -1950,24 +1950,28 @@
             
+    
 this.update(
@@ -1998,32 +1998,36 @@
 e);%0A            
+  
+  
 this.physicsFram
@@ -2081,16 +2081,20 @@
         
+    
 loops ++
@@ -2091,24 +2091,28 @@
   loops ++;%0A
+    
         %7D%0A%0A 
@@ -2118,16 +2118,20 @@
         
+    
 // Log s
@@ -2145,32 +2145,36 @@
 formanc metrics%0A
+    
         if (loop
@@ -2213,16 +2213,20 @@
         
+  
+  
 else if 
@@ -2243,32 +2243,36 @@
 ) %7B%0A            
+    
 this.multiUpdate
@@ -2268,32 +2268,36 @@
 multiUpdates++;%0A
+    
             if (
@@ -2354,35 +2354,43 @@
  loops;%0A        
-%7D%0A%0A
+    %7D%0A%0A    
         // Run r
@@ -2458,16 +2458,20 @@
         
+  
+  
 // (usin
@@ -2537,16 +2537,20 @@
 we are.%0A
+    
         
@@ -2662,20 +2662,252 @@
 FrameTime);%0A
+        %7D catch(exception) %7B%0A            // Next rAF already registered. We need to make sure it doesn't do anything.%0A            console.error('Bailing out!');%0A            this.abort = true;%0A            throw exception;;%0A        %7D%0A
     %7D%0A%7D%0A

Abort rAF if errors occur