The HTML file:

<html>
<head>

<script id="vertex-shader" type="x-shader/x-vertex" >
precision highp float;

attribute vec4 vPosition;

// a matrix to move the item to where it needs to be
uniform mat4 firstT;
// a matrix to move the world
uniform mat4 midT;

void main() {
    gl_Position = midT * firstT *  vPosition;
}

</script>

<script id="fragment-shader" type="x-shader/x-fragment">
precision highp float;

void main(){
   gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
</script>

<script type="text/javascript" src="../Common/webgl-utils.js"></script>
<script type="text/javascript" src="../Common/initShaders.js"></script>
<script type="text/javascript" src="../Common/MV.js"></script>
<script type="text/javascript" src="https://mirkwood.cs.edinboro.edu/~bennett/f360-19/Models/bunny.js"></script>
<script type="text/javascript" src="https://mirkwood.cs.edinboro.edu/~bennett/f360-19/Models/lizard.js"></script>
<script type="text/javascript" src="https://mirkwood.cs.edinboro.edu/~bennett/f360-19/Models/dragon.js"></script>
<script type="text/javascript" src="https://mirkwood.cs.edinboro.edu/~bennett/f360-19/Models/teapot.js"></script>

<script type="text/javascript" src="Widget.js"></script>
<script type="text/javascript" src="GLCanvas.js"></script>
<script type="text/javascript" src="Program.js" defer></script>
</head>
<body>
<script>
    var canvas = new Canvas(500, 500);
</script>
</body>

GLCanvas.js

'use strict'

class Canvas {
    constructor (width, height) {
        this.height = height;
        this.width = width;

        this.MakeCanvas();

        this.SetupGL();
        this.MakeShaders();

        this.Init();
    }


    // I don't want to expose the undrlying canvas.
    AddKeypress(Keypress) {
        this.canvas.addEventListener("keypress", Keypress);
    }

    MakeCanvas() {
        if (this.width == undefined || this.width < 0) {
           this.width = 300;
        }

        if (this.height == undefined || this.height < 0) {
           this.height = 300;
        }

         this.canvas = document.createElement('canvas')
	 this.canvas.tabIndex=0;
         this.canvas.height = this.height;
         this.canvas.width = this.width;
         this.canvas.style="border:1px solid #000000;"

         document.body.appendChild(this.canvas);
    }

    SetupGL() {
        this.gl = WebGLUtils.setupWebGL(this.canvas);
        if (!this.gl) {
            alert ("WebGL isn't available");
	    return;
        }
	this.gl.getExtension('OES_standard_derivatives');
    }

    MakeShaders() {
        this.program = initShaders(this.gl, "vertex-shader","fragment-shader");
        this.gl.useProgram(this.program);
    }

    Init() {
        this.gl.clearColor(1.0, 1.0, 1.0, 1.0);
        this.gl.viewport(0,0, this.width, this.height);
    }

    Program() {
       return this.program;
    }

    GL() {
       return this.gl;
    }

    Clear() {
        this.gl.clear(this.gl.COLOR_BUFFER_BIT);
    }
}

Program.js

'use strict'

let rx = 0;
let ry = 0;
let rz = 0;
let Objects = []

function MakeItems() {
    let cx = canvas.width/2;
    let cy = canvas.height/2;

    // Where, behind the bunny?
    let obj = new Widget(canvas.GL(), canvas.Program(), 
                           "vPosition","firstT", Bunny_Triangles );
    obj.Viewport(0,0, cx, cy);
    Objects.push(obj);

    // there goes Tokyo 
    obj = new Widget(canvas.GL(), canvas.Program(), 
                           "vPosition", "firstT",Lizard_Triangles );
    obj.Viewport(cx,0, cx,cy);
    Objects.push(obj);

     //dragon
    obj = new Widget(canvas.GL(), canvas.Program(), 
                           "vPosition", "firstT",Dragon_Triangles );
    obj.Viewport(cx,cy, cx,cy);
    Objects.push(obj);

    // tea
    obj = new Widget(canvas.GL(), canvas.Program(), 
                           "vPosition", "firstT",Teapot_Triangles );
    obj.Viewport(0,cy, cx,cy);
    
    Objects.push(obj);
}

// probably wrong but everyone needs to rotate by the same amount.
function SetUPMidTrans() {
    let loc = canvas.GL().getUniformLocation(canvas.Program(), "midT");

    let trans = mat4(1);
    trans = mult(trans, rotate(rx, [1,0,0]));
    trans = mult(trans, rotate(ry, [0,1,0]));
    trans = mult(trans, rotate(rz, [0,0,1]));
    canvas.GL().uniformMatrix4fv(loc, false, flatten(trans))
}

function SetUP() {
    MakeItems();

    canvas.AddKeypress(Keypress);
    Reset();
}

function Reset() {
    rx = 0;
    ry = 0;
    rz = 0;

    SetUPMidTrans();
}

function Keypress(evnt) {
    if (evnt.key >= '0' && evnt.key <= '9') {
        ToggleItemVis(evnt.key-'0');
    } else {
       switch(evnt.key) {
          case 'x': ++rx; break;
          case 'X': --rx; break;
          case 'y': ++ry; break;
          case 'Y': --ry; break;
          case 'z': ++rz; break;
          case 'Z': --rz; break;
          case 'r': Reset(); break;
       }
       SetUPMidTrans();
    }

    Display();
}

function ToggleItemVis(id) {
    if (id < Objects.length) {
         if (Objects[id].Visible() ) {
	     Objects[id].Hide();
	 } else {
	     Objects[id].Show();
	 }
    }
}

function DisplayItem(item) {
    item.Display(canvas.GL());
}

function Display() {
    canvas.Clear();
    Objects.forEach(DisplayItem);
}

SetUP()
Display();

Widget.js

'use strict'

class Widget {
    
    constructor(gl, program, posName, ltName, edges) {
        this.visible = true;
	this.size = edges.length;
	this.SetupVBO(gl, edges);

        this.FindMove(edges);

	this.vpos =  gl.getAttribLocation(program, posName);
	this.localTransform = gl.getUniformLocation(program, ltName);

        this.Viewport(0,0,canvas.width, canvas.height);
    }


    // I want the thing centered on 0,0 and scaled nicely to fit in the 
    // view cube.
    FindMove(edges) {
        // ... is a flatten operation
        // Copy the first vertex into the lower left or upper right.
        let lowerLeft = [...edges[0]]  
        let upperRight = [...edges[0]];

        // find the corners of the bounding box.
        for(let i = 1; i< edges.length; ++i) {
            for(let j=0; j < 3; ++j) {
                lowerLeft[j] = Math.min(lowerLeft[j], edges[i][j]);
                upperRight[j] = Math.max(upperRight[j], edges[i][j]);
            }
        }

        // find the dimensions of the bounding box.
        // each coordinate will be the size of the box in that dimension
        let diff = subtract(upperRight, lowerLeft);


        // start with the identity matrix.
        this.transform = mat4(1)

        // find 1/2 the length of the dimensions of the bounding box.
        let diff2 = mult(diff, [1/2, 1/2, 1/2]);

        // this is the center of the object
        let center = add(lowerLeft,diff2);
        // so this is how much you need to move the center by to get it to 0,0,0
        center = mult(center, [-1,-1,-1]);

        // max is the biggest dimension, in case the bounding box is not square
        let max = Math.max(...diff);
        // this is a guess so it looks nice.  1.7 fits iside a 2x2x2 box.
        let scale = 1.7/max;

        //Then scale it.
        this.transform = mult(this.transform, scalem(scale,scale,scale));
        // translate the center to 0,0,0
        this.transform = mult(this.transform, translate(center))
    }

    SetupVBO(gl, edges) {
        this.vbuf =  gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, this.vbuf);
	    gl.bufferData(gl.ARRAY_BUFFER,flatten(edges),gl.STATIC_DRAW);
    }

    Show() {
        this.visible = true;
    }

    Hide() {
        this.visible = false;
    }

    Visible() {
        return this.visible;
    }

    Viewport(x,y,w,h) {
         this.vx = x;
         this.vy = y;
         this.vw = w;
         this.vh = h;
    }

    Display(gl) {
          if (this.visible) {

              // send over the first transformation matrix
              gl.uniformMatrix4fv(this.localTransform, false,
                                                flatten(this.transform));

              gl.viewport(this.vx, this.vy, this.vw, this.vh);

              gl.bindBuffer(gl.ARRAY_BUFFER, this.vbuf);

              gl.vertexAttribPointer(this.vpos, 3, gl.FLOAT, false, 0, 0);
              gl.enableVertexAttribArray(this.vpos);

              for(let i =0; i < this.size;++i) {
                  gl.drawArrays(gl.LINE_LOOP, 3*i, 3);
              }
         }
    }
}