Index: /issm/trunk-jpl/src/m/plot/plot_mesh.js
===================================================================
--- /issm/trunk-jpl/src/m/plot/plot_mesh.js	(revision 21093)
+++ /issm/trunk-jpl/src/m/plot/plot_mesh.js	(revision 21094)
@@ -63,5 +63,5 @@
 	scale = 1 / (xmax - xmin);
 	node["shaderName"] = "colored";
-	node["shader"] = gl["shaders"][node["shaderName"]]["program"];
+	node["shader"] = gl["shaders"][node["shaderName"]];
 	node["lineWidth"] = options.getfieldvalue('linewidth',1);
 	node["scale"] = [scale, scale, scale * matrixscale];
@@ -73,7 +73,4 @@
 	node["maskHeight"] = options.getfieldvalue('innermaskheight',150.0)*options.getfieldvalue('heightscale',1);
 	node["maskColor"] = options.getfieldvalue('innermaskcolor',[0.0,0.0,1.0,1.0]);
-				
-	//some defaults:
-	colors.itemSize = 4;
 
 	//retrieve some options
@@ -88,5 +85,4 @@
 	}
 	else{ //2D triangular elements
-		vertices.itemSize = 3;
 		var xyz = vec3.create();
 		var direction = vec3.create();
@@ -135,10 +131,6 @@
 			indices[indices.length] = element[0];
 		}
-		indices.itemSize = 1;
 	}
 	//}}}
-
-	/*Initalize buffers: */
-	node["arrays"] = [vertices, colors, indices];
-	node["buffers"] = initBuffers(gl, node["arrays"]);
+	node["mesh"] = GL.Mesh.load({vertices:vertices, colors:colors, indices:indices});
 }
Index: /issm/trunk-jpl/src/m/plot/plot_overlay.js
===================================================================
--- /issm/trunk-jpl/src/m/plot/plot_overlay.js	(revision 21093)
+++ /issm/trunk-jpl/src/m/plot/plot_overlay.js	(revision 21094)
@@ -63,5 +63,5 @@
 	scale = 1 / (xmax - xmin);
 	node["shaderName"] = "unlit_textured";
-	node["shader"] = gl["shaders"][node["shaderName"]]["program"];
+	node["shader"] = gl["shaders"][node["shaderName"]];
 	node["scale"] = [scale, scale, scale * matrixscale];
 	node["translation"] = [(xmin + xmax) / (-2 / scale), (ymin + ymax) / (-2 / scale), (zmin + zmax) / (2 / scale)];
@@ -73,8 +73,4 @@
 	node["maskHeight"] = options.getfieldvalue('outermaskheight',150.0);
 	node["maskColor"] = options.getfieldvalue('outermaskcolor',[0.0,0.0,1.0,1.0]);
-				
-	//some defaults:
-	texcoords.itemSize = 2;
-	vertices.itemSize = 3;
 	
 	//Handle outer radaroverlay
@@ -152,5 +148,4 @@
 		}
 	}
-
 	//linearize the elements array:
 	var element;
@@ -162,8 +157,4 @@
 		indices[indices.length] = element[2];
 	}
-	indices.itemSize = 1;
-	
-	/*Initalize buffers: */
-	node["arrays"] = [vertices, texcoords, indices];
-	node["buffers"] = initBuffers(gl,node["arrays"]);	
+	node["mesh"] = GL.Mesh.load({vertices:vertices, coords:texcoords, indices:indices});
 }
Index: /issm/trunk-jpl/src/m/plot/plot_quiver.js
===================================================================
--- /issm/trunk-jpl/src/m/plot/plot_quiver.js	(revision 21093)
+++ /issm/trunk-jpl/src/m/plot/plot_quiver.js	(revision 21094)
@@ -11,5 +11,4 @@
 	var indices = [];
 	var colors = [];
-	var rgbcolor = [];
 	var xmin,xmax;
 	var ymin,ymax;
@@ -66,5 +65,5 @@
 	scale = 1 / (xmax - xmin);
 	node["shaderName"] = "colored";
-	node["shader"] = gl["shaders"][node["shaderName"]]["program"];
+	node["shader"] = gl["shaders"][node["shaderName"]];
 	node["lineWidth"] = options.getfieldvalue('linewidth',1);
 	node["scale"] = [scale, scale, scale * matrixscale];
