Performance clarification Javascript animation

I am creating my own little library for creating sprites and animations and i have a working class.
Currently i am trying to find out how the performance of my classes are in the google chrome browser.
I am having a very hard time understanding it tho since the functions that are being called look to be inconsistent.

As can be seen every call the loop sometimes does way more than other times.
I am wondering if this is something i did in my code or if the browser does this to divide the work more equally.

Lib_2D_JS_Biirra_Base.js
/*
An object that keeps track of data that has coordinates in 2d.
For example the location, velocity, acceleration of a sprite.
*/
class Vector2d{
    constructor(x, y){
        this._x = x;
        this._y = y;
    }
    /**
     * Overwrite the current parameters with a Vector2d.
     * @param {Vector2d} vector2d Value to overwrite object parameters with.
     */
    set(vector2d){
        this.setX(vector2d.getX());
        this.setY(vector2d.getY());
    }
    /**
     * Add the values of a Vector2d to the current parameters.
     * @param {Vector2d} vector2d Value to overwrite object parameters with.
     */
    add(vector2d) {
        this._x += vector2d.x;
        this._y += vector2d.y;
    }
    /**
     * Subtract the values of a Vector2d from the current parameters.
     * @param {Vector2d} vector2d Value to overwrite object parameters with.
     */
    sub(vector2d) {
        this._x -= vector2d.x;
        this._y -= vector2d.y;
    }
    /**
     * Multiply the current parameters with a multiplier.
     * @param {number} mult Value to multiply object parameters with.
     */
    mult(mult) {
        this._x *= mult;
        this._y *= mult;
    }
    /**
     * Divide the current parameters with a divider.
     * @param {number} div Value to divide object parameters with.
     */
    div(div) {
        this._x /= div;
        this._y /= div;
    }
    /**
     * Return the magnitude of the object.
     * @returns {number} Contains the magnitude of this object.
     */
    mag() {
        return Math.sqrt((this._x * this._x) + (this._y * this._y));
    };
    // Normalize the parameters of this object.
    norm() {
        let m = this.mag();
        if (m !== 0) {
            this.div(m);
        }
    }
    /**
     * If the current parameters have values above the max this function will overwrite the parameters of this object to the maximal value it can have. 
     * @param {number} max Maximal value this object can have. 
     */
    limit(max) {
        if (this.mag() > max) {
            this.norm();
            this.mult(max);
        }
    }
    /**
     * Add two Vector2d's together.
     * @param   {Vector2d} v1 
     * @param   {Vector2d} v2 
     * @returns {Vector2d}      Contains a new Vector2d with combined values.
     */
    static add(v1, v2){
        return new Vector2d(v1.getX() + v2.getX(), v1.getY() + v2.getY());
    }
    /**
     * Subtract two Vector2d's from eachother.
     * @param   {Vector2d} v1 
     * @param   {Vector2d} v2 
     * @returns {Vector2d}      Contains a new Vector2d with combined values.
     */
    static sub(v1, v2){
        return new Vector2d(v1.getX() - v2.getX(), v1.getY() - v2.getY());
    }
    /**
     * Multiply a given Vector2d with a value.
     * @param   {Vector2d}  v1 
     * @param   {number}    mult 
     * @returns {Vector2d}          Contains a new Vector2d with Multiplied values.
     */
    static mult(v1, mult){
        return new Vector2d(v1.getX() * mult, v1.getY() * mult);
    }
    /**
     * Divide a given Vector2d with a value.
     * @param   {Vector2d}  v1 
     * @param   {number}    div 
     * @returns {Vector2d}          Contains a new Vector2d with divided values.
     */
    static div(v1, div){
        return new Vector2d(v1.x / div, v1.y / div);
    }
    /*
    Shortcut functions to create a new Vector2d
    */
    static get zero(){
        return new Vector2d(0,0);
    }
    static get one(){
        return new Vector2d(1,1);
    }
    static get up(){
        return new Vector2d(0,1);
    }
    static get down(){
        return new Vector2d(0,-1);
    }
    static get right(){
        return new Vector2d(1,0);
    }
    static get left(){
        return new Vector2d(-1,0);
    }
    /**
     * Returns a Vector2d with random coordinates from 0 to 1.
     * Use a multiplier to increase this.
     * @param   {number}    multiplier  
     * @returns {Vector2d}              Contains a new Vector with random parameters.
     * TODO: Make sure it takes y coordinates in consideration aswell.
     */
    static random(multiplier){
        let vector;
        let random = Math.random();
        if (random <= 0.25) {
            vector = new Vector2d(-Math.random() * multiplier, -Math.random() * multiplier);
        } else if (random > 0.25 && random <= 0.5) {
            vector = new Vector2d(-Math.random() * multiplier, Math.random() * multiplier);
        } else if (random > 0.5 && random <= 0.75) {
            vector = new Vector2d(Math.random() * multiplier, -Math.random() * multiplier);
        } else {
            vector = new Vector2d(Math.random() * multiplier, Math.random() * multiplier);
        }
        return vector;
    }
    static polarToCartasian(length, angleDegree){
        let x = length * cos(angleDegree);
        let y = length * sin(angleDegree);
        return new Vector2d(x,y);
    }
    /**
     * Set the x parameter of the object.
     * @param {number} x Value will be parsed to float before set.
     */
    set x(x){
        this._x = parseFloat(x);
    }
    /**
     * Get the x parameter of the object.  
     * @returns {number} Returns the value of this._x.
     */ 
    get x(){
        return this._x;
    }
    /**
     * Set the y parameter of the object.
     * @param {number} y Value will be parsed to float before set.
     */
    set y(y){
        this._y = parseFloat(y);
    }
    /**
     * Get the y parameter of the object.  
     * @returns {number} Returns the value of this._y.
     */ 
    get y(){
        return this._y;
    }
    static randomFloat(min, max) {
        let plusOrMinus = Math.random() < 0.5 ? -1 : 1;
        return (Math.random() * (max - min) + min) * plusOrMinus; //The maximum is inclusive and the minimum is inclusive 
    }
    get copy(){
        let result = new Vector2d(this.x, this.y);
        return result;
    }
}

