diff --git a/GPUMath.js b/GPUMath.js
new file mode 100644
index 0000000000000000000000000000000000000000..fb9d499b73dc4359b57ff372083e5e05d165be72
--- /dev/null
+++ b/GPUMath.js
@@ -0,0 +1,147 @@
+/**
+ * Created by ghassaei on 2/24/16.
+ */
+
+
+function initGPUMath(){
+
+    var glBoilerplate = initBoilerPlate();
+
+    var canvas = document.getElementById("glcanvas");
+    var gl = canvas.getContext("webgl", {antialias:false}) || canvas.getContext("experimental-webgl", {antialias:false});
+    gl.getExtension('OES_texture_float');
+    gl.disable(gl.DEPTH_TEST);
+
+    var maxTexturesInFragmentShader = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
+    console.log(maxTexturesInFragmentShader + " textures max");
+
+
+    function GPUMath(){
+        this.reset();
+    }
+
+    GPUMath.prototype.createProgram = function(programName, vertexShader, fragmentShader){
+        var programs = this.programs;
+        var program = programs[programName];
+        if (program) {
+            console.warn("already a program with the name " + programName);
+            return;
+        }
+        program = glBoilerplate.createProgramFromScripts(gl, vertexShader, fragmentShader);
+        gl.useProgram(program);
+        glBoilerplate.loadVertexData(gl, program);
+        programs[programName] = {
+            program: program,
+            uniforms: {}
+        };
+    };
+
+    GPUMath.prototype.initTextureFromData = function(name, width, height, typeName, data, shouldReplace){
+        var texture = this.textures[name];
+        if (!shouldReplace && texture) {
+            console.warn("already a texture with the name " + name);
+            return;
+        }
+        texture = glBoilerplate.makeTexture(gl, width, height, gl[typeName], data);
+        this.textures[name] = texture;
+    };
+
+
+
+    GPUMath.prototype.initFrameBufferForTexture = function(textureName){
+        var framebuffer = this.frameBuffers[textureName];
+        if (framebuffer) {
+            console.warn("framebuffer already exists for texture " + textureName);
+            return;
+        }
+        var texture = this.textures[textureName];
+        if (!texture){
+            console.warn("texture " + textureName + " does not exist");
+            return;
+        }
+
+        framebuffer = gl.createFramebuffer();
+        gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
+        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
+        this.frameBuffers[textureName] = framebuffer;
+    };
+
+
+    GPUMath.prototype.setUniformForProgram = function(programName, name, val, type){
+        if (!this.programs[programName]){
+            console.warn("no program with name " + programName);
+            return;
+        }
+        var uniforms = this.programs[programName].uniforms;
+        var location = uniforms[name];
+        if (!location) {
+            location = gl.getUniformLocation(this.programs[programName].program, name);
+            uniforms[name] = location;
+        }
+        if (type == "1f") gl.uniform1f(location, val);
+        else if (type == "2f") gl.uniform2f(location, val[0], val[1]);
+        else if (type == "3f") gl.uniform3f(location, val[0], val[1], val[2]);
+        else if (type == "1i") gl.uniform1i(location, val);
+        else {
+            console.warn("no uniform for type " + type);
+        }
+    };
+
+    GPUMath.prototype.setSize = function(width, height){
+        gl.viewport(0, 0, width, height);
+        canvas.clientWidth = width;
+        canvas.clientHeight = height;
+    };
+
+    GPUMath.prototype.setProgram = function(programName){
+        gl.useProgram(this.programs[programName].program);
+    };
+
+    GPUMath.prototype.step = function(programName, inputTextures, outputTexture){
+
+        gl.useProgram(this.programs[programName].program);
+        gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffers[outputTexture]);
+        for (var i=0;i<inputTextures.length;i++){
+            gl.activeTexture(gl.TEXTURE0 + i);
+            gl.bindTexture(gl.TEXTURE_2D, this.textures[inputTextures[i]]);
+        }
+        gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);//draw to framebuffer
+    };
+
+    GPUMath.prototype.swapTextures = function(texture1Name, texture2Name){
+        var temp = this.textures[texture1Name];
+        this.textures[texture1Name] = this.textures[texture2Name];
+        this.textures[texture2Name] = temp;
+        temp = this.frameBuffers[texture1Name];
+        this.frameBuffers[texture1Name] = this.frameBuffers[texture2Name];
+        this.frameBuffers[texture2Name] = temp;
+    };
+
+    GPUMath.prototype.swap3Textures = function(texture1Name, texture2Name, texture3Name){
+        var temp = this.textures[texture3Name];
+        this.textures[texture3Name] = this.textures[texture2Name];
+        this.textures[texture2Name] = this.textures[texture1Name];
+        this.textures[texture1Name] = temp;
+        temp = this.frameBuffers[texture3Name];
+        this.frameBuffers[texture3Name] = this.frameBuffers[texture2Name];
+        this.frameBuffers[texture2Name] = this.frameBuffers[texture1Name];
+        this.frameBuffers[texture1Name] = temp;
+    };
+
+    GPUMath.prototype.readyToRead = function(){
+        return gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE;
+    };
+
+    GPUMath.prototype.readPixels = function(xMin, yMin, width, height, array){
+        gl.readPixels(xMin, yMin, width, height, gl.RGBA, gl.UNSIGNED_BYTE, array);
+    };
+
+    GPUMath.prototype.reset = function(){
+        this.programs = {};
+        this.frameBuffers = {};
+        this.textures = {};
+        this.index = 0;
+    };
+
+    return new GPUMath;
+}
\ No newline at end of file
diff --git a/GlBoilerplate.js b/GlBoilerplate.js
index 57d717fdf27451b914cc535f7d1c4a514e78342c..a8ec137f59ad593a9985bcfc8088838f8ae65a5b 100644
--- a/GlBoilerplate.js
+++ b/GlBoilerplate.js
@@ -1,109 +1,155 @@
 //from http://webglfundamentals.org/webgl/lessons/webgl-boilerplate.html
 
