JSFiddle - React, Tailwind, and code Playground

JavaScript

function Matrix() {
    this.m = Array.prototype.concat.apply([], arguments);
}

function frustum(l, r, b, t, n, f) {
    var m = new Matrix(
	    2 * n / (r - l),
	    0,
	    (r + l) / (r - l),
	    0,

	    0,
	    2 * n / (t - b),
	    (t + b) / (t - b),
	    0,

	    0,
	    0,
	    -(f + n) / (f - n),
	    -2 * f * n / (f - n),

	    0,
	    0,
	    -1,
	    0
    );
    return m;
}

function perspective(fovy, aspect, near, far) {
    var y = Math.tan(fovy * Math.PI / 360) * near;
    var x = y * aspect;
    return frustum(-x, x, -y, y, near, far);
}

//from NDC to Screen space
function viewport(w, h) {
	
	var depthRangeMin = 0;
	var depthRangeMax = 1;
	var depthRange = depthRangeMax - depthRangeMin;
	
	var m = new Matrix(
	    w / 2, // scaling
	    0,
	    0,
	    0, // translation

	    0,
	    h / 2, // scaling
	    0,
	    0, // translation

	    0,
	    0,
	                    depthRange / 2, // depthRange scaling to utilize depth buffer.
	    depthRangeMin + depthRange / 2, // translation

	    0,
	    0,
	    0,
	    1
    );
    return m;
}



function Vector3D(x, y, z, w) {
    this.x = x || 0;
    this.y = y || 0;
    this.z = z || 0;
    this.w = w || 1;
}

Vector3D.prototype.transform = function(matrix) {
	var vector = new Vector3D(
		matrix.m[0] *this.x + matrix.m[1] *this.y + matrix.m[2] *this.z + matrix.m[3] *this.w,
		matrix.m[4] *this.x + matrix.m[5] *this.y + matrix.m[6] *this.z + matrix.m[7] *this.w,
		matrix.m[8] *this.x + matrix.m[9] *this.y + matrix.m[10]*this.z + matrix.m[11]*this.w,
		matrix.m[12]*this.x + matrix.m[13]*this.y + matrix.m[14]*this.z + matrix.m[15]*this.w
	);
	return vector;
};

//"perspective divide" to create 3D effect
Vector3D.prototype.normalize = function() {
	var vector = new Vector3D(this.x / this.w,
							  this.y / this.w,
							  this.z / this.w,
							  1);
	return vector;
};

Vector3D.prototype.toString = function() {
	return "x=" + this.x.toFixed(4) + ", y=" + this.y.toFixed(4) + ", z=" +...