@@ -77,7 +76,4 @@
 	node["maskHeight"] = options.getfieldvalue('innermaskheight',150.0)*options.getfieldvalue('heightscale',1);
 	node["maskColor"] = options.getfieldvalue('innermaskcolor',[0.0,0.0,1.0,1.0]);
-				
-	//some defaults:
-	colors.itemSize = 4;
 
 	//retrieve some options
@@ -92,5 +88,4 @@
 	}
 	else{ //2D triangular elements
-		vertices.itemSize = 3;
 		var xyz = vec3.create();
 		var xyz = vec3.create();
@@ -137,7 +132,4 @@
 	}
 	//}}}
-
-	/*Initalize buffers: */
-	node["arrays"] = [vertices, colors];
-	node["buffers"] = initBuffers(gl, node["arrays"]);
+	node["mesh"] = GL.Mesh.load({vertices:vertices, colors:colors});
 }
Index: /issm/trunk-jpl/src/m/plot/plot_unit.js
===================================================================
--- /issm/trunk-jpl/src/m/plot/plot_unit.js	(revision 21093)
+++ /issm/trunk-jpl/src/m/plot/plot_unit.js	(revision 21094)
@@ -8,14 +8,4 @@
 
 	//declare variables:  {{{
-	var vertices = [];
-	var indices = [];
-	var texcoords = [];
-	var nanindices = {};
-	var xmin,xmax;
-	var ymin,ymax;
-	var zmin,zmax;
-	var datamin,datamax,datadelta;
-	var scale,matrixscale,vertexscale;
-
 	//Process data and model
 	var meshresults = processmesh(md,data,options);
@@ -26,4 +16,14 @@
 	var is2d = meshresults[4]; 
 	var isplanet = meshresults[5];
+	
+	var vertices = new Float32Array(x.length * 3);
+	var texcoords = new Float32Array(x.length * 2);
+	var indices = new Uint16Array(elements.length * 3);
+	var nanindices = {};
+	var xmin,xmax;
+	var ymin,ymax;
+	var zmin,zmax;
+	var datamin,datamax,datadelta;
+	var scale,matrixscale,vertexscale;
 
 	//Compue scaling through matrices for 2d meshes and vertices for 3d meshes
@@ -65,5 +65,5 @@
 	scale = 1 / (xmax - xmin);
 	node["shaderName"] = "unlit_textured";
-	node["shader"] = gl["shaders"][node["shaderName"]]["program"];
+	node["shader"] = gl["shaders"][node["shaderName"]];
 	node["scale"] = [scale, scale, scale * matrixscale];
 	node["translation"] = [(xmin + xmax) / (-2 / scale), (ymin + ymax) / (-2 / scale), (zmin + zmax) / (2 / scale)];
@@ -101,7 +101,4 @@
 				datadelta = datamax - datamin;
 
-				vertices.itemSize = 3;
-				texcoords.itemSize = 2;
-				
 				var xyz = vec3.create();
 				var direction = vec3.create();
@@ -109,14 +106,14 @@
 				var magnitude;
 