-/**
- * Creates and compiles a shader.
- *
- * @param {!WebGLRenderingContext} gl The WebGL Context.
- * @param {string} shaderSource The GLSL source code for the shader.
- * @param {number} shaderType The type of shader, VERTEX_SHADER or
- *     FRAGMENT_SHADER.
- * @return {!WebGLShader} The shader.
- */
-function compileShader(gl, shaderSource, shaderType) {
-  // Create the shader object
-  var shader = gl.createShader(shaderType);
-
-  // Set the shader source code.
-  gl.shaderSource(shader, shaderSource);
-
-  // Compile the shader
-  gl.compileShader(shader);
-
-  // Check if it compiled
-  var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
-  if (!success) {
-    // Something went wrong during compilation; get the error
-    throw "could not compile shader:" + gl.getShaderInfoLog(shader);
-  }
-
-  return shader;
-}
-
-/**
- * Creates a program from 2 shaders.
- *
- * @param {!WebGLRenderingContext) gl The WebGL context.
- * @param {!WebGLShader} vertexShader A vertex shader.
- * @param {!WebGLShader} fragmentShader A fragment shader.
- * @return {!WebGLProgram} A program.
- */
-function createProgram(gl, vertexShader, fragmentShader) {
-  // create a program.
-  var program = gl.createProgram();
-
-  // attach the shaders.
-  gl.attachShader(program, vertexShader);
-  gl.attachShader(program, fragmentShader);
-
-  // link the program.
-  gl.linkProgram(program);
-
-  // Check if it linked.
-  var success = gl.getProgramParameter(program, gl.LINK_STATUS);
-  if (!success) {
-      // something went wrong with the link
-      throw ("program filed to link:" + gl.getProgramInfoLog (program));
-  }
-
-  return program;
-}
-
-/**
- * Creates a shader from the content of a script tag.
- *
- * @param {!WebGLRenderingContext} gl The WebGL Context.
- * @param {string} scriptId The id of the script tag.
- * @param {string} opt_shaderType. The type of shader to create.
- *     If not passed in will use the type attribute from the
- *     script tag.
- * @return {!WebGLShader} A shader.
- */
-function createShaderFromScript(gl, scriptId, opt_shaderType) {
-  // look up the script tag by id.
-  var shaderScript = document.getElementById(scriptId);
-  if (!shaderScript) {
-    throw("*** Error: unknown script element" + scriptId);
-  }
-
-  // extract the contents of the script tag.
-  var shaderSource = shaderScript.text;
-
-  // If we didn't pass in a type, use the 'type' from
-  // the script tag.
-  if (!opt_shaderType) {
-    if (shaderScript.type == "x-shader/x-vertex") {
-      opt_shaderType = gl.VERTEX_SHADER;
-    } else if (shaderScript.type == "x-shader/x-fragment") {
-      opt_shaderType = gl.FRAGMENT_SHADER;
-    } else if (!opt_shaderType) {
-      throw("*** Error: shader type not set");
+function initBoilerPlate(){
+
+    /**
+     * Creates and compiles a shader.
+     *
+     * @param {!WebGLRenderingContext} gl The WebGL Context.
+     * @param {string} shaderSource The GLSL source code for the shader.
+     * @param {number} shaderType The type of shader, VERTEX_SHADER or
+     *     FRAGMENT_SHADER.
+     * @return {!WebGLShader} The shader.
+     */
+    function compileShader(gl, shaderSource, shaderType) {
+      // Create the shader object
+      var shader = gl.createShader(shaderType);
+
+      // Set the shader source code.
+      gl.shaderSource(shader, shaderSource);
+
+      // Compile the shader
+      gl.compileShader(shader);
+
+      // Check if it compiled
+      var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
+      if (!success) {
+        // Something went wrong during compilation; get the error
+        throw "could not compile shader:" + gl.getShaderInfoLog(shader);
+      }
+
+      return shader;
+    }
+
+    /**
+     * Creates a program from 2 shaders.
+     *
+     * @param {!WebGLRenderingContext) gl The WebGL context.
+     * @param {!WebGLShader} vertexShader A vertex shader.
+     * @param {!WebGLShader} fragmentShader A fragment shader.
+     * @return {!WebGLProgram} A program.
+     */
+    function createProgram(gl, vertexShader, fragmentShader) {
+      // create a program.
+      var program = gl.createProgram();
+
+      // attach the shaders.
+      gl.attachShader(program, vertexShader);
+      gl.attachShader(program, fragmentShader);
+
+      // link the program.
+      gl.linkProgram(program);
+
+      // Check if it linked.
+      var success = gl.getProgramParameter(program, gl.LINK_STATUS);
+      if (!success) {
+          // something went wrong with the link
+          throw ("program filed to link:" + gl.getProgramInfoLog (program));
+      }
+
+      return program;
+    }
+
+    /**
+     * Creates a shader from the content of a script tag.
+     *
+     * @param {!WebGLRenderingContext} gl The WebGL Context.
+     * @param {string} scriptId The id of the script tag.
+     * @param {string} opt_shaderType. The type of shader to create.
+     *     If not passed in will use the type attribute from the
+     *     script tag.
+     * @return {!WebGLShader} A shader.
+     */
+    function createShaderFromSource(gl, shaderSource, shaderType) {
+      return compileShader(gl, shaderSource, shaderType);
+    }
+
+    /**
+     * Creates a program from 2 script tags.
+     *
+     * @param {!WebGLRenderingContext} gl The WebGL Context.
+     * @param {string} vertexShaderId The id of the vertex shader script tag.
+     * @param {string} fragmentShaderId The id of the fragment shader script tag.
+     * @return {!WebGLProgram} A program
+     */
+    function createProgramFromSource(
+        gl, vertexShader, fragmentShader) {
+      var vertexShader = createShaderFromSource(gl, vertexShader, gl.VERTEX_SHADER);
+      var fragmentShader = createShaderFromSource(gl, fragmentShader, gl.FRAGMENT_SHADER);
+      return createProgram(gl, vertexShader, fragmentShader);
+    }
+
+    function createProgramFromScripts(gl, vertexShaderId, fragmentShaderId) {
+      var vertexShader = createShaderFromScript(gl, vertexShaderId);
+      var fragmentShader = createShaderFromScript(gl, fragmentShaderId);
+      return createProgram(gl, vertexShader, fragmentShader);
+    }
+
+    function createShaderFromScript(gl, scriptId, opt_shaderType) {
+      // look up the script tag by id.
+      var shaderScript = document.getElementById(scriptId);
+      if (!shaderScript) {
+        throw("*** Error: unknown script element" + scriptId);
+      }
+
+      // extract the contents of the script tag.
+      var shaderSource = shaderScript.text;
+
+      // If we didn't pass in a type, use the 'type' from
+      // the script tag.
+      if (!opt_shaderType) {
+        if (shaderScript.type == "x-shader/x-vertex") {
+          opt_shaderType = gl.VERTEX_SHADER;
+        } else if (shaderScript.type == "x-shader/x-fragment") {
+          opt_shaderType = gl.FRAGMENT_SHADER;
+        } else if (!opt_shaderType) {
+          throw("*** Error: shader type not set");
+        }
+      }
+
+      return compileShader(gl, shaderSource, opt_shaderType);
+    }
+
+    function loadVertexData(gl, program) {
+        gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
+        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ -1,-1, 1,-1, -1, 1, 1, 1]), gl.STATIC_DRAW);
+
+        // look up where the vertex data needs to go.
+        var positionLocation = gl.getAttribLocation(program, "a_position");
+        gl.enableVertexAttribArray(positionLocation);
+        gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
+    }
+
+    function makeTexture(gl, width, height, type, data){
+
+        var texture = gl.createTexture();
+        gl.bindTexture(gl.TEXTURE_2D, texture);
+
+        // Set the parameters so we can render any size image.
+        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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+
+        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, type, data);
+
+        return texture;
+    }
+
+    return {
+        createProgramFromSource: createProgramFromSource,
+        createProgramFromScripts: createProgramFromScripts,
+        loadVertexData: loadVertexData,
+        makeTexture: makeTexture
     }
