function compileProgram(gl, vertex, fragment, attributes) {
	const vertShader = compileShader(gl, gl.VERTEX_SHADER, vertex);
	const fragShader = compileShader(gl, gl.FRAGMENT_SHADER, fragment);

	let program = gl.createProgram();

	gl.attachShader(program, vertShader);
	gl.attachShader(program, fragShader);

	// force vertex attributes to use location 0 as starting location to prevent
	// browser to do complicated emulation when running on desktop OpenGL (e.g. on macOS)
	for (const location in attributes) {
		gl.bindAttribLocation(program, attributes[location], location);
	}

	gl.linkProgram(program);

	if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
		const error_msg =
			"Error initializing Shader " +
			this +
			"\n" +
			"gl.VALIDATE_STATUS: " +
			gl.getProgramParameter(program, gl.VALIDATE_STATUS) +
			"\n" +
			"gl.getError()" +
			gl.getError() +
			"\n" +
			"gl.getProgramInfoLog()" +
			gl.getProgramInfoLog(program);
		// house cleaning
		gl.deleteProgram(program);
		program = null;
		// throw the exception
		throw new Error(error_msg);
	}

	gl.useProgram(program);

	// clean-up
	gl.deleteShader(vertShader);
	gl.deleteShader(fragShader);

	return program;
}
Compile GLSL into a shader object
@ignore