-				for(var i = 0; i < x.length; i++){
+				for(var i = 0, vindex = 0, tindex = 0; i < x.length; i++){
 					//Check for NaN values and remove from indices array as necessary, but preserve vertex array spacing
 					if (isNaN(x[i]) || isNaN(y[i]) || isNaN(z[i]) || isNaN(data[i])) {
 						nanindices[i] = i;
-						vertices[vertices.length] = vertex[0];
-						vertices[vertices.length] = vertex[1];
-						vertices[vertices.length] = vertex[2];
+						vertices[vindex++] = vertex[0];
+						vertices[vindex++] = vertex[1];
+						vertices[vindex++] = vertex[2];
 						
-						texcoords[texcoords.length] = 0.0;
-						texcoords[texcoords.length] = 0.0;
+						texcoords[tindex++] = 0.0;
+						texcoords[tindex++] = 0.0;
 						continue;
 					}
@@ -126,28 +123,23 @@
 					vec3.normalize(direction, xyz);
 					vec3.scale(vertex, direction, magnitude);
-					vertices[vertices.length] = vertex[0];
-					vertices[vertices.length] = vertex[1];
-					vertices[vertices.length] = vertex[2];
-
-					texcoords[texcoords.length] = 0.5;
-					texcoords[texcoords.length] = (data[i] - datamin) / datadelta;
+					vertices[vindex++] = vertex[0];
+					vertices[vindex++] = vertex[1];
+					vertices[vindex++] = vertex[2];
+
+					texcoords[tindex++] = 0.5;
+					texcoords[tindex++] = (data[i] - datamin) / datadelta;
 				}
 
 				//linearize the elements array: 
 				var element;
-				for(var i = 0; i < elements.length; i++){
+				for(var i = 0, iindex = 0; i < elements.length; i++){
 					element = [elements[i][0] - 1, elements[i][1] - 1, elements[i][2] - 1];
 					if (element[0] in nanindices || element[1] in nanindices || element[2] in nanindices) continue;
-					indices[indices.length] = element[0];
-					indices[indices.length] = element[1];
-					indices[indices.length] = element[2];
-				}
-				indices.itemSize = 1;
-
-			}
-		
-			//Initalize buffers
-			node["arrays"] = [vertices, texcoords, indices];
-			node["buffers"] = initBuffers(gl,node["arrays"]);
+					indices[iindex++] = element[0];
+					indices[iindex++] = element[1];
+					indices[iindex++] = element[2];
+				}
+			}
+			node["mesh"] = GL.Mesh.load({vertices:vertices, coords:texcoords, indices:indices});
 			break;
 		//}}}
@@ -169,6 +161,4 @@
 			}
 			else{ //triangular elements
-				vertices.itemSize = 3;
-				
 				var xyz = vec3.create();
 				var direction = vec3.create();
@@ -176,24 +166,19 @@
 				var magnitude;
 				var timestamps = data[data.length-1];
-				for(var i = 0; i < x.length; i++){
+				for(var i = 0, vindex = 0, tindex = 0; i < x.length; i++){
 					//Check for NaN values and remove from indices array as necessary, but preserve vertex array spacing
 					if (isNaN(x[i]) || isNaN(y[i]) || isNaN(z[i]) || isNaN(data[i][0])) {
 						nanindices[i] = i;
-						vertices[vertices.length] = vertex[0];
-						vertices[vertices.length] = vertex[1];
-						vertices[vertices.length] = vertex[2];
-						
-						texcoords[texcoords.length] = 0.0;
-						texcoords[texcoords.length] = 0.0;
-						continue;
-					}
-					//Scale vertices
-					xyz = vec3.fromValues(x[i], y[i], z[i]);
-					magnitude = vec3.length(xyz) + md.geometry.surface[i] * vertexscale;
-					vec3.normalize(direction, xyz);
-					vec3.scale(vertex, direction, magnitude);
-					vertices[vertices.length] = vertex[0];
-					vertices[vertices.length] = vertex[1];
-					vertices[vertices.length] = vertex[2];
+					}
+					else {
+						//Scale vertices
+						xyz = vec3.fromValues(x[i], y[i], z[i]);
+						magnitude = vec3.length(xyz) + md.geometry.surface[i] * vertexscale;
+						vec3.normalize(direction, xyz);
+						vec3.scale(vertex, direction, magnitude);
+					}
+					vertices[vindex++] = vertex[0];
+					vertices[vindex++] = vertex[1];
+					vertices[vindex++] = vertex[2];
 				}	
 				//Transpose data to obtain column addressable data matrix
@@ -204,23 +189,18 @@
 				});
 				//Prevent evaluation of datasubarray min/max if caxis exists
-				if (options.exist('caxis')) {
-					caxis = options.getfieldvalue('caxis');
-				}
-				else {
-					caxis = [ArrayMin(data[0]),ArrayMax(data[0].slice(0,-1))];
-				}
+				if (options.exist('caxis')) caxis = options.getfieldvalue('caxis');
+				else caxis = [ArrayMin(data[0]),ArrayMax(data[0].slice(0,-1))];
 				if (options.getfieldvalue('log','off')!='off') caxis = [Math.log10(caxis[0])/Math.log10(options.getfieldvalue('log',10)),Math.log10(caxis[1])/Math.log10(options.getfieldvalue('log',10))];