-  }
-
-  return compileShader(gl, shaderSource, opt_shaderType);
-}
-
-/**
- * Creates a program from 2 script tags.
- *
- * @param {!WebGLRenderingContext} gl The WebGL Context.
- * @param {string} vertexShaderId The id of the vertex shader script tag.
- * @param {string} fragmentShaderId The id of the fragment shader script tag.
- * @return {!WebGLProgram} A program
- */
-function createProgramFromScripts(
-    gl, vertexShaderId, fragmentShaderId) {
-  var vertexShader = createShaderFromScript(gl, vertexShaderId);
-  var fragmentShader = createShaderFromScript(gl, fragmentShaderId);
-  return createProgram(gl, vertexShader, fragmentShader);
-}
\ No newline at end of file
+};
\ No newline at end of file
diff --git a/index.html b/index.html
index a5e4d79e55c962a23b0cbef78261f64627df74cb..97fc66f2bc766c47d44fd3cfafb4c6a65dde218c 100644
--- a/index.html
+++ b/index.html
@@ -20,14 +20,36 @@
     <script id="2d-render-shader" type="x-shader/x-fragment">
         precision mediump float;
 
-        uniform sampler2D u_image;
-        uniform vec2 u_textureSize;
+        //uniform sampler2D u_velocities;
+        //uniform vec2 u_textureSize;
+
+        void main() {
+            gl_FragColor = vec4(1, 0, 1, 1);
+        }
+    </script>
+
+     <script id="advectShader" type="x-shader/x-fragment">
+        precision mediump float;
 