/**
 * Sprite texture. 
 * Takes a Spritesheet or a Large image and take controll over which section to show on canvas or its parent.
 */
class Sprite {
    _sLocation = Vector2d.zero;             // Source x & Source y
    _sWidth = 0;                            // Source width
    _sHeight = 0;                           // Source height

    _location = Vector2d.zero;             // Destination x & Destination y
    _width = 0;                            // Destination width
    _height = 0;                           // Destination height
    _offset = Vector2d.zero;

    _visible = true;
    constructor(options, debug = false){
        this.context    = options.context;
        this._texture   = options.texture;

        this.sLocation  = options.sLocation     || Vector2d.zero;
        this.sWidth     = options.sWidth        || 0;
        this.sHeight    = options.sHeight       || 0;

        this.location   = options.location      || Vector2d.zero;
        this.anchor     = options.anchor        || Vector2d.zero;
        this.scale      = options.scale         || Vector2d.one;
        this.offset     = options.offset        || Vector2d.zero;
        this.width      = options.width         || this.sWidth;
        this.height     = options.height        || this.sHeight;
        
        this.rotation   = options.rotation      || 0;
        
        this.developerMode = debug; // TODO: Make sure this does stuff.
    }
    render(){   // TODO: It looks like this is called multiple times when more than 1 object exists. Check this and find out why this happens.
        if(!this.visible)
            return;

        this.draw()
    }
    draw(){ 
        this.context.translate(this.location.x, this.location.y);
        this.context.rotate(this.rotation);

        this.context.drawImage(
            this.texture,               //img	Source image 
            this.sLocation.x,           //sx	Source x	        
            this.sLocation.y,           //sy	Source y	        
            this.sWidth,                //sw	Source width	   
            this.sHeight,               //sh	Source height	   
            -this.offset.x,             //dx	Destination x	 
            -this.offset.y,             //dy	Destination y	   
            this.width,                 //dw	Destination width	
            this.height                 //dh	Destination height	
            );
        
        this.context.rotate(-this.rotation);
        this.context.translate(-this.location.x, -this.location.y);
    }
    /**
     * Readonly the offset from the original 0,0 position.
     */
    get offset(){
        return this._offset;
    }
    set offset(vector2d){ // TODO: Be sure of what this is supposed to do and check if it works as expected. 
        this._offset.x = ((this._width * this._scale.x) * this._anchor.x) + vector2d.x;
        this._offset.y = ((this._height * this._scale.y) * this._anchor.y) + vector2d.y;
    }
    /**
     * texture
     * @param {Image||string} image
     */
    set texture(image){
        if(typeof image === string){
            let result = new Image();
            result.src = image;
            this._texture = result;
            if(this.developerMode){
                Console.warn("New Image created for this.texture. If same source is used repeatedly, consider defining it first as a new Image(src) and use that instead.");
                Console.warn(this);
            }
            return;
        }
        if(typeof image === Image){
            this._texture = image;
            return;
        }
            
    }
    get texture(){
        return this._texture;
    }
    /**
     * Source location.
     * @param {Vector2d} vector2d
     */
    set sLocation(vector2d){
        this._sLocation.x = vector2d.x;
        this._sLocation.y = vector2d.y;
    }
    get sLocation(){
        return this._sLocation;
    }
    /**
     * Source height.
     * @param {number} number
     */
    set sHeight(number){
        this._sHeight = number;
    }
    get sHeight(){
        return this._sHeight;
    }
    /**
     * Source width.
     * @param {number} number
     */
    set sWidth(number){
        this._sWidth = number;
    }
    get sWidth(){
        return this._sWidth;
    }