-				
+				//Prepare texcoords to hold array of data values
+				texcoords = [];
 				for(var i = 0; i < data.length; i++){					
 					datamin = caxis[0];
 					datamax = caxis[1];
 					datadelta = datamax - datamin;
-
 					//Precalculate arrays for each datasubarray, trimming off timestamp value by using x.length instead of data[i].length
-					texcoords[i] = [];
-					texcoords[i].itemSize = 2;
-					for(var j = 0; j < x.length; j++){
-						texcoords[i][texcoords[i].length] = 0.5;
-						texcoords[i][texcoords[i].length] = (data[i][j] - datamin) / datadelta;
+					texcoords[i] = new Float32Array(x.length * 2);
+					for(var j = 0, index = 0; j < x.length; j++){
+						texcoords[i][index++] = 0.5;
+						texcoords[i][index++] = (data[i][j] - datamin) / datadelta;
 					}
 				}
@@ -228,12 +208,11 @@
 				//linearize the elements array:
 				var element;
-				for(var i = 0; i < elements.length; i++){
+				for(var i = 0, iindex = 0; i < elements.length; i++){
 					element = [elements[i][0] - 1, elements[i][1] - 1, elements[i][2] - 1];
 					if (element[0] in nanindices || element[1] in nanindices || element[2] in nanindices) continue;
-					indices[indices.length] = element[0];
-					indices[indices.length] = element[1];
-					indices[indices.length] = element[2];
-				}
-				indices.itemSize = 1;
+					indices[iindex++] = element[0];
+					indices[iindex++] = element[1];
+					indices[iindex++] = element[2];
+				}
 			
 				//Initialize movie loop
@@ -242,25 +221,20 @@
 				node["movieLength"] = timestamps.length;
 				node["movieFrame"] = 0;
-
 				if (canvas["movieHandler"])	clearInterval(canvas["movieHandler"]);
 				canvas["movieHandler"] = setInterval(function () {
 						node["movieFrame"] = canvas["movieFrame"];
 						if (canvas["moviePlay"] && canvas["movieIncrement"]) {
-							if (canvas["movieReverse"]) {
-								node["movieFrame"] = (((node["movieFrame"] - 1) % node["movieLength"]) + node["movieLength"]) % node["movieLength"]; //Handle negative modulus
-							}
-							else {
-								node["movieFrame"] = (((node["movieFrame"] + 1) % node["movieLength"]) + node["movieLength"]) % node["movieLength"]; //Handle negative modulus
-							}
+							if (canvas["movieReverse"]) node["movieFrame"] = (((node["movieFrame"] - 1) % node["movieLength"]) + node["movieLength"]) % node["movieLength"]; //Handle negative modulus
+							else node["movieFrame"] = (((node["movieFrame"] + 1) % node["movieLength"]) + node["movieLength"]) % node["movieLength"]; //Handle negative modulus
 						}
-						if (canvas["timeLabel"]) {
-							canvas["timeLabel"].html(node["movieTimestamps"][node["movieFrame"]].toFixed(0) + " " + options.getfieldvalue("movietimeunit","yr"));
-						}
+						if (canvas["timeLabel"]) canvas["timeLabel"].html(node["movieTimestamps"][node["movieFrame"]].toFixed(0) + " " + options.getfieldvalue("movietimeunit","yr"));
 						if (canvas["progressBar"]) {
 							canvas["progressBar"].val(node["movieFrame"]);
 							canvas["progressBar"].slider('refresh');
 						}
-						node["buffers"] = node["buffersArray"][node["movieFrame"]];
 						canvas["movieFrame"] = node["movieFrame"];
+						var buffer = node["mesh"].getBuffer("coords");
+						buffer.data = texcoords[node["movieFrame"]];
+						buffer.upload(gl.DYNAMIC_DRAW);
 					}, node["movieInterval"]);
 				if (canvas["progressBar"]) {
@@ -271,12 +245,5 @@
 				}
 			}
-			
-			//Initialize buffers
-			node["arrays"] = [vertices, texcoords, indices];
-			node["buffersArray"] = [];
-			for(var i = 0; i < timestamps.length; i++){
-				node["buffersArray"][i] = initBuffers(gl,[node["arrays"][0],node["arrays"][1][i],node["arrays"][2]]);
-			}
-			node["buffers"] = node["buffersArray"][0];
+			node["mesh"] = GL.Mesh.load({vertices:vertices, coords:texcoords[0], indices:indices});
 			break;
 		//}}}