-        const float COLOR_MIN = 0.2, COLOR_MAX = 0.4;
+        //uniform sampler2D u_velocities;
+        //uniform sampler2D u_material;
+
+        //uniform vec2 u_textureSize;
+
+        //uniform float u_dt;
 
         void main() {
-            float v = (COLOR_MAX - texture2D(u_image, gl_FragCoord.xy / u_textureSize)).y / (COLOR_MAX - COLOR_MIN);
-            gl_FragColor = vec4(v, v, v, 1);
+
+            //vec2 fragCoord = gl_FragCoord.xy;
+
+            //vec2 currentVelocity = texture2D(u_velocities, fragCoord/u_textureSize).xy;
+
+            //implicitly solve advection
+            //vec2 pos = fragCoord - u_dt * currentVelocity;
+
+            // bilinear interp between nearest cells
+            //gl_FragColor = texture2D(u_material, pos/u_textureSize);
+            gl_FragColor = vec4(1,1,1,1);
         }
     </script>
 
@@ -74,7 +96,8 @@
     <script type="text/javascript" src="dependencies/jquery-3.1.0.min.js"></script>
     <script type="text/javascript" src="dependencies/flat-ui.min.js"></script>
 
-    <script type="text/javascript" src="GlBoilerplate.js"></script>
+    <script type="text/javascript" src="GLBoilerplate.js"></script>
+    <script type="text/javascript" src="GPUMath.js"></script>
     <script type="text/javascript" src="main.js"></script>
 </head>
 <body>
@@ -89,9 +112,10 @@
             <div class="modal-body">
                 <b>Fluid Simulation Shader</b><br/><br/>
                 I used the following sources to write this simulation:<br/><br/>