    /**
     * Destination location.
     * @param {Vector2d} vector2d
     */
    set location(vector2d){
        this._location.x = vector2d.x;
        this._location.y = vector2d.y;
    }
    get location(){
        return this._location;
    }
    /**
     * Destination height.
     * @param {number} number
     */
    set height(number){
        this._height = number*this.scale.y;
    }
    get height(){
        return this._height;
    }
    /**
     * Destination width.
     * @param {number} number
     */
    set width(number){
        this._width = number*this.scale.x;
    }
    get width(){
        return this._width;
    }
    set anchor(vector2d){
        this._anchor = vector2d;
    }
    get anchor(){
        return this._anchor;
    }
    /**
     * Set the rotation of the texture.
     * @param {number} radian
     */
    set rotation(radian){
        this._rotation = radian;
    }
    /**
     * Get the rotation of the texture.
     * @returns {number} 
     */
    get rotation(){
        return this._rotation;
    }
    set scale(vector2d){
        this._scale = vector2d;
    }
    get scale(){
        return this._scale;
    }
    set visible(boolean){
        this._vivible = boolean;
    }
    get visible(){
        if(!this.context){
            console.warn("Context not found. Visibility is set to false by force.");
            console.warn(this);
            return false;
        }
        return this._visible;
    }
}

/**
 * Sprite texture. 
 * creates an animation of a spritesheet. 
 */
class Animated_Sprite extends Sprite{
    _frameIndex = Vector2d.one;                                            // The current frame to be displayed
    _tickCount = 0;                                             // The number updates since the current frame was first displayed

    constructor(options){
        super(options);
        this.numberOfFrames = options.numberOfFrames    || 1;   // The number of frames your spritesheet contains.
        this.numberOfRows   = options.numberOfRows      || 1;   // If your sprite contains more rows select the correct row to animate.
        this.ticksPerFrame  = options.ticksPerFrame     || 1;   // The number updates until the next frame should be displayed. Less than 1 will skip frames.
        this.loop = options.loop || false;                      // The animation will loop or not.
        this.reverse = options.reverse || false;                // Determines if the animation will play in reverse.
    }
    update() {
        this._tickCount += 1;
        while (this._tickCount > this.ticksPerFrame) {
            this._tickCount -= this.ticksPerFrame;

            // calculate next frame.
            if(!this.reverse)
                this.nextFrame();
            else
                this.previousFrame();
        }
        // set source location on Spritesheet.
        this.sLocation.x = this._sWidth * this.frameIndex.x;
        this.sLocation.y = this._sHeight * this.frameIndex.y;
    }
    previousFrame(){
        // If the current frame index is in range
        if (this.frameIndex.x > 0) {	
            // Go to the next frame
            this.frameIndex.x -= 1;
        }
        else if(this._frameIndex.y > 0){
            this.frameIndex.x = this.numberOfFrames-1;
            this.frameIndex.y -= 1;
        }
        else if (this.loop){
            this.frameIndex.x = this.numberOfFrames-1;
            this.frameIndex.y = this.numberOfRows-1;
        }
    }
    nextFrame(){
        // If the current frame index is in range
        if (this.frameIndex.x < this.numberOfFrames - 1) {	
            // Go to the next frame
            this.frameIndex.x += 1;
        }	
        else if(this._frameIndex.y < this.numberOfRows -1){
            this.frameIndex.x = 0;
            this.frameIndex.y += 1;
        }
        else if (this.loop){
            this.frameIndex.x = 0;
            this.frameIndex.y = 0;
        }
    }
    get frameIndex(){
        return this._frameIndex;
    }
    set frameIndex(vector2d){
        if(vector2d.y > this.numberOfRows){
            console.warn(`Frameindex.y is bigger than the total amount of rows that has been set. Changing frameindex.y to ${this.numberOfRows}`);
            vector2d.y = this.numberOfRows;
        }
        if(vector2d.x > this.numberOfFrames){
            console.warn(`Frameindex.x is bigger than the total amount of frames that has been set. Changing frameindex.x to ${this.numberOfFrames}`);
            vector2d.y = this.numberOfFrames;
        }
        this._frameIndex = vector2d;
    }
}

/**
 * Sprite that uses phisycs
 */