Index: /issm/trunk-jpl/src/m/plot/webgl.js
===================================================================
--- /issm/trunk-jpl/src/m/plot/webgl.js	(revision 21093)
+++ /issm/trunk-jpl/src/m/plot/webgl.js	(revision 21094)
@@ -15,16 +15,13 @@
 function initWebGL(canvas,options) { //{{{
 	var gl;
-
 	try {
-		// Try to grab the standard context. If it fails, fallback to experimental.
-		gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
-	}
-	catch(e) {}
-
-	// If we don't have a GL context, give up now
-	if (!gl) {
-		alert("Unable to initialize WebGL. Your browser may not support it.");
-	}
-		
+		if (!canvas.gl) gl = GL.create({canvas:canvas});
+		else gl = canvas.gl;
+	}
+	catch(e) {
+		console.log(e);
+		return;
+	}
+
 	// Enable depth testing
 	gl.enable(gl.DEPTH_TEST);
@@ -33,8 +30,4 @@
 	// Enable color blending/overlay
 	gl.enable(gl.BLEND);
-
-	// Allocate arrays equal to maximium number of attributes used by any one shader
-	gl.enableVertexAttribArray(0);
-	gl.enableVertexAttribArray(1);
 
 	// Load shaders and store them in gl object
@@ -80,50 +73,11 @@
 	return gl;
 } //}}}