-                <a href="https://pdfs.semanticscholar.org/84b8/c7b7eecf90ebd9d54a51544ca0f8ff93c137.pdf" target="_blank">Real-time ink simulation using a grid-particle method</a><br/>
+                <a href="https://pdfs.semanticscholar.org/84b8/c7b7eecf90ebd9d54a51544ca0f8ff93c137.pdf" target="_blank">Real-time ink simulation using a grid-particle method</a> - a method for real-time simulation of ink
+                in water using a coarse-grained fluid simulation with a particle simulation on top.<br/>
                 <a href="http://http.developer.nvidia.com/GPUGems/gpugems_ch38.html" target="_blank">Fast Fluid Dynamics Simulation on the GPU</a> - a very well written tutorial about programming the Navier-Stokes equations on a GPU.<br/>
-                <a href="http://www.dgp.toronto.edu/people/stam/reality/Research/pdf/ns.pdf" target="_blank">Stable Fluids</a> - a paper about numerical methods for evaluating Navier-Stokes on a discrete grid.<br/>
+                <a href="http://www.dgp.toronto.edu/people/stam/reality/Research/pdf/ns.pdf" target="_blank">Stable Fluids</a> - a paper about stable numerical methods for evaluating Navier-Stokes on a discrete grid.<br/>
                 <br/>
                 By <a href="http://www.amandaghassaei.com/" target="_blank">Amanda Ghassaei</a>, code on <a href="https://github.com/amandaghassaei/FluidSimulation" target="_blank">Github</a>.
                 <br/><br/>
diff --git a/main.js b/main.js
index da231740fd28d3ebe8c0c4bb1810360f6df824cd..dabcad1efbef69dc297104b405656b5e4d896cd4 100755
--- a/main.js
+++ b/main.js
@@ -1,21 +1,6 @@
 //used a lot of ideas from https://bl.ocks.org/robinhouston/ed597847175cf692ecce to clean this code up
 
-var gl;
-var canvas;
-var frameBuffers;
-var states = [null, null];
-
-var resizedLastState;
-var resizedCurrentState;
-
-var width;
-var height;
-
-var stepProgram;
-var renderProgram;
-
-var textureSizeLocation;
-var textureSizeLocationRender;
+var width, height;
 
 var mouseCoordLocation;
 var mouseCoordinates =  [null, null];
@@ -24,6 +9,8 @@ var mouseEnable = false;
 
 var paused = false;//while window is resizing
 
+var GPU;
+
 window.onload = initGL;
 
 function initGL() {
@@ -33,10 +20,7 @@ function initGL() {
         $("#aboutModal").modal('show');
     });
 
-    // Get A WebGL context
     canvas = document.getElementById("glcanvas");
-    canvas.width = canvas.clientWidth;
-    canvas.height = canvas.clientHeight;
 
     canvas.onmousemove = onMouseMove;
     canvas.onmousedown = onMouseDown;
@@ -45,125 +29,31 @@ function initGL() {
 
     window.onresize = onResize;
 
-    gl = canvas.getContext("webgl", {antialias:false}) || canvas.getContext("experimental-webgl", {antialias:false});
-    if (!gl) {
-        alert('Could not initialize WebGL, try another browser');
-        return;
-    }
-
-    gl.disable(gl.DEPTH_TEST);
-    gl.getExtension('OES_texture_float');
+    GPU = initGPUMath();
 
-    // setup a GLSL program
-    stepProgram = createProgramFromScripts(gl, "2d-vertex-shader", "2d-fragment-shader");
-    renderProgram = createProgramFromScripts(gl, "2d-vertex-shader", "2d-render-shader");
-    gl.useProgram(renderProgram);
-    loadVertexData(gl, renderProgram);
-    textureSizeLocationRender = gl.getUniformLocation(renderProgram, "u_textureSize");
-    gl.useProgram(stepProgram);
-    loadVertexData(gl, stepProgram);
-
-
-    textureSizeLocation = gl.getUniformLocation(stepProgram, "u_textureSize");
-    mouseCoordLocation = gl.getUniformLocation(stepProgram, "u_mouseCoord");
-    mouseEnableLocation = gl.getUniformLocation(stepProgram, "u_mouseEnable");
-
-    frameBuffers = [makeFrameBuffer(), makeFrameBuffer()];
+    // setup a GLSL programs
+    GPU.createProgram("advect", "2d-vertex-shader", "advectShader");
+    GPU.createProgram("render", "2d-vertex-shader", "2d-render-shader");
 
     resetWindow();
 
-    gl.bindTexture(gl.TEXTURE_2D, states[0]);//original texture
+    GPU.initFrameBufferForTexture("velocities");
 
     render();
 }
 