class Entity extends Animated_Sprite{
    _original;
    constructor(options){
        super(options);
        this._velocity      = options.velocity      || Vector2d.zero;
        this._acceleration  = options.acceleration  || Vector2d.zero;

        this._aVelocity     = options.aVelocity     || 0;        // Angular velocity.
        this._aAcceleration = options.aAcceleration || 0;    // Angular acceleration.

        this.mass           = options.mass          || 1;

        this.init();
    }
    init(){
        this.original = this.copy;
    }
    update(){
        this.velocity.add(this.acceleration);
        this.location.add(this.velocity);
        this.acceleration.mult(0);

        this.aVelocity += this.aAcceleration;
        this.rotation += this.aVelocity; 
        this.aAcceleration = 0;
        
        super.update();
    }
    /**
     * Add a force to the object that influenses the location of this object.
     * @param {Vector2d} force The force applied to the acceleration of the object.
     */
    applyForce(force){
        let f = Vector2d.div(force, this.mass);
        this.acceleration.add(f);
    }
    applySpin(number){
        this.aAcceleration += number / this.mass;
    }
    get aAcceleration(){
        return this._aAcceleration;
    }
    set aAcceleration(number){
        this._aAcceleration = number;
    }
    get aVelocity(){
        return this._aVelocity;
    }
    set aVelocity(number){
        this._aVelocity = number;
    }
    set acceleration(vector2d){
        this._acceleration = vector2d;
    }
    get acceleration(){
        return this._acceleration;
    }
    set velocity(vector2d){
        this._velocity = vector2d;
    }
    get velocity(){
        return this._velocity;
    }
    get original(){
        return this._original;
    }
    set original(value){
        this._original = value;
    }
    get copy(){
        let result = new Animated_Sprite({
            context:        this.context,                        // The context.
            texture:        this.texture,                       // The image. May be a Image or a string of the source.
            sLocation:      this.sLocation.copy,                     // Top right location of the selected area on the image.
            sWidth:         this.sWidth,                         // The height of the selected area on the image.
            sHeight:        this.sHeight,                        // The Widht of the selected area on the image.
            location:       this.location.copy,                      // The location where to draw the selected area on the canvas.
            anchor:         this.anchor.copy,                        // The center of the selected area. Vector.one = bottom right corner.
            width:          this.width,                         // The width that scales the selected area.
            height:         this.height,                        // The height that scales the selected area.
            scale:          this.scale.copy,                         // The scale of the selected area.
            rotation:       this.rotation,                       // The rotation.

            numberOfFrames: this.numberOfFrames,                 // The amount of frames horizontaly on the spritesheet. Left to right.
	        numberOfRows:   this.numberOfRows,                   // The amount of frames vertically on the spritesheet. Top to bottom.
	        loop:           this.loop,                         // The animation will start over if its finished.
	        reverse:        this.reverse                      // Play the animation in reverse
        });
        return result;
    }
}
index.html
<html>
    <head>
            <script src="Lib_2D_JS_Biirra_Base.js"></script>
    </head>
    <body>
        <canvas id="field" width="1024" height="1024"></canvas>
    </body>
    <script>
        let canvas = document.getElementById("field");
        let ctx = canvas.getContext("2d");

        let earthImage = new Image();
        earthImage.src = "assets/Earth1024x1024_256Frames.png"; // source: https://s3.us-east-2.amazonaws.com/singular-assets/Global/Earth1024x1024_256Frames.png

        let earths = [];

        for(let i = 0; i < 16; i++){
            for(let j = 0; j < 16; j++){
                let earth = new Animated_Sprite({
                    context:ctx,                        // The context.
                    texture:earthImage,                 // The image. May be a Image or a string of the source.
                    sWidth: 64,                         // The height of the selected area on the image.
                    sHeight: 64,                        // The Widht of the selected area on the image.
                    ticksPerFrame: 1,                 // Try to keep it above 0.2 for perfermance reasons.
                    location: new Vector2d(i*64,j*64),    // The location where to draw the selected area on the canvas.
                    anchor: new Vector2d(0,0),      // The center of the selected area. Vector.one = bottom right corner.
                    //scale: new Vector2d(0.5,0.5),

                    numberOfFrames: 16,                 // The amount of frames horizontaly on the spritesheet. Left to right.
                    numberOfRows: 16,                   // The amount of frames vertically on the spritesheet. Top to bottom.
                    loop: true,                         // The animation will start over if its finished.
                    reverse: false                      // Play the animation in reverse
                }, true);
                earth.frameIndex = new Vector2d(0,0);
                earths.push(earth);
            }
        }

        loop();

        function loop (delta) {
            ctx.clearRect(0, 0, 1024, 1024);
            for(let i = 0; i < earths.length; i++){
                earths[i].update();
                earths[i].render(delta);
            }
            window.requestAnimationFrame(loop);
        }        
    </script>
</html>

As far as i can tell the draw and update functions only run once per object every time the loop is called. So why is the performance tab so inconsistent ?