-function initBuffers(gl,arrays) { //{{{	
-	var bufferArray = [];
-	for (var i = 0; i < arrays.length; i++) {
-		bufferArray[i] = gl.createBuffer();	
-		bufferArray[i].itemSize = arrays[i].itemSize;
-		bufferArray[i].numItems = arrays[i].length/bufferArray[i].itemSize;
-		
-		if (bufferArray[i].itemSize > 1) {
-			gl.bindBuffer(gl.ARRAY_BUFFER, bufferArray[i]);
-			gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(arrays[i]), gl.STATIC_DRAW);
-		}
-		else {
-			//TODO: identify index buffers uniquely (by name)
-			gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, bufferArray[i]);
-			gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(arrays[i]), gl.STATIC_DRAW);
-		}
-	}	
-	return bufferArray;
-} //}}}
 function initTexture(gl,imageSource) { //{{{
-	var texture = gl.createTexture();
-	texture.image = new Image();
-	texture.isLoaded = false;
-	texture.image.onload = function () {
-		handleLoadedTexture(gl,texture);
-	}
-	texture.image.src = imageSource;
-	return texture;
-} //}}}
-function handleLoadedTexture(gl,texture) { //{{{
-	gl.activeTexture(gl.TEXTURE0);
-	gl.bindTexture(gl.TEXTURE_2D, texture);
-	gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
-	gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image);
-	gl.generateMipmap(gl.TEXTURE_2D);
-	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_LINEAR);
-	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
-	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
-	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
-	gl.bindTexture(gl.TEXTURE_2D, null);
-	texture.isLoaded = true;
+	return GL.Texture.fromURL(imageSource, {minFilter:gl.LINEAR_MIPMAP_LINEAR, magFilter:gl.LINEAR});
 } //}}}
 function Node(gl,options) { //{{{
 	//Returns a Node object that contains default display states for webgl object
 	return {buffers:[],
-		shader:gl.shaders["colored"]["program"],
+		shader:gl.shaders["colored"],
 		draw:null,
 		hideOcean:false,
@@ -195,135 +149,87 @@
 	shaders = {};
 	shaders["colored"] = {loaded:false, vsh:{}, fsh:{}};
-	shaders["colored"]["vsh"]["string"] = 
-		['attribute vec3 aVertexPosition;',
-		'attribute vec4 aVertexColor;',
-		'',
-		'uniform mat4 uMVPMatrix;',
-		'uniform float uAlpha;',
-		'',
-		'varying vec4 vColor;',
-		'',
-		'void main(void) {',
-		'	gl_PointSize = 3.0;',
-		'	gl_Position = uMVPMatrix * vec4(aVertexPosition.xyz, 1.0);',
-		'	vColor = vec4(aVertexColor.xyz, uAlpha);',
-		'}'].join('\n');
-	shaders["colored"]["fsh"]["string"] =
-		['precision mediump float;',
-		'',
-		'varying vec4 vColor;',
-		'',
-		'void main(void) {',
-		'	gl_FragColor = vColor;',
-		'}'].join('\n');
-	shaders["unlit_textured"] = {loaded:false, vsh:{}, fsh:{}};
-	shaders["unlit_textured"]["vsh"]["string"] = 
-		['attribute vec3 aVertexPosition;',
-		'attribute vec2 aTextureCoord;',
-		'',
-		'uniform mat4 uMVPMatrix;',
-		'',
-		'varying vec2 vTextureCoord;',
-		'varying float vZCoord;',
-		'',
-		'void main(void) {',
-		'	gl_PointSize = 3.0;',
-		'	gl_Position = uMVPMatrix * vec4(aVertexPosition.xyz, 1.0);',
-		'	vTextureCoord = aTextureCoord;',
-		'	vZCoord = aVertexPosition.z;',
-		'}'].join('\n');
-	shaders["unlit_textured"]["fsh"]["string"] =
-		['precision mediump float;',
-		'',
-		'varying vec2 vTextureCoord;',
-		'varying float vZCoord;',
-		'',
-		'uniform sampler2D uColorSampler;',
-		'uniform float uAlpha;',
-		'uniform bool uMaskEnabled;',
-		'uniform float uMaskHeight;',
-		'uniform vec4 uMaskColor;',
-		'',
-		'void main(void) {',
-		'	if (uMaskEnabled && (vZCoord < uMaskHeight)) {',
-		'		gl_FragColor = vec4(uMaskColor.xyz, uAlpha);',
-		'	}',
-		'	else {',
-		'		gl_FragColor = vec4(texture2D(uColorSampler, vec2(vTextureCoord.s, vTextureCoord.t)).rgb, uAlpha);',
-		'	}',
-		'}'].join('\n');
-	shaderNames.forEach(function(shaderName){
-		shaders[shaderName]["vsh"]["shader"] = getShaderByString(gl, shaders[shaderName]["vsh"]["string"], "vsh");
-		shaders[shaderName]["fsh"]["shader"] = getShaderByString(gl, shaders[shaderName]["fsh"]["string"], "fsh");
-
-		shaders[shaderName]["program"] = gl.createProgram();
-		gl.attachShader(shaders[shaderName]["program"], shaders[shaderName]["vsh"]["shader"]);
-		gl.attachShader(shaders[shaderName]["program"], shaders[shaderName]["fsh"]["shader"]);
-		gl.linkProgram(shaders[shaderName]["program"]);
-
-		if (!gl.getProgramParameter(shaders[shaderName]["program"], gl.LINK_STATUS)) {
-			alert("Could not initialise shaders");
-		}
-
-		var vshStringArray = shaders[shaderName]["vsh"]["string"].split("\n");
-		var fshStringArray = shaders[shaderName]["fsh"]["string"].split("\n");
-		var line = "";
-		var property = "";
-		for (var i = 0; i < vshStringArray.length; i++) {
-			line = vshStringArray[i];
-			if (line.search("attribute") != -1) {
-				property = nameFromLine(line);
-				shaders[shaderName]["program"][property] = gl.getAttribLocation(shaders[shaderName]["program"], property);
-			}
-			else if (line.search("uniform") != -1) {
-				property = nameFromLine(line);
-				shaders[shaderName]["program"][property] = gl.getUniformLocation(shaders[shaderName]["program"], property);
-			}
-			else if (line.search("void main") != -1) {
-				break;
-			}
-		}
-		for (var i = 0; i < fshStringArray.length; i++) {
-			line = fshStringArray[i];
-			if (line.search("uniform") != -1) {
-				property = nameFromLine(line);
-				shaders[shaderName]["program"][property] = gl.getUniformLocation(shaders[shaderName]["program"], property);
-			}
-			else if (line.search("void main") != -1) {
-				break;
-			}
-		}
-		shaders[shaderName]["loaded"] = true;
-	});
+			//basic phong shader
+	shaders["colored"] = new Shader('\
+		precision highp float;\
+		attribute vec3 a_vertex;\
+		attribute vec4 a_color;\
+		uniform mat4 u_mvp;\
+		uniform float u_alpha;\
+		varying vec4 v_color;\
+		void main() {\
+			gl_PointSize = 3.0;\
+			gl_Position = u_mvp * vec4(a_vertex.xyz, 1.0);\
+			v_color = vec4(a_color.xyz, u_alpha);\
+		}\
+		', '\
+		precision mediump float;\
+		varying vec4 v_color;\
+		void main() {\
+			gl_FragColor = v_color;\
+		}\
+	');
+			
+	shaders["unlit_textured"] = new Shader('\
+		precision highp float;\
+		attribute vec3 a_vertex;\
+		attribute vec2 a_coord;\
+		uniform mat4 u_mvp;\
+		varying vec2 v_coord;\
+		varying float v_z;\
+		void main() {\
+			gl_PointSize = 3.0;\
+			gl_Position = u_mvp * vec4(a_vertex.xyz, 1.0);\
+			v_coord = a_coord;\
+			v_z = a_vertex.z;\
+		}\
+		', '\
+		precision mediump float;\
+		varying vec2 v_coord;\
+		varying float v_z;\
+		uniform sampler2D u_texture;\
+		uniform float u_alpha;\
+		uniform bool u_maskEnabled;\
+		uniform float u_maskHeight;\
+		uniform vec4 u_maskColor;\
+		void main() {\
+			if (u_maskEnabled && (v_z < u_maskHeight)) {\
+				gl_FragColor = vec4(u_maskColor.rgb, u_alpha);\
+			}\
+			else {\
+				gl_FragColor = vec4(texture2D(u_texture, v_coord).rgb, u_alpha);\
+			}\
+		}\
+	');
+	/*
+	shaders["phong"] = new Shader('\
+		precision highp float;\
+		attribute vec3 a_vertex;\
+		attribute vec3 a_normal;\
+		attribute vec2 a_coord;\
+		varying vec3 v_normal;\
+		varying vec2 v_coord;\
+		uniform mat4 u_mvp;\
+		uniform mat4 u_model;\
+		void main() {\
+			v_coord = a_coord;\
+			v_normal = (u_model * vec4(a_normal,0.0)).xyz;\
+			gl_Position = u_mvp * vec4(a_vertex,1.0);\
+		}\
+		', '\
+		precision highp float;\
+		varying vec3 v_normal;\
+		varying vec2 v_coord;\
+		uniform vec3 u_lightvector;\
+		uniform vec4 u_color;\
+		uniform sampler2D u_texture;\
+		void main() {\
+		  vec3 N = normalize(v_normal);\
+		  vec4 color = u_color * texture2D( u_texture, v_coord);\
+		  gl_FragColor = color * max(0.0, dot(u_lightvector,N));\
+		}\
+	');
+	*/
 	return shaders;
 } //}}}
-function getShaderByString(gl,str,type) { //{{{
-	var shader;
-	if (type == "fsh") {
-		shader = gl.createShader(gl.FRAGMENT_SHADER);
-	}
-	else if (type == "vsh") {
-		shader = gl.createShader(gl.VERTEX_SHADER);
-	}
-	else {
-		return null;
-	}
-	
-	gl.shaderSource(shader, str);
-	gl.compileShader(shader);
-
-	if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {	
-		alert(gl.getShaderInfoLog(shader));
-		return null;
-	}
-
-	return shader;
-} //}}}
-function nameFromLine(line) { //{{{
-	//returns lowerCamelCase property name from shader line
-	var fullName = line.split(" ")[2];
-	return fullName.slice(0, fullName.search(";"));
-} //}}}
-//}}}
 //{{{ Interface Functions
 function onPan(ev,canvas,displaylog) { //{{{
@@ -428,58 +334,29 @@
 }//}}}
 function drawSceneGraphNode(canvas,node) { //{{{
-	if (!node["enabled"]) {
-		return;
-	}
-	if (node["texture"]) {
-		if (!node["texture"]["isLoaded"]) {
-			return;
-		}
-	}
-	var gl = canvas.gl;
-	bindAttributes(gl, node["shader"], node["buffers"]);
+	if (!node["enabled"]) return;
+	
 	var mvpMatrix = mat4.create();
 	mat4.multiply(mvpMatrix, canvas.cameraMatrix, node["modelMatrix"]);
-	gl.uniformMatrix4fv(node["shader"]["uMVPMatrix"], false, mvpMatrix);
-	gl.uniform1f(node["shader"]["uAlpha"], node["alpha"]);
-	gl.uniform1i(node["shader"]["uMaskEnabled"], node["maskEnabled"]);
-	gl.uniform1f(node["shader"]["uMaskHeight"], node["maskHeight"]);
-	gl.uniform4fv(node["shader"]["uMaskColor"], node["maskColor"]);
-	if (node["texture"]) {
-		gl.activeTexture(gl.TEXTURE0);
-		gl.bindTexture(gl.TEXTURE_2D, node["texture"]);
-		gl.uniform1i(node["shader"]["uColorSampler"], 0);	
-	}
-	if (node["disableDepthTest"]) {
-		gl.disable(gl.DEPTH_TEST);
-	}
+	
+	if (node["texture"]) node["texture"].bind(0);
+	if (node["disableDepthTest"]) gl.disable(gl.DEPTH_TEST);
+	
 	gl.lineWidth(node["lineWidth"]);
-	gl.blendFunc (gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
-	if  (node["useIndexBuffer"] == true) {
-		gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, node["buffers"][node["buffers"].length - 1]);
-		gl.drawElements(node["drawMode"], node["buffers"][node["buffers"].length - 1].numItems, gl.UNSIGNED_SHORT, 0);
-	}
-	else {
-		gl.drawArrays(node["drawMode"], 0, node["buffers"][0].numItems);
-	}	
+	gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
+
+	node["shader"].uniforms({
+		u_mvp: mvpMatrix,
+		u_texture: 0,
+		u_alpha: node["alpha"],
+		u_maskEnabled: node["maskEnabled"],
+		u_maskHeight: node["maskHeight"],
+		u_maskColor: node["maskColor"]
+	})
+	if (node["useIndexBuffer"] == true) node["shader"].draw(node["mesh"], node["drawMode"], "indices");
+	else node["shader"].draw(node["mesh"], node["drawMode"]);
+
 	gl.enable(gl.DEPTH_TEST);
 } //}}}
-function bindAttributes(gl,shaderProgram,bufferArray) { //{{{
-	gl.useProgram(shaderProgram);
-	var arrayNumber = 0;
-	for (var propertyName in shaderProgram) {
-		if (propertyName[0] == "a") {
-			if (bufferArray[arrayNumber].itemSize > 1) {
-				gl.bindBuffer(gl.ARRAY_BUFFER, bufferArray[arrayNumber]);
-				gl.vertexAttribPointer(shaderProgram[propertyName], bufferArray[arrayNumber].itemSize, gl.FLOAT, false, 0, 0);
-				arrayNumber++;
-			}
-		}
-	}
-} //}}}
 function draw(canvas,options) { //{{{
-	if (canvas.nodes.length < 1) {
-		canvas.drawHandler = window.requestAnimationFrame(function(time) {draw(canvas,options)});
-		return;
-	}
 	// Ensure canvas and gl viewport sizes are the same
 	var displayWidth  = canvas.clientWidth;
@@ -493,17 +370,21 @@
 	if (canvas.textcanvas) canvas.textcanvas.draw(canvas);
 
-	var gl = canvas.gl;
-	gl.clearColor(canvas.backgroundcolor[0], canvas.backgroundcolor[1], canvas.backgroundcolor[2], canvas.backgroundcolor[3]);
-		
-	// Skip drawing of new frame if any texture is not yet loaded
 	var nodes = canvas.nodes;
+	if (nodes.length < 1) {
+		canvas.drawHandler = window.requestAnimationFrame(function(time) {draw(canvas,options)});
+		return;
+	}
 	for (var node in nodes) {
-		if (nodes[node]["texture"] && !nodes[node]["texture"]["isLoaded"]) {
+		if (nodes[node]["texture"] && nodes[node]["texture"]["ready"] == false) {
 			canvas.drawHandler = window.requestAnimationFrame(function(time) {draw(canvas,options)});
 			return;
 		}
 	}
-	// Else, clear the color as well as the depth buffer for new frame
+	
+	var gl = canvas.gl;
+	gl.clearColor(canvas.backgroundcolor[0], canvas.backgroundcolor[1], canvas.backgroundcolor[2], canvas.backgroundcolor[3]);
 	gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+	
+	
 	
 	updateCameraMatrix(canvas);