-function loadVertexData(gl, program) {
-	gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
-	gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ -1,-1, 1,-1, -1, 1, 1, 1]), gl.STATIC_DRAW);
-
-    // look up where the vertex data needs to go.
-	var positionLocation = gl.getAttribLocation(program, "a_position");
-	gl.enableVertexAttribArray(positionLocation);
-	gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
-}
-
-function makeFrameBuffer(){
-    return gl.createFramebuffer();
-}
-
-function makeRandomArray(rgba){
-    for (var x=0;x<width;x++) {
-        for (var y=0;y<height;y++) {
-            var ii = (y*width + x) * 4;
-			var central_square = (x > width/2-10 && x < width/2+10 && y > height/2-10 && y < height/2+10);
-			if (central_square) {
-				rgba[ii] = 0.5 + Math.random() * 0.02 - 0.01;
-				rgba[ii + 1] = 0.25 + Math.random() * 0.02 - 0.01;
-			} else {
-				rgba[ii] = 1.0;
-				rgba[ii + 1] = 0;
-			}
-        }
-    }
-    return rgba;
-}
-
-function makeTexture(gl, data){
-
-    var texture = gl.createTexture();
-    gl.bindTexture(gl.TEXTURE_2D, texture);
-
-    // Set the parameters so we can render any size image.
-    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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
-    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
-
-    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.FLOAT, data);
-
-    return texture;
-}
-
 function render(){
 
     if (!paused) {
 
-        gl.useProgram(stepProgram);
-
-        if (mouseEnable){
-            gl.uniform1f(mouseEnableLocation, 1);
-            gl.uniform2f(mouseCoordLocation, mouseCoordinates[0], mouseCoordinates[1]);
-        } else gl.uniform1f(mouseEnableLocation, 0);
-
-        if (resizedLastState) {
-            states[0] = resizedLastState;
-            gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffers[0]);
-            gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, states[0], 0);
-            resizedLastState = null;
-        }
-        if (resizedCurrentState) {
-            states[1] = resizedCurrentState;
-            gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffers[1]);
-            gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, states[1], 0);
-            resizedCurrentState = null;
-        }
 
-        for (var i=0;i<2;i++) {
-            if (paused) {
-                window.requestAnimationFrame(render);
-                return;
-            }
 
-            step(i);
-        }
+        // if (mouseEnable){
+        //     gl.uniform1f(mouseEnableLocation, 1);
+        //     gl.uniform2f(mouseCoordLocation, mouseCoordinates[0], mouseCoordinates[1]);
+        // } else gl.uniform1f(mouseEnableLocation, 0);
 
-
-        gl.useProgram(renderProgram);
-
-        //draw to canvas
-        gl.bindFramebuffer(gl.FRAMEBUFFER, null);
-        gl.bindTexture(gl.TEXTURE_2D, states[0]);
-        gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
+        GPU.step("render", []);
 
     } else resetWindow();
 
@@ -187,20 +77,14 @@ function resetWindow(){
     width = canvas.clientWidth;
     height = canvas.clientHeight;
 
-    gl.viewport(0, 0, width, height);
+    GPU.setSize(width, height);
+
 
-    // set the size of the texture
-    gl.useProgram(stepProgram);
-    gl.uniform2f(textureSizeLocation, width, height);
-    gl.useProgram(renderProgram);
-    gl.uniform2f(textureSizeLocationRender, width, height);
+    GPU.setUniformForProgram("advect" ,"u_textureSize", [width, height], "2f");
 
-    //texture for saving output from frag shader
-    resizedCurrentState = makeTexture(gl, null);
 
-    //fill with random pixels
-    var rgba = new Float32Array(width*height*4);
-    resizedLastState = makeTexture(gl, makeRandomArray(rgba));
+    var velocities = new Float32Array(width*height*4);
+    GPU.initTextureFromData("velocities", width, height, "FLOAT", velocities, true);
 
     paused = false;
 }