From 75f252a390702d99c4926bd9a1fc756e513172e3 Mon Sep 17 00:00:00 2001
From: lp407 <lukeplowden@gmail.com>
Date: Thu, 13 Feb 2025 15:52:16 +0000
Subject: [PATCH 01/54] local dev server vite path changed

---
 preview/global/index.html      | 4 ++--
 preview/global/vite.config.mjs | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/preview/global/index.html b/preview/global/index.html
index 7c8fa7d0d4..3235c1e5bd 100644
--- a/preview/global/index.html
+++ b/preview/global/index.html
@@ -13,9 +13,9 @@
   }
   </style>
 
-  <script src="./p5.js"></script>
+  <script src="/p5.js"></script>
 </head>
 <body>
-<script src="./sketch.js"></script>
+<script src="/sketch.js"></script>
 </body>
 </html>
\ No newline at end of file
diff --git a/preview/global/vite.config.mjs b/preview/global/vite.config.mjs
index e322f16aa4..ed95741666 100644
--- a/preview/global/vite.config.mjs
+++ b/preview/global/vite.config.mjs
@@ -17,7 +17,7 @@ export default defineConfig({
       name: 'reload',
       configureServer(server) {
         const { ws, watcher } = server;
-        const buildLibPath = path.resolve(libPath, './p5.rollup.js');
+        const buildLibPath = path.resolve(libPath, './p5.js');
         watcher.add(buildLibPath);
         watcher.on('change', file => {
           if(file === buildLibPath){

From 80b5351c983e385caf0248e341671348d5a1b0b3 Mon Sep 17 00:00:00 2001
From: lp407 <lukeplowden@gmail.com>
Date: Thu, 13 Feb 2025 16:31:32 +0000
Subject: [PATCH 02/54] import system working

---
 src/webgl/ShaderGen.js | 25 +++++++++++++++++++++++++
 src/webgl/index.js     |  2 ++
 2 files changed, 27 insertions(+)
 create mode 100644 src/webgl/ShaderGen.js

diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
new file mode 100644
index 0000000000..11dca9b157
--- /dev/null
+++ b/src/webgl/ShaderGen.js
@@ -0,0 +1,25 @@
+/**
+ * @module 3D
+ * @submodule ShaderGenerator
+ * @for p5
+ * @requires core
+ */
+
+function shadergen(p5, fn) {
+    const oldModify = p5.Shader.prototype.modify
+    p5.Shader.prototype.modify = function(arg) {
+      if (arg instanceof Function) {
+        // const program = new ShaderProgram(arg)
+        // const newArg = program.run()
+        return oldModify.call(this, arg)
+      } else {
+        return oldModify.call(this, arg)
+      }
+    }
+  }
+  
+  if (typeof p5 !== 'undefined') {
+    p5.registerAddon(shadergen)
+}
+
+export default shadergen;
\ No newline at end of file
diff --git a/src/webgl/index.js b/src/webgl/index.js
index c2515fce5a..2ce0957fa6 100644
--- a/src/webgl/index.js
+++ b/src/webgl/index.js
@@ -14,6 +14,7 @@ import shader from './p5.Shader';
 import camera from './p5.Camera';
 import texture from './p5.Texture';
 import rendererGL from './p5.RendererGL';
+import shadergen from './ShaderGen';
 
 export default function(p5){
   rendererGL(p5, p5.prototype);
@@ -32,4 +33,5 @@ export default function(p5){
   dataArray(p5, p5.prototype);
   shader(p5, p5.prototype);
   texture(p5, p5.prototype);
+  shadergen(p5, p5.prototype);
 }

From 76eef1bf20b078b390dc98910c5b2196058c4ec1 Mon Sep 17 00:00:00 2001
From: lp407 <lukeplowden@gmail.com>
Date: Thu, 13 Feb 2025 18:31:43 +0000
Subject: [PATCH 03/54] Most of the code from original file ported here

---
 src/webgl/ShaderGen.js | 454 +++++++++++++++++++++++++++++++++++++++--
 1 file changed, 439 insertions(+), 15 deletions(-)

diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index 11dca9b157..a72013020e 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -1,25 +1,449 @@
 /**
- * @module 3D
- * @submodule ShaderGenerator
- * @for p5
- * @requires core
- */
+* @module 3D
+* @submodule ShaderGenerator
+* @for p5
+* @requires core
+*/
 
 function shadergen(p5, fn) {
-    const oldModify = p5.Shader.prototype.modify
-    p5.Shader.prototype.modify = function(arg) {
-      if (arg instanceof Function) {
-        // const program = new ShaderProgram(arg)
-        // const newArg = program.run()
-        return oldModify.call(this, arg)
+  const oldModify = p5.Shader.prototype.modify
+  let GLOBAL_SHADER;
+  p5.Shader.prototype.modify = function(arg) {
+    if (arg instanceof Function) {
+      const program = new ShaderProgram(arg)
+      const newArg = program.generate();
+      console.log(newArg)
+      return oldModify.call(this, newArg);
+    } else {
+      return oldModify.call(this, arg)
+    }
+  }
+
+  class BaseNode {
+    constructor() {
+      if (new.target === BaseNode) {
+        throw new TypeError("Cannot construct BaseNode instances directly. This is an abstract class.");
+      }
+      this.type = null;
+      
+      // For tracking recursion depth and creating temporary variables
+      this.isInternal = false; // TODO: dont use temp nodes for internal nodes 
+      this.usedIn = [];
+      this.dependsOn = [];
+      this.srcLine = null;
+      try {
+        throw new Error("StackCapture");
+      } catch (e) {
+        const lines = e.stack.split("\n");
+        console.log(lines)
+        this.srcLine = lines[4].trim();
+      }
+    }
+    
+    // The base node implements a version of toGLSL which determines whether the generated code should be stored in a temporary variable.
+    toGLSLBase(context){
+      if (this.useTempVar()) {
+        return this.getTemp(context);
+      } 
+      else {
+        return this.toGLSL(context);
+      }
+    }
+
+    useTempVar() {
+      if (this.isInternal) { return false; }
+      if (isVariableNode(this)) { return false; }
+      if (isVectorNode(this)) { return true; }
+      return (this.usedIn.length > 1);
+    };
+    
+    getTemp(context) {
+      if (!this.temporaryVariable) {
+        this.temporaryVariable = `temp_${context.getNextID()}`;
+        let line = "";
+        if (this.srcLine) {
+          line += `\n// From ${this.srcLine}\n`;
+        }
+        line += this.type + " " + this.temporaryVariable + " = " + this.toGLSLNoTemp(context) + ";";
+        context.declarations.push(line);
+      }
+      return this.temporaryVariable;
+    };
+    
+    // TODO: Add more operations here
+    add(other)  { return new AdditionNode(this, this.enforceType(other)); }
+    sub(other)  { return new SubtractionNode(this, this.enforceType(other)); }
+    mult(other) { return new MultiplicationNode(this, this.enforceType(other)); }
+    div(other)  { return new DivisionNode(this, this.enforceType(other)); }
+    mod(other)  { return new ModulusNode(this, this.enforceType(other)); }
+    sin()       { return new FunctionCallNode('sin', this, 'float'); }
+    cos()       { return new FunctionCallNode('cos', this, 'float'); }
+    radians()   { return new FunctionCallNode('radians', this, 'float'); }
+    
+    // Check that the types of the operands are compatible.
+    // TODO: improve this with less branching if elses
+    enforceType(other){
+      if (isShaderNode(other)){
+        if ((isFloatNode(this) || isVectorNode(this)) && isIntNode(other)) {
+          return new FloatNode(other)
+        }
+        return other;
+      }
+      else if(typeof other === 'number') {
+        if (isIntNode(this)) {
+          return new IntNode(other);
+        }
+        return new FloatNode(other);
+      } 
+      else {
+        return new this.constructor(other);
+      }
+    }
+    
+    toGLSL(context){
+      throw new TypeError("Not supposed to call this function on BaseNode, which is an abstract class.");
+    }
+  }
+
+  // Primitive Types
+  class IntNode extends BaseNode {
+    constructor(x = 0) {
+      super();
+      this.x = x;
+      this.type = 'int';
+    }
+    toGLSL(context) {
+      if (isShaderNode(this.x)) {
+        let code = this.x.toGLSL(context);
+        return isIntNode(this.x.type) ? code : `int(${code})`;
+      }
+      else if (typeof this.x === "number") {
+        return `${Math.floor(this.x)}`;
+      }
+      else {
+        return `int(${this.x})`;
+      }
+    }
+  }
+  
+  class FloatNode extends BaseNode {
+    constructor(x = 0){
+      super();
+      this.x = x;
+      this.type = 'float';
+    }
+    toGLSL(context) {
+      if (isShaderNode(this.x)) {
+        let code = this.x.toGLSL(context);
+        console.log(code)
+        return isFloatNode(this.x) ? code : `float(${code})`;
+      }
+      else if (typeof this.x === "number") {
+        return `${this.x.toFixed(4)}`;
+      }
+      else {
+        return `float(${this.x})`;
+      }
+    }
+  }
+  
+  class VectorNode extends BaseNode {
+    constructor(values, type) {
+      super();
+      this.x = new FloatNode(x);
+      this.y = new FloatNode(y);
+      this.type = type;
+    }
+    
+    toGLSL(context) {
+      return `${this.type}(${this.x.toGLSLBase(context)}, ${this.y.toGLSLBase(context)})`
+    }
+  }
+
+  // Function Call Nodes
+  class FunctionCallNode extends BaseNode {
+    constructor(name, args, type) {
+      super();
+      this.name = name;
+      this.args = args;
+      this.type = type;
+    }
+    deconstructArgs(context) {
+      if (this.args.constructor === Array) {
+        let argsString = `${this.args[0].toGLSL(context)}`
+        for (let arg of this.args.slice(1)) {
+          argsString += `, ${arg.toGLSL(context)}` 
+          return argsString;
+        }
       } else {
-        return oldModify.call(this, arg)
+        return `${this.args.toGLSL(context)}`;
+      }
+    }
+    toGLSL(context) {
+      return `${this.name}(${this.deconstructArgs(context)})`;
+    }
+  }
+  
+  // Variables and member variable nodes
+  class VariableNode extends BaseNode {
+    constructor(name, type) {
+      super()
+      this.name = name;
+      this.type = type;
+      switch (type) {
+        case 'float':
+        break;
+        case 'vec2':
+        this.addSwizzles('x', 'y')
+        break;
+        case 'vec3':
+        this.addSwizzles('x', 'y', 'z')
+        break;
+        case 'vec4':
+        this.addSwizzles('x', 'y', 'z', 'w');  
+        break;
       }
     }
+    addSwizzles() {
+      for (let name of arguments) {
+        this[name] = new ComponentNode(this.name, name);
+      }
+    }
+    toGLSL(context) {
+      return `${this.name}`;
+    }
   }
   
-  if (typeof p5 !== 'undefined') {
-    p5.registerAddon(shadergen)
+  class ComponentNode extends BaseNode {
+    constructor(parent, component) {
+      super();
+      this.varName = parent;
+      this.component = component;
+      this.type = 'float';
+    }
+    toGLSL(context) {
+      return `${this.varName}.${this.component}`;
+    }
+  }
+
+  // Binary Operator Nodes
+  class BinaryOperatorNode extends BaseNode {
+    constructor(a, b) {
+      super();
+      this.a = a;
+      this.b = b;
+      for (const param of arguments) {
+        param.usedIn.push(this);
+      }
+      this.type = this.determineType();
+    }
+    
+    // We know that both this.a and this.b are nodes because of PrimitiveNode.enforceType
+    determineType() {
+      if (this.a.type === this.b.type) {
+        return this.a.type;
+      } 
+      else if (isVectorNode(this.a) && isFloatNode(this.b)) {
+        return this.a.type;
+      } 
+      else if (isVectorNode(this.b) && isFloatNode(this.a)) {
+        return this.b.type;
+      } 
+      else if (isFloatNode(this.a) && isIntNode(this.b) 
+        || isIntNode(this.a) && isFloatNode(this.b)
+      ) {
+        return 'float';
+      }
+      else {
+        throw new Error("Incompatible types for binary operator");
+      }
+    }
+
+    processOperand(context, operand) {
+      const code = operand.toGLSL(context);
+      if (this.type === 'float' && isIntNode(operand)) {
+        return `float${code}`;
+      }
+      return code;
+    }
+  }
+
+  class MultiplicationNode extends BinaryOperatorNode {
+    constructor(a, b) {
+      super(a, b)
+    }
+    toGLSLNoTemp(context) {
+      return `(${this.processOperand(context, this.a)} * ${this.processOperand(context, this.b)})`;
+    }
+  }
+
+  class DivisionNode extends BinaryOperatorNode {
+    constructor(a, b) {
+      super(a, b)
+    }
+    toGLSLNoTemp(context) {
+      return `(${this.processOperand(context, this.a)} / ${this.processOperand(context, this.b)})`;
+    }
+  }
+
+  class AdditionNode extends BinaryOperatorNode {
+    constructor(a, b) {
+      super(a, b)
+    }
+    toGLSLNoTemp(context) {
+      return `(${this.processOperand(context, this.a)} + ${this.processOperand(context, this.b)})`;
+    }
+  }
+
+  class SubtractionNode extends BinaryOperatorNode {
+    constructor(a, b) {
+      super(a, b)
+    }
+    toGLSLNoTemp(context) {
+      return `(${this.processOperand(context, this.a)} - ${this.processOperand(context, this.b)})`;
+    }
+  }
+
+  // TODO: Correct the implementation for floats/ genType etc
+  class ModulusNode extends BinaryOperatorNode {
+    constructor(a, b) {
+      super(a, b);
+    }
+    toGLSLNoTemp(context) {
+      // Switch on type between % or mod()
+      if (isVectorNode(this) || isFloatNode(this)) {
+        return `mod(${this.a.toGLSL(context)}, ${this.b.toGLSL(context)})`;
+      }
+      return `(${this.processOperand(context, this.a)} % ${this.processOperand(context, this.b)})`;
+    }
+  }
+
+  // Helper functions
+  function isShaderNode(value) {  return (value instanceof BaseNode); }
+
+  function isIntNode(value) { 
+    return (isShaderNode(value) && (value.type === 'int')); 
+  }
+
+  function isFloatNode(value) { 
+    return (isShaderNode(value) && (value.type === 'float')); 
+  }
+
+  function isVectorNode(value) { 
+    return (isShaderNode(value) && (value.type === 'vec2'|| value.type === 'vec3' || value.type === 'vec4')); 
+  }
+
+  function isVariableNode(node) { 
+    return (node instanceof VariableNode || node instanceof ComponentNode); 
+  }
+
+  // Shader program
+  // This class is responsible for converting the nodes into an object containing GLSL code, to be used by p5.Shader.modify
+
+  class ShaderProgram {
+    constructor(modifyFunction) {
+      this.uniforms = {
+      }
+      this.functions = {
+        getWorldPosition: null
+      }
+      this.resetGLSLContext();
+      global.GLOBAL_SHADER = this;
+      this.generator = modifyFunction;
+    }
+    generate() {
+      this.generator();
+      return {
+        uniforms: this.uniforms,
+        functions: this.functions,
+        vertex: this.functions.getWorldPosition,
+        fragment: this.functions.getFinalColor,
+      }
+    }
+    resetGLSLContext() { 
+      this.context = {
+        id: 0,
+        getNextID: function() { return this.id++ },
+        declarations: [],
+      }
+    }
+    uniform(name, value) {
+      this.uniforms[name] = value;
+      return new VariableNode(name, value.type);
+    }
+    buildFunction(argumentName, argumentType, callback) {
+      let functionArgument = new VariableNode(argumentName, argumentType);
+      const finalLine = callback(functionArgument).toGLSL(this.context);
+      let codeLines = this.context.declarations.slice();
+      codeLines.push(`\n${argumentName} = ${finalLine}; \nreturn ${argumentName};`)
+      this.resetGLSLContext();
+      return codeLines.join("\n");
+    }
+    getWorldPosition(func) {
+      this.functions.getWorldPosition = this.buildFunction("pos", "vec3", func);
+    }
+    getFinalColor(func) {
+      this.functions.getFinalColor = this.buildFunction("col", "vec3", func);
+    }
+  }
+
+  // User functions
+  fn.createVector2 = function(x, y) {
+    return new VectorNode(x, y, 'vec2');
+  }
+
+  fn.createVector3 = function(x, y, z) {
+    return new VectorNode(x, y, z, 'vec3');
+  }
+
+  fn.createVector4 = function(x, y, z, w) {
+    return new VectorNode(x, y, z, w, 'vec4');
+  }
+  
+  fn.createFloat = function(x) {
+    return new FloatNode(x);
+  }
+  
+  fn.createInt = function(x) {
+    return new IntNode(x);
+  }
+  
+  fn.instanceID = function() {
+    return new VariableNode('gl_InstanceID', 'int');
+  }
+  
+  fn.uvCoords = function() {
+    return new VariableNode('vTexCoord', 'vec2');
+  }
+
+  fn.discard = function() {
+    return new VariableNode('discard', 'keyword');
+  }
+  
+  fn.uniform = function(name, value) {
+    let result = GLOBAL_SHADER.uniform(name, value)
+    return result;
+  }
+  
+  function getWorldPosition(func){
+    GLOBAL_SHADER.getWorldPosition(func)
+  }
+  function getFinalColor(func){
+    GLOBAL_SHADER.getFinalColor(func)
+  }
+
+  const oldTexture = p5.prototype.texture;
+  p5.prototype.texture = function(...args) {
+    if (isShaderNode(args[0])) {
+      return new FunctionCallNode('texture', args, 'vec4');
+    } else {
+      return oldTexture.apply(this, args);
+    }
+  }
 }
 
-export default shadergen;
\ No newline at end of file
+export default shadergen;
+
+if (typeof p5 !== 'undefined') {
+  p5.registerAddon(shadergen)
+}
\ No newline at end of file

From 4ba46ee7ba1da1830964d51f2ca7d003ddf74a5a Mon Sep 17 00:00:00 2001
From: Luke Plowden <lukeplowden@gmail.com>
Date: Fri, 14 Feb 2025 17:57:58 +0000
Subject: [PATCH 04/54] example sketch

---
 preview/global/sketch.js | 58 +++++++++-------------------------------
 1 file changed, 12 insertions(+), 46 deletions(-)

diff --git a/preview/global/sketch.js b/preview/global/sketch.js
index 4789f83f36..927489719f 100644
--- a/preview/global/sketch.js
+++ b/preview/global/sketch.js
@@ -1,55 +1,21 @@
-const vertSrc = `#version 300 es
- precision mediump float;
- uniform mat4 uModelViewMatrix;
- uniform mat4 uProjectionMatrix;
-
- in vec3 aPosition;
- in vec2 aOffset;
-
- void main(){
-   vec4 positionVec4 = vec4(aPosition.xyz, 1.0);
-   positionVec4.xy += aOffset;
-   gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;
- }
-`;
-
-const fragSrc = `#version 300 es
- precision mediump float;
- out vec4 outColor;
- void main(){
-   outColor = vec4(0.0, 1.0, 1.0, 1.0);
- }
-`;
-
 let myShader;
 function setup(){
-  createCanvas(100, 100, WEBGL);
+  createCanvas(windowWidth, windowHeight, WEBGL);
 
   // Create and use the custom shader.
-  myShader = createShader(vertSrc, fragSrc);
-
-  describe('A wobbly, cyan circle on a gray background.');
+  myShader = baseMaterialShader().modify(
+    () => {
+      const x = createVector3(1, 2)
+
+      getWorldPosition((pos) => {
+        pos = pos.add(x);
+        return pos;
+      })
+    }
+  );
 }
 
 function draw(){
   // Set the styles
-  background(125);
-  noStroke();
-  shader(myShader);
-
-  // Draw the circle.
-  beginShape();
-  for (let i = 0; i < 30; i++){
-    const x = 40 * cos(i/30 * TWO_PI);
-    const y = 40 * sin(i/30 * TWO_PI);
-
-    // Apply some noise to the coordinates.
-    const xOff = 10 * noise(x + millis()/1000) - 5;
-    const yOff = 10 * noise(y + millis()/1000) - 5;
-
-    // Apply these noise values to the following vertex.
-    vertexProperty('aOffset', [xOff, yOff]);
-    vertex(x, y);
-  }
-  endShape(CLOSE);
+  background(0)
 }

From 253f6fe326a306bd60e953855893c215c31b9a15 Mon Sep 17 00:00:00 2001
From: Luke Plowden <lukeplowden@gmail.com>
Date: Fri, 14 Feb 2025 17:59:10 +0000
Subject: [PATCH 05/54] reduce vector2,3, and 4 classes to a single vector
 class to avoid repitition

---
 src/webgl/ShaderGen.js | 94 +++++++++++++++++++++++++++---------------
 1 file changed, 61 insertions(+), 33 deletions(-)

diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index a72013020e..89db1f3f61 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -20,23 +20,25 @@ function shadergen(p5, fn) {
   }
 
   class BaseNode {
-    constructor() {
+    constructor(isInternal) {
       if (new.target === BaseNode) {
         throw new TypeError("Cannot construct BaseNode instances directly. This is an abstract class.");
       }
       this.type = null;
       
       // For tracking recursion depth and creating temporary variables
-      this.isInternal = false; // TODO: dont use temp nodes for internal nodes 
+      this.isInternal = isInternal; // TODO: dont use temp nodes for internal nodes 
       this.usedIn = [];
       this.dependsOn = [];
       this.srcLine = null;
-      try {
-        throw new Error("StackCapture");
-      } catch (e) {
-        const lines = e.stack.split("\n");
-        console.log(lines)
-        this.srcLine = lines[4].trim();
+      if (!isInternal) {
+        try {
+          throw new Error("StackCapture");
+        } catch (e) {
+          const lines = e.stack.split("\n");
+          console.log(lines)
+          this.srcLine = lines[3].trim();
+        }
       }
     }
     
@@ -107,8 +109,8 @@ function shadergen(p5, fn) {
 
   // Primitive Types
   class IntNode extends BaseNode {
-    constructor(x = 0) {
-      super();
+    constructor(x = 0, isInternal = false) {
+      super(isInternal);
       this.x = x;
       this.type = 'int';
     }
@@ -127,8 +129,8 @@ function shadergen(p5, fn) {
   }
   
   class FloatNode extends BaseNode {
-    constructor(x = 0){
-      super();
+    constructor(x = 0, isInternal = false){
+      super(isInternal);
       this.x = x;
       this.type = 'float';
     }
@@ -147,11 +149,28 @@ function shadergen(p5, fn) {
     }
   }
   
+  // There is a possibility that since we *always* use a temporary variable for vectors
+  // that we don't actually need a Float Node for every component. They could be component node's instead? 
+  // May need to then store the temporary variable name in this class.
+
+  // I think swizzles could be easy then
+
   class VectorNode extends BaseNode {
-    constructor(values, type) {
-      super();
-      this.x = new FloatNode(x);
-      this.y = new FloatNode(y);
+    constructor(values, type, isInternal = false) {
+      super(isInternal);
+      const componentVariants = { 
+        pos: ['x', 'y', 'z', 'w'],
+        col: ['r', 'g', 'b', 'a'],
+        uv: ['s', 't', 'p', 'q']
+      }
+      for (let variant in componentVariants) {
+        for (let i = 0; i < arguments[0].length; i++) {
+          let componentCollection = componentVariants[variant];
+          let component = componentCollection[i];
+          this[component] = new FloatNode(arguments[0][i], isInternal = true);
+        }
+      }
+      console.log(this);
       this.type = type;
     }
     
@@ -162,8 +181,8 @@ function shadergen(p5, fn) {
 
   // Function Call Nodes
   class FunctionCallNode extends BaseNode {
-    constructor(name, args, type) {
-      super();
+    constructor(name, args, type, isInternal = false) {
+      super(isInternal);
       this.name = name;
       this.args = args;
       this.type = type;
@@ -186,25 +205,25 @@ function shadergen(p5, fn) {
   
   // Variables and member variable nodes
   class VariableNode extends BaseNode {
-    constructor(name, type) {
-      super()
+    constructor(name, type, isInternal = false) {
+      super(isInternal)
       this.name = name;
       this.type = type;
       switch (type) {
         case 'float':
         break;
         case 'vec2':
-        this.addSwizzles('x', 'y')
+        this.addComponents('x', 'y')
         break;
         case 'vec3':
-        this.addSwizzles('x', 'y', 'z')
+        this.addComponents('x', 'y', 'z')
         break;
         case 'vec4':
-        this.addSwizzles('x', 'y', 'z', 'w');  
+        this.addComponents('x', 'y', 'z', 'w');  
         break;
       }
     }
-    addSwizzles() {
+    addComponents() {
       for (let name of arguments) {
         this[name] = new ComponentNode(this.name, name);
       }
@@ -215,8 +234,8 @@ function shadergen(p5, fn) {
   }
   
   class ComponentNode extends BaseNode {
-    constructor(parent, component) {
-      super();
+    constructor(parent, component, isInternal = false) {
+      super(isInternal);
       this.varName = parent;
       this.component = component;
       this.type = 'float';
@@ -228,8 +247,8 @@ function shadergen(p5, fn) {
 
   // Binary Operator Nodes
   class BinaryOperatorNode extends BaseNode {
-    constructor(a, b) {
-      super();
+    constructor(a, b, isInternal = false) {
+      super(isInternal);
       this.a = a;
       this.b = b;
       for (const param of arguments) {
@@ -348,7 +367,7 @@ function shadergen(p5, fn) {
         getWorldPosition: null
       }
       this.resetGLSLContext();
-      global.GLOBAL_SHADER = this;
+      GLOBAL_SHADER = this;
       this.generator = modifyFunction;
     }
     generate() {
@@ -371,14 +390,16 @@ function shadergen(p5, fn) {
       this.uniforms[name] = value;
       return new VariableNode(name, value.type);
     }
+    
     buildFunction(argumentName, argumentType, callback) {
       let functionArgument = new VariableNode(argumentName, argumentType);
-      const finalLine = callback(functionArgument).toGLSL(this.context);
+      const finalLine = callback(functionArgument).toGLSLBase(this.context);
       let codeLines = this.context.declarations.slice();
       codeLines.push(`\n${argumentName} = ${finalLine}; \nreturn ${argumentName};`)
       this.resetGLSLContext();
       return codeLines.join("\n");
     }
+
     getWorldPosition(func) {
       this.functions.getWorldPosition = this.buildFunction("pos", "vec3", func);
     }
@@ -388,16 +409,23 @@ function shadergen(p5, fn) {
   }
 
   // User functions
+  fn.getWorldPosition = function(func){
+    GLOBAL_SHADER.getWorldPosition(func)
+  }
+  fn.getFinalColor = function(func){
+    GLOBAL_SHADER.getFinalColor(func)
+  }
+  
   fn.createVector2 = function(x, y) {
-    return new VectorNode(x, y, 'vec2');
+    return new VectorNode([x, y], 'vec2');
   }
 
   fn.createVector3 = function(x, y, z) {
-    return new VectorNode(x, y, z, 'vec3');
+    return new VectorNode([x, y, z], 'vec3');
   }
 
   fn.createVector4 = function(x, y, z, w) {
-    return new VectorNode(x, y, z, w, 'vec4');
+    return new VectorNode([x, y, z], w, 'vec4');
   }
   
   fn.createFloat = function(x) {

From f513b05c7d421e6c92efa8239ddaddb664e51a91 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Mon, 17 Feb 2025 12:56:34 +0000
Subject: [PATCH 06/54] ComponentNode.toGLSL() currently broken. improving
 vector API.

---
 src/webgl/ShaderGen.js | 117 +++++++++++++++++++++++++----------------
 1 file changed, 73 insertions(+), 44 deletions(-)

diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index 89db1f3f61..5cc21b578a 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -6,15 +6,18 @@
 */
 
 function shadergen(p5, fn) {
-  const oldModify = p5.Shader.prototype.modify
   let GLOBAL_SHADER;
+  
+  const oldModify = p5.Shader.prototype.modify
+
   p5.Shader.prototype.modify = function(arg) {
     if (arg instanceof Function) {
       const program = new ShaderProgram(arg)
       const newArg = program.generate();
-      console.log(newArg)
+      console.log(newArg.vertex)
       return oldModify.call(this, newArg);
-    } else {
+    } 
+    else {
       return oldModify.call(this, arg)
     }
   }
@@ -27,17 +30,19 @@ function shadergen(p5, fn) {
       this.type = null;
       
       // For tracking recursion depth and creating temporary variables
-      this.isInternal = isInternal; // TODO: dont use temp nodes for internal nodes 
+      this.isInternal = isInternal;
       this.usedIn = [];
       this.dependsOn = [];
       this.srcLine = null;
-      if (!isInternal) {
+      
+      if (isInternal === false) {
         try {
           throw new Error("StackCapture");
         } catch (e) {
           const lines = e.stack.split("\n");
           console.log(lines)
-          this.srcLine = lines[3].trim();
+          let index = 5;
+          this.srcLine = lines[index].trim();
         }
       }
     }
@@ -66,7 +71,7 @@ function shadergen(p5, fn) {
         if (this.srcLine) {
           line += `\n// From ${this.srcLine}\n`;
         }
-        line += this.type + " " + this.temporaryVariable + " = " + this.toGLSLNoTemp(context) + ";";
+        line += this.type + " " + this.temporaryVariable + " = " + this.toGLSL(context) + ";";
         context.declarations.push(line);
       }
       return this.temporaryVariable;
@@ -116,7 +121,7 @@ function shadergen(p5, fn) {
     }
     toGLSL(context) {
       if (isShaderNode(this.x)) {
-        let code = this.x.toGLSL(context);
+        let code = this.x.toGLSLBase(context);
         return isIntNode(this.x.type) ? code : `int(${code})`;
       }
       else if (typeof this.x === "number") {
@@ -136,8 +141,7 @@ function shadergen(p5, fn) {
     }
     toGLSL(context) {
       if (isShaderNode(this.x)) {
-        let code = this.x.toGLSL(context);
-        console.log(code)
+        let code = this.x.toGLSLBase(context);
         return isFloatNode(this.x) ? code : `float(${code})`;
       }
       else if (typeof this.x === "number") {
@@ -153,29 +157,41 @@ function shadergen(p5, fn) {
   // that we don't actually need a Float Node for every component. They could be component node's instead? 
   // May need to then store the temporary variable name in this class.
 
-  // I think swizzles could be easy then
+  // This would be the next step before adding all swizzles 
 
   class VectorNode extends BaseNode {
     constructor(values, type, isInternal = false) {
       super(isInternal);
+
       const componentVariants = { 
         pos: ['x', 'y', 'z', 'w'],
         col: ['r', 'g', 'b', 'a'],
         uv: ['s', 't', 'p', 'q']
       }
       for (let variant in componentVariants) {
-        for (let i = 0; i < arguments[0].length; i++) {
+        for (let i = 0; i < values.length; i++) {
           let componentCollection = componentVariants[variant];
           let component = componentCollection[i];
-          this[component] = new FloatNode(arguments[0][i], isInternal = true);
+          this[component] = new ComponentNode(this, component, true);
+          // this[component] = new FloatNode(values[i], true);
         }
       }
-      console.log(this);
+
       this.type = type;
+      this.size = values.length; 
     }
     
     toGLSL(context) {
-      return `${this.type}(${this.x.toGLSLBase(context)}, ${this.y.toGLSLBase(context)})`
+      let glslArgs = ``;
+      const components = ['x', 'y', 'z', 'w'].slice(0, this.size);
+      
+      for (let i = 0; i < this.size; i++) {
+        const comma = i === this.size - 1 ? `` : `, `;
+        const component = components[i]
+        glslArgs += `${this[component].toGLSLBase(context)}${comma}`;
+      }
+      
+      return `${this.type}(${glslArgs})`;
     }
   }
 
@@ -187,17 +203,19 @@ function shadergen(p5, fn) {
       this.args = args;
       this.type = type;
     }
+
     deconstructArgs(context) {
       if (this.args.constructor === Array) {
-        let argsString = `${this.args[0].toGLSL(context)}`
+        let argsString = `${this.args[0].toGLSLBase(context)}`
         for (let arg of this.args.slice(1)) {
-          argsString += `, ${arg.toGLSL(context)}` 
+          argsString += `, ${arg.toGLSLBase(context)}` 
           return argsString;
         }
       } else {
-        return `${this.args.toGLSL(context)}`;
+        return `${this.args.toGLSLBase(context)}`;
       }
     }
+
     toGLSL(context) {
       return `${this.name}(${this.deconstructArgs(context)})`;
     }
@@ -213,19 +231,19 @@ function shadergen(p5, fn) {
         case 'float':
         break;
         case 'vec2':
-        this.addComponents('x', 'y')
+        this.addComponents(['x', 'y'])
         break;
         case 'vec3':
-        this.addComponents('x', 'y', 'z')
+        this.addComponents(['x', 'y', 'z'])
         break;
         case 'vec4':
-        this.addComponents('x', 'y', 'z', 'w');  
+        this.addComponents(['x', 'y', 'z', 'w']);  
         break;
       }
     }
-    addComponents() {
-      for (let name of arguments) {
-        this[name] = new ComponentNode(this.name, name);
+    addComponents(componentNames) {
+      for (let componentName of componentNames) {
+        this[componentName] = new ComponentNode(this, componentName, true);
       }
     }
     toGLSL(context) {
@@ -236,12 +254,14 @@ function shadergen(p5, fn) {
   class ComponentNode extends BaseNode {
     constructor(parent, component, isInternal = false) {
       super(isInternal);
-      this.varName = parent;
+      this.parent = parent;
       this.component = component;
       this.type = 'float';
     }
     toGLSL(context) {
-      return `${this.varName}.${this.component}`;
+      // CURRENTLY BROKEN:
+      const parentName = this.parent.toGLSL(context);
+      return `${parentName}.${this.component}`;
     }
   }
 
@@ -279,7 +299,10 @@ function shadergen(p5, fn) {
     }
 
     processOperand(context, operand) {
-      const code = operand.toGLSL(context);
+      const code = operand.toGLSLBase(context);
+      if (operand.temporaryVariable) {
+        return operand.temporaryVariable;
+      }
       if (this.type === 'float' && isIntNode(operand)) {
         return `float${code}`;
       }
@@ -291,7 +314,7 @@ function shadergen(p5, fn) {
     constructor(a, b) {
       super(a, b)
     }
-    toGLSLNoTemp(context) {
+    toGLSL(context) {
       return `(${this.processOperand(context, this.a)} * ${this.processOperand(context, this.b)})`;
     }
   }
@@ -300,7 +323,7 @@ function shadergen(p5, fn) {
     constructor(a, b) {
       super(a, b)
     }
-    toGLSLNoTemp(context) {
+    toGLSL(context) {
       return `(${this.processOperand(context, this.a)} / ${this.processOperand(context, this.b)})`;
     }
   }
@@ -309,7 +332,7 @@ function shadergen(p5, fn) {
     constructor(a, b) {
       super(a, b)
     }
-    toGLSLNoTemp(context) {
+    toGLSL(context) {
       return `(${this.processOperand(context, this.a)} + ${this.processOperand(context, this.b)})`;
     }
   }
@@ -318,7 +341,7 @@ function shadergen(p5, fn) {
     constructor(a, b) {
       super(a, b)
     }
-    toGLSLNoTemp(context) {
+    toGLSL(context) {
       return `(${this.processOperand(context, this.a)} - ${this.processOperand(context, this.b)})`;
     }
   }
@@ -328,32 +351,39 @@ function shadergen(p5, fn) {
     constructor(a, b) {
       super(a, b);
     }
-    toGLSLNoTemp(context) {
+    toGLSL(context) {
       // Switch on type between % or mod()
       if (isVectorNode(this) || isFloatNode(this)) {
-        return `mod(${this.a.toGLSL(context)}, ${this.b.toGLSL(context)})`;
+        return `mod(${this.a.toGLSLBase(context)}, ${this.b.toGLSLBase(context)})`;
       }
       return `(${this.processOperand(context, this.a)} % ${this.processOperand(context, this.b)})`;
     }
   }
 
   // Helper functions
-  function isShaderNode(value) {  return (value instanceof BaseNode); }
+  function isShaderNode(node) {  
+    return (node instanceof BaseNode); 
+  }
+
+  function isIntNode(node) { 
+    return (isShaderNode(node) && (node.type === 'int')); 
+  }
 
-  function isIntNode(value) { 
-    return (isShaderNode(value) && (value.type === 'int')); 
+  function isFloatNode(node) { 
+    return (isShaderNode(node) && (node.type === 'float')); 
   }
 
-  function isFloatNode(value) { 
-    return (isShaderNode(value) && (value.type === 'float')); 
+  function isVectorNode(node) { 
+    return (isShaderNode(node) && (node.type === 'vec2'|| node.type === 'vec3' || node.type === 'vec4')); 
   }
 
-  function isVectorNode(value) { 
-    return (isShaderNode(value) && (value.type === 'vec2'|| value.type === 'vec3' || value.type === 'vec4')); 
+  function isBinaryOperatorNode(node) {
+    return (node instanceof BinaryOperatorNode);
   }
 
   function isVariableNode(node) { 
-    return (node instanceof VariableNode || node instanceof ComponentNode); 
+    console.log(node.temporaryVariable);
+    return (node instanceof VariableNode || node instanceof ComponentNode || typeof(node.temporaryVariable) != 'undefined'); 
   }
 
   // Shader program
@@ -364,7 +394,6 @@ function shadergen(p5, fn) {
       this.uniforms = {
       }
       this.functions = {
-        getWorldPosition: null
       }
       this.resetGLSLContext();
       GLOBAL_SHADER = this;
@@ -392,7 +421,7 @@ function shadergen(p5, fn) {
     }
     
     buildFunction(argumentName, argumentType, callback) {
-      let functionArgument = new VariableNode(argumentName, argumentType);
+      let functionArgument = new VariableNode(argumentName, argumentType, true);
       const finalLine = callback(functionArgument).toGLSLBase(this.context);
       let codeLines = this.context.declarations.slice();
       codeLines.push(`\n${argumentName} = ${finalLine}; \nreturn ${argumentName};`)
@@ -425,7 +454,7 @@ function shadergen(p5, fn) {
   }
 
   fn.createVector4 = function(x, y, z, w) {
-    return new VectorNode([x, y, z], w, 'vec4');
+    return new VectorNode([x, y, z, w], 'vec4');
   }
   
   fn.createFloat = function(x) {

From 9ef9cc7f7a4a2d177602247a68f79097db92b8b7 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Mon, 17 Feb 2025 12:56:46 +0000
Subject: [PATCH 07/54] updating sketch

---
 preview/global/sketch.js | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/preview/global/sketch.js b/preview/global/sketch.js
index 927489719f..e6be4a4221 100644
--- a/preview/global/sketch.js
+++ b/preview/global/sketch.js
@@ -1,14 +1,21 @@
 let myShader;
+p5.disableFriendlyErrors = true;
 function setup(){
   createCanvas(windowWidth, windowHeight, WEBGL);
 
   // Create and use the custom shader.
   myShader = baseMaterialShader().modify(
     () => {
-      const x = createVector3(1, 2)
-
       getWorldPosition((pos) => {
-        pos = pos.add(x);
+        let a = createVector3(1, 2, 3);
+        let b = createVector3(3,4,5);
+        a = a.add(b);
+
+        let c = a.add(b);
+        
+        c.x = b.x.add(1).sin();
+        pos = pos.add(c);
+
         return pos;
       })
     }

From 59b443efde73253751072ffd9eb54763304fdc02 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Mon, 17 Feb 2025 12:56:46 +0000
Subject: [PATCH 08/54] put temporary fix in comment

---
 preview/global/sketch.js | 13 ++++++++++---
 src/webgl/ShaderGen.js   |  3 ++-
 2 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/preview/global/sketch.js b/preview/global/sketch.js
index 927489719f..e6be4a4221 100644
--- a/preview/global/sketch.js
+++ b/preview/global/sketch.js
@@ -1,14 +1,21 @@
 let myShader;
+p5.disableFriendlyErrors = true;
 function setup(){
   createCanvas(windowWidth, windowHeight, WEBGL);
 
   // Create and use the custom shader.
   myShader = baseMaterialShader().modify(
     () => {
-      const x = createVector3(1, 2)
-
       getWorldPosition((pos) => {
-        pos = pos.add(x);
+        let a = createVector3(1, 2, 3);
+        let b = createVector3(3,4,5);
+        a = a.add(b);
+
+        let c = a.add(b);
+        
+        c.x = b.x.add(1).sin();
+        pos = pos.add(c);
+
         return pos;
       })
     }
diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index 5cc21b578a..195e34d13c 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -260,7 +260,8 @@ function shadergen(p5, fn) {
     }
     toGLSL(context) {
       // CURRENTLY BROKEN:
-      const parentName = this.parent.toGLSL(context);
+      const parentName = this.parent.toGLSLBase(context);
+      // const parentName = this.parent.temporaryVariable ? this.parent.temporaryVariable : this.parent.name; 
       return `${parentName}.${this.component}`;
     }
   }

From 17fb0a6e35cf76c98faf7ef64f53fdb61637459d Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Mon, 17 Feb 2025 20:29:25 +0000
Subject: [PATCH 09/54] adding proxy for vectors to make swizzling simpler

---
 preview/global/sketch.js |  11 +++-
 src/webgl/ShaderGen.js   | 125 +++++++++++++++++++++++++++++----------
 2 files changed, 102 insertions(+), 34 deletions(-)

diff --git a/preview/global/sketch.js b/preview/global/sketch.js
index e6be4a4221..d5c9e20fff 100644
--- a/preview/global/sketch.js
+++ b/preview/global/sketch.js
@@ -6,14 +6,19 @@ function setup(){
   // Create and use the custom shader.
   myShader = baseMaterialShader().modify(
     () => {
+      const offset = uniform('offset', createVector3())
       getWorldPosition((pos) => {
         let a = createVector3(1, 2, 3);
-        let b = createVector3(3,4,5);
+        let b = createVector3(3, 4, 5);
         a = a.add(b);
 
         let c = a.add(b);
-        
-        c.x = b.x.add(1).sin();
+        c = c.add(offset);
+        c.x = b.x.add(1);
+
+
+        console.log("TEST:")
+        console.log(c.x);
         pos = pos.add(c);
 
         return pos;
diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index 195e34d13c..9429707151 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -40,7 +40,6 @@ function shadergen(p5, fn) {
           throw new Error("StackCapture");
         } catch (e) {
           const lines = e.stack.split("\n");
-          console.log(lines)
           let index = 5;
           this.srcLine = lines[index].trim();
         }
@@ -159,10 +158,43 @@ function shadergen(p5, fn) {
 
   // This would be the next step before adding all swizzles 
 
+  // I think it is also possible to make a proxy for vector nodes to check if the user accesses  or sets any 
+  // property .xyz / .rgb /.zxx etc as then we can automatically assign swizzles and only define .xyzw on the actual 
+  // object.
+
+  // Ultimately vectors are the focus of & most complex type in glsl (before we add matrices...) which justifies the complexity
+  // of this class.
+
+  // I am interested in the 'prop' value of get and set then
+
+  const VectorNodeHandler = {
+    swizzles: [
+      ['x', 'y', 'z', 'w'],
+      ['r', 'b', 'g', 'a'],
+      ['s', 't', 'p', 'q'],
+    ],
+    get(target, prop, receiver) {
+      // if (!this.isInternal) {
+        // console.log("TARGET: ", target);
+        // console.log("PROP: ", prop);
+        // console.log("RECEIVER: ", receiver);
+      // }
+      return Reflect.get(...arguments);
+    },
+    set(obj, prop, receiver) {
+      // if (!this.isInternal) {
+      //   console.log("OBJ: ", obj);
+      //   console.log("PROP: ", prop);
+      //   console.log("RECEIVER: ", receiver);
+      // }
+      obj[prop] = receiver;
+      return true;
+    }
+  }
+
   class VectorNode extends BaseNode {
     constructor(values, type, isInternal = false) {
       super(isInternal);
-
       const componentVariants = { 
         pos: ['x', 'y', 'z', 'w'],
         col: ['r', 'g', 'b', 'a'],
@@ -172,8 +204,8 @@ function shadergen(p5, fn) {
         for (let i = 0; i < values.length; i++) {
           let componentCollection = componentVariants[variant];
           let component = componentCollection[i];
-          this[component] = new ComponentNode(this, component, true);
-          // this[component] = new FloatNode(values[i], true);
+          // this[component] = new ComponentNode(this, component, true);
+          this[component] = new FloatNode(values[i], true);
         }
       }
 
@@ -182,12 +214,13 @@ function shadergen(p5, fn) {
     }
     
     toGLSL(context) {
+      console.log(this)
       let glslArgs = ``;
       const components = ['x', 'y', 'z', 'w'].slice(0, this.size);
       
       for (let i = 0; i < this.size; i++) {
         const comma = i === this.size - 1 ? `` : `, `;
-        const component = components[i]
+        const component = components[i];
         glslArgs += `${this[component].toGLSLBase(context)}${comma}`;
       }
       
@@ -202,6 +235,11 @@ function shadergen(p5, fn) {
       this.name = name;
       this.args = args;
       this.type = type;
+
+      // TODO:
+      this.argumentTypes = {
+
+      };
     }
 
     deconstructArgs(context) {
@@ -227,19 +265,29 @@ function shadergen(p5, fn) {
       super(isInternal)
       this.name = name;
       this.type = type;
-      switch (type) {
-        case 'float':
-        break;
-        case 'vec2':
-        this.addComponents(['x', 'y'])
-        break;
-        case 'vec3':
-        this.addComponents(['x', 'y', 'z'])
-        break;
-        case 'vec4':
-        this.addComponents(['x', 'y', 'z', 'w']);  
-        break;
-      }
+      this.addComponents(
+        (() => {
+          switch (type) {
+            case 'vec2': return ['x', 'y'];
+            case 'vec3': return ['x', 'y', 'z'];
+            case 'vec4': return ['x', 'y', 'z', 'w'];
+            default: return [];
+          }
+        })()
+      );
+      // switch (type) {
+      //   case 'float':
+      //   break;
+      //   case 'vec2':
+      //   this.addComponents(['x', 'y'])
+      //   break;
+      //   case 'vec3':
+      //   this.addComponents(['x', 'y', 'z'])
+      //   break;
+      //   case 'vec4':
+      //   this.addComponents(['x', 'y', 'z', 'w']);  
+      //   break;
+      // }
     }
     addComponents(componentNames) {
       for (let componentName of componentNames) {
@@ -260,8 +308,8 @@ function shadergen(p5, fn) {
     }
     toGLSL(context) {
       // CURRENTLY BROKEN:
-      const parentName = this.parent.toGLSLBase(context);
-      // const parentName = this.parent.temporaryVariable ? this.parent.temporaryVariable : this.parent.name; 
+      // const parentName = this.parent.toGLSLBase(context);
+      const parentName = this.parent.temporaryVariable ? this.parent.temporaryVariable : this.parent.name; 
       return `${parentName}.${this.component}`;
     }
   }
@@ -299,13 +347,16 @@ function shadergen(p5, fn) {
       }
     }
 
+    // TODO: change order of parameters
     processOperand(context, operand) {
-      const code = operand.toGLSLBase(context);
-      if (operand.temporaryVariable) {
-        return operand.temporaryVariable;
+      if (operand.temporaryVariable) { return operand.temporaryVariable; }
+      let code = operand.toGLSLBase(context);      
+      if (isBinaryOperatorNode(operand)) {
+        console.log(operand)
+        code = `(${code})`;
       }
       if (this.type === 'float' && isIntNode(operand)) {
-        return `float${code}`;
+        code = `float(${code})`;
       }
       return code;
     }
@@ -316,7 +367,7 @@ function shadergen(p5, fn) {
       super(a, b)
     }
     toGLSL(context) {
-      return `(${this.processOperand(context, this.a)} * ${this.processOperand(context, this.b)})`;
+      return `${this.processOperand(context, this.a)} * ${this.processOperand(context, this.b)}`;
     }
   }
 
@@ -325,7 +376,7 @@ function shadergen(p5, fn) {
       super(a, b)
     }
     toGLSL(context) {
-      return `(${this.processOperand(context, this.a)} / ${this.processOperand(context, this.b)})`;
+      return `${this.processOperand(context, this.a)} / ${this.processOperand(context, this.b)}`;
     }
   }
 
@@ -334,7 +385,7 @@ function shadergen(p5, fn) {
       super(a, b)
     }
     toGLSL(context) {
-      return `(${this.processOperand(context, this.a)} + ${this.processOperand(context, this.b)})`;
+      return `${this.processOperand(context, this.a)} + ${this.processOperand(context, this.b)}`;
     }
   }
 
@@ -343,7 +394,7 @@ function shadergen(p5, fn) {
       super(a, b)
     }
     toGLSL(context) {
-      return `(${this.processOperand(context, this.a)} - ${this.processOperand(context, this.b)})`;
+      return `${this.processOperand(context, this.a)} - ${this.processOperand(context, this.b)}`;
     }
   }
 
@@ -357,7 +408,7 @@ function shadergen(p5, fn) {
       if (isVectorNode(this) || isFloatNode(this)) {
         return `mod(${this.a.toGLSLBase(context)}, ${this.b.toGLSLBase(context)})`;
       }
-      return `(${this.processOperand(context, this.a)} % ${this.processOperand(context, this.b)})`;
+      return `${this.processOperand(context, this.a)} % ${this.processOperand(context, this.b)}`;
     }
   }
 
@@ -383,7 +434,6 @@ function shadergen(p5, fn) {
   }
 
   function isVariableNode(node) { 
-    console.log(node.temporaryVariable);
     return (node instanceof VariableNode || node instanceof ComponentNode || typeof(node.temporaryVariable) != 'undefined'); 
   }
 
@@ -416,6 +466,16 @@ function shadergen(p5, fn) {
         declarations: [],
       }
     }
+    // TODO:
+    uniformInt() {
+      return
+    }
+    uniformFloat() {
+      return
+    }
+    uniformVector2() {
+      return
+    }
     uniform(name, value) {
       this.uniforms[name] = value;
       return new VariableNode(name, value.type);
@@ -447,14 +507,17 @@ function shadergen(p5, fn) {
   }
   
   fn.createVector2 = function(x, y) {
-    return new VectorNode([x, y], 'vec2');
+    return new Proxy(new VectorNode([x, y], 'vec2'), VectorNodeHandler);
   }
 
   fn.createVector3 = function(x, y, z) {
+    return new Proxy(new VectorNode([x, y, z], 'vec3'), VectorNodeHandler);
+
     return new VectorNode([x, y, z], 'vec3');
   }
 
   fn.createVector4 = function(x, y, z, w) {
+    return new Proxy(new VectorNode([x, y, z, w], 'vec4'), VectorNodeHandler);
     return new VectorNode([x, y, z, w], 'vec4');
   }
   

From a1b51b08f2f69b97ac74f9c6c136e991d7ffc419 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Tue, 18 Feb 2025 16:11:32 +0000
Subject: [PATCH 10/54] add estraverse

---
 package-lock.json | 2 +-
 package.json      | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/package-lock.json b/package-lock.json
index edccbe8993..cfe61bad9d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,6 +13,7 @@
         "acorn": "^8.12.1",
         "acorn-walk": "^8.3.4",
         "colorjs.io": "^0.5.2",
+        "estraverse": "^5.3.0",
         "file-saver": "^1.3.8",
         "gifenc": "^1.0.3",
         "i18next": "^19.0.2",
@@ -4607,7 +4608,6 @@
       "version": "5.3.0",
       "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
       "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
-      "dev": true,
       "license": "BSD-2-Clause",
       "engines": {
         "node": ">=4.0"
diff --git a/package.json b/package.json
index e34a8ef5c7..dd9b44a6c0 100644
--- a/package.json
+++ b/package.json
@@ -28,6 +28,7 @@
     "acorn": "^8.12.1",
     "acorn-walk": "^8.3.4",
     "colorjs.io": "^0.5.2",
+    "estraverse": "^5.3.0",
     "file-saver": "^1.3.8",
     "gifenc": "^1.0.3",
     "i18next": "^19.0.2",

From fb9fd94d790fe9225c3f79e9d32824c28338a8e3 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Tue, 18 Feb 2025 16:15:45 +0000
Subject: [PATCH 11/54] add AST modules

---
 src/webgl/ShaderGen.js | 19 +++++++++++++++----
 1 file changed, 15 insertions(+), 4 deletions(-)

diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index 9429707151..1e074b38d2 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -5,17 +5,28 @@
 * @requires core
 */
 
+import { parse } from 'acorn';
+import { simple as walk } from 'acorn-walk';
+import estraverse from 'estraverse';
+
 function shadergen(p5, fn) {
   let GLOBAL_SHADER;
   
   const oldModify = p5.Shader.prototype.modify
 
   p5.Shader.prototype.modify = function(arg) {
+
     if (arg instanceof Function) {
-      const program = new ShaderProgram(arg)
-      const newArg = program.generate();
-      console.log(newArg.vertex)
-      return oldModify.call(this, newArg);
+      const fnSource = arg.toString()
+      const ast = parse(fnSource, { ecmaVersion: 2021, locations: true });
+      const result = estraverse.traverse(ast, {
+        enter: (node) => console.log(node),
+      })
+
+      // const program = new ShaderProgram(arg)
+      // const newArg = program.generate();
+      // console.log(newArg.vertex)
+      // return oldModify.call(this, newArg);
     } 
     else {
       return oldModify.call(this, arg)

From e8435ceddc4c8737c8958a21d07de6bd5f321cf9 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Tue, 18 Feb 2025 16:23:12 +0000
Subject: [PATCH 12/54] remove walk

---
 src/webgl/ShaderGen.js | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index 1e074b38d2..9d514edf41 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -6,7 +6,6 @@
 */
 
 import { parse } from 'acorn';
-import { simple as walk } from 'acorn-walk';
 import estraverse from 'estraverse';
 
 function shadergen(p5, fn) {

From aaa8aaceaee1e6678588ae5f392e5e8d8780649e Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Tue, 18 Feb 2025 16:25:08 +0000
Subject: [PATCH 13/54] refactor binary operator nodes to remove redundant
 classes and repitition

---
 src/webgl/ShaderGen.js | 55 ++++++++++--------------------------------
 1 file changed, 13 insertions(+), 42 deletions(-)

diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index 9d514edf41..1dd58745c2 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -51,6 +51,7 @@ function shadergen(p5, fn) {
         } catch (e) {
           const lines = e.stack.split("\n");
           let index = 5;
+          if (isBinaryOperatorNode(this)) { index--; };
           this.srcLine = lines[index].trim();
         }
       }
@@ -87,10 +88,10 @@ function shadergen(p5, fn) {
     };
     
     // TODO: Add more operations here
-    add(other)  { return new AdditionNode(this, this.enforceType(other)); }
-    sub(other)  { return new SubtractionNode(this, this.enforceType(other)); }
-    mult(other) { return new MultiplicationNode(this, this.enforceType(other)); }
-    div(other)  { return new DivisionNode(this, this.enforceType(other)); }
+    add(other)  { return new BinaryOperatorNode(this, this.enforceType(other), '+'); }
+    sub(other)  { return new BinaryOperatorNode(this, this.enforceType(other), '-'); }
+    mult(other) { return new BinaryOperatorNode(this, this.enforceType(other), '*'); }
+    div(other)  { return new BinaryOperatorNode(this, this.enforceType(other), '/'); }
     mod(other)  { return new ModulusNode(this, this.enforceType(other)); }
     sin()       { return new FunctionCallNode('sin', this, 'float'); }
     cos()       { return new FunctionCallNode('cos', this, 'float'); }
@@ -326,12 +327,13 @@ function shadergen(p5, fn) {
 
   // Binary Operator Nodes
   class BinaryOperatorNode extends BaseNode {
-    constructor(a, b, isInternal = false) {
+    constructor(a, b, operator, isInternal = false) {
       super(isInternal);
+      this.op = operator;
       this.a = a;
       this.b = b;
-      for (const param of arguments) {
-        param.usedIn.push(this);
+      for (const operand of [a, b]) {
+        operand.usedIn.push(this);
       }
       this.type = this.determineType();
     }
@@ -361,8 +363,7 @@ function shadergen(p5, fn) {
     processOperand(context, operand) {
       if (operand.temporaryVariable) { return operand.temporaryVariable; }
       let code = operand.toGLSLBase(context);      
-      if (isBinaryOperatorNode(operand)) {
-        console.log(operand)
+      if (isBinaryOperatorNode(operand) && !operand.temporaryVariable) {
         code = `(${code})`;
       }
       if (this.type === 'float' && isIntNode(operand)) {
@@ -370,41 +371,11 @@ function shadergen(p5, fn) {
       }
       return code;
     }
-  }
 
-  class MultiplicationNode extends BinaryOperatorNode {
-    constructor(a, b) {
-      super(a, b)
-    }
-    toGLSL(context) {
-      return `${this.processOperand(context, this.a)} * ${this.processOperand(context, this.b)}`;
-    }
-  }
-
-  class DivisionNode extends BinaryOperatorNode {
-    constructor(a, b) {
-      super(a, b)
-    }
-    toGLSL(context) {
-      return `${this.processOperand(context, this.a)} / ${this.processOperand(context, this.b)}`;
-    }
-  }
-
-  class AdditionNode extends BinaryOperatorNode {
-    constructor(a, b) {
-      super(a, b)
-    }
-    toGLSL(context) {
-      return `${this.processOperand(context, this.a)} + ${this.processOperand(context, this.b)}`;
-    }
-  }
-
-  class SubtractionNode extends BinaryOperatorNode {
-    constructor(a, b) {
-      super(a, b)
-    }
     toGLSL(context) {
-      return `${this.processOperand(context, this.a)} - ${this.processOperand(context, this.b)}`;
+      const a = this.processOperand(this.a, context);
+      const b = this.processOperand(this.b, context);
+      return `${a} ${this.op} ${b}`;
     }
   }
 

From 057dc0eef97df7c2210bfeb288dad8e6d46aec18 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Tue, 18 Feb 2025 16:26:10 +0000
Subject: [PATCH 14/54] add type specific uniforms

---
 src/webgl/ShaderGen.js | 39 ++++++++++++++++++++++++++++++---------
 1 file changed, 30 insertions(+), 9 deletions(-)

diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index 1dd58745c2..c70ff6aa96 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -424,6 +424,12 @@ function shadergen(p5, fn) {
   class ShaderProgram {
     constructor(modifyFunction) {
       this.uniforms = {
+        int: {},
+        float: {},
+        vec2: {},
+        vec3: {},
+        vec4: {},
+        texture: {},
       }
       this.functions = {
       }
@@ -448,18 +454,33 @@ function shadergen(p5, fn) {
       }
     }
     // TODO:
-    uniformInt() {
-      return
+    uniformInt(name, defaultValue) {
+      this.uniforms.int[name] = defaultValue;
+      return new VariableNode(name, 'int');
     }
-    uniformFloat() {
-      return
+    uniformFloat(name, defaultValue) {
+      this.uniforms.float[name] = defaultValue;
+      return new VariableNode(name, 'float');
     }
-    uniformVector2() {
-      return
+    uniformVector2(name, defaultValue) {
+      this.uniforms.vec2[name] = defaultValue;
+      return new VariableNode(name, 'vec2');
     }
-    uniform(name, value) {
-      this.uniforms[name] = value;
-      return new VariableNode(name, value.type);
+    uniformVector2(name, defaultValue) {
+      this.uniforms.vec3[name] = defaultValue;
+      return new VariableNode(name, 'vec3');
+    }
+    uniformVector2(name, defaultValue) {
+      this.uniforms.vec4[name] = defaultValue;
+      return new VariableNode(name, 'vec4');
+    }
+    uniformTexture(name, defaultValue) {
+      this.uniforms.texture[name] = defaultValue;
+      return new VariableNode(name, 'vec4');
+    }
+    uniform(name, defaultValue) {
+      this.uniforms[name] = defaultValue;
+      return new VariableNode(name, defaultValue.type);
     }
     
     buildFunction(argumentName, argumentType, callback) {

From 23220797ea7651e31b379619184f43f342ac8771 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Tue, 18 Feb 2025 16:26:40 +0000
Subject: [PATCH 15/54] swap parameter's order in processOperand

---
 src/webgl/ShaderGen.js | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index c70ff6aa96..5ebc068f03 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -359,8 +359,7 @@ function shadergen(p5, fn) {
       }
     }
 
-    // TODO: change order of parameters
-    processOperand(context, operand) {
+    processOperand(operand, context) {
       if (operand.temporaryVariable) { return operand.temporaryVariable; }
       let code = operand.toGLSLBase(context);      
       if (isBinaryOperatorNode(operand) && !operand.temporaryVariable) {

From 95d9da320a38784f2ab22fac0e0d985613570a0e Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Tue, 18 Feb 2025 16:27:03 +0000
Subject: [PATCH 16/54] remove comment

---
 src/webgl/ShaderGen.js | 13 -------------
 1 file changed, 13 deletions(-)

diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index 5ebc068f03..746ece0f05 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -286,19 +286,6 @@ function shadergen(p5, fn) {
           }
         })()
       );
-      // switch (type) {
-      //   case 'float':
-      //   break;
-      //   case 'vec2':
-      //   this.addComponents(['x', 'y'])
-      //   break;
-      //   case 'vec3':
-      //   this.addComponents(['x', 'y', 'z'])
-      //   break;
-      //   case 'vec4':
-      //   this.addComponents(['x', 'y', 'z', 'w']);  
-      //   break;
-      // }
     }
     addComponents(componentNames) {
       for (let componentName of componentNames) {

From d212480acd23c7d7a2beb59cc541ee6ebdd32c89 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Tue, 18 Feb 2025 16:30:01 +0000
Subject: [PATCH 17/54] change temporary variable logic (testing)

---
 src/webgl/ShaderGen.js | 57 ++++++++++++++++++++++--------------------
 1 file changed, 30 insertions(+), 27 deletions(-)

diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index 746ece0f05..d382cef8a0 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -67,12 +67,26 @@ function shadergen(p5, fn) {
       }
     }
 
+    // useTempVar() {
+    //   if (isBinaryOperatorNode(this)) {
+    //     console.log(this.a);
+    //     console.log(this.b);
+    //     return false; 
+    //   }
+    //   if (this.isInternal) { return false; }
+    //   if (isVariableNode(this)) { return false; }
+    //   if (isVectorNode(this)) { return true; }
+    //   return (this.usedIn.length > 1);
+    // };
+    
     useTempVar() {
-      if (this.isInternal) { return false; }
-      if (isVariableNode(this)) { return false; }
-      if (isVectorNode(this)) { return true; }
-      return (this.usedIn.length > 1);
-    };
+      if (this.isInternal || isVariableNode(this)) { return false; }
+      let score = 0;
+      score += isBinaryOperatorNode(this);
+      score += isVectorNode(this) * 2;
+      score += this.usedIn.length;
+      return score > 3;
+    }
     
     getTemp(context) {
       if (!this.temporaryVariable) {
@@ -206,35 +220,24 @@ function shadergen(p5, fn) {
   class VectorNode extends BaseNode {
     constructor(values, type, isInternal = false) {
       super(isInternal);
-      const componentVariants = { 
-        pos: ['x', 'y', 'z', 'w'],
-        col: ['r', 'g', 'b', 'a'],
-        uv: ['s', 't', 'p', 'q']
-      }
-      for (let variant in componentVariants) {
-        for (let i = 0; i < values.length; i++) {
-          let componentCollection = componentVariants[variant];
-          let component = componentCollection[i];
-          // this[component] = new ComponentNode(this, component, true);
-          this[component] = new FloatNode(values[i], true);
-        }
-      }
+
+      this.components = ['x', 'y', 'z', 'w'].slice(0, values.length);
+      this.components.forEach((component, i) => {
+        this[component] = new FloatNode(values[i], true);
+        // this[component] = new ComponentNode(this, component, true);
+      });
 
       this.type = type;
-      this.size = values.length; 
     }
     
     toGLSL(context) {
-      console.log(this)
       let glslArgs = ``;
-      const components = ['x', 'y', 'z', 'w'].slice(0, this.size);
-      
-      for (let i = 0; i < this.size; i++) {
-        const comma = i === this.size - 1 ? `` : `, `;
-        const component = components[i];
+
+      this.components.forEach((component, i) => {
+        const comma = i === this.components.length - 1 ? `` : `, `;
         glslArgs += `${this[component].toGLSLBase(context)}${comma}`;
-      }
-      
+      })
+
       return `${this.type}(${glslArgs})`;
     }
   }

From 1b7560867aab496a12e1f484fa5c805696ac8dc1 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Tue, 18 Feb 2025 16:30:29 +0000
Subject: [PATCH 18/54] Start working on Proxy for swizzles

---
 src/webgl/ShaderGen.js | 25 ++++++++++++-------------
 1 file changed, 12 insertions(+), 13 deletions(-)

diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index d382cef8a0..76f982cc8c 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -199,21 +199,20 @@ function shadergen(p5, fn) {
       ['s', 't', 'p', 'q'],
     ],
     get(target, prop, receiver) {
-      // if (!this.isInternal) {
-        // console.log("TARGET: ", target);
-        // console.log("PROP: ", prop);
-        // console.log("RECEIVER: ", receiver);
-      // }
-      return Reflect.get(...arguments);
+      if (prop in target) {
+         return Reflect.get(target, prop, receiver); 
+      } else {
+        console.log(prop);
+      }
+      let modifiedProp;
+      return Reflect.get(target, modifiedProp, receiver);
     },
     set(obj, prop, receiver) {
-      // if (!this.isInternal) {
-      //   console.log("OBJ: ", obj);
-      //   console.log("PROP: ", prop);
-      //   console.log("RECEIVER: ", receiver);
-      // }
-      obj[prop] = receiver;
-      return true;
+      console.log("OBJect:",obj,"PROPERTY", prop, "RECEIVER", receiver);
+
+      if (prop in obj) { return Reflect.set(...arguments); }
+      let modifiedProp;
+      return Reflect.set(obj, modifiedProp, receiver);
     }
   }
 

From 9813cb5a9a2802a245b5aa5e560fd732f557ba08 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Tue, 18 Feb 2025 16:30:49 +0000
Subject: [PATCH 19/54] Whitespace

---
 src/webgl/ShaderGen.js | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index 76f982cc8c..feeb1723b5 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -143,6 +143,7 @@ function shadergen(p5, fn) {
       this.x = x;
       this.type = 'int';
     }
+
     toGLSL(context) {
       if (isShaderNode(this.x)) {
         let code = this.x.toGLSLBase(context);
@@ -163,6 +164,7 @@ function shadergen(p5, fn) {
       this.x = x;
       this.type = 'float';
     }
+
     toGLSL(context) {
       if (isShaderNode(this.x)) {
         let code = this.x.toGLSLBase(context);
@@ -177,6 +179,7 @@ function shadergen(p5, fn) {
     }
   }
   
+  // TODO:
   // There is a possibility that since we *always* use a temporary variable for vectors
   // that we don't actually need a Float Node for every component. They could be component node's instead? 
   // May need to then store the temporary variable name in this class.
@@ -289,11 +292,13 @@ function shadergen(p5, fn) {
         })()
       );
     }
+
     addComponents(componentNames) {
       for (let componentName of componentNames) {
         this[componentName] = new ComponentNode(this, componentName, true);
       }
     }
+
     toGLSL(context) {
       return `${this.name}`;
     }
@@ -502,7 +507,6 @@ function shadergen(p5, fn) {
 
   fn.createVector3 = function(x, y, z) {
     return new Proxy(new VectorNode([x, y, z], 'vec3'), VectorNodeHandler);
-
     return new VectorNode([x, y, z], 'vec3');
   }
 

From 9ca999eeefd52eeb726ae876304be112e76f0f06 Mon Sep 17 00:00:00 2001
From: lp407 <lukeplowden@gmail.com>
Date: Thu, 20 Feb 2025 17:24:55 +0000
Subject: [PATCH 20/54] add escodegen, remove estree-walker

---
 package-lock.json | 53 +++++++++++++++++++++++++++++------------------
 package.json      |  2 +-
 2 files changed, 34 insertions(+), 21 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index cfe61bad9d..da6f5826f7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,7 +13,7 @@
         "acorn": "^8.12.1",
         "acorn-walk": "^8.3.4",
         "colorjs.io": "^0.5.2",
-        "estraverse": "^5.3.0",
+        "escodegen": "^2.1.0",
         "file-saver": "^1.3.8",
         "gifenc": "^1.0.3",
         "i18next": "^19.0.2",
@@ -1426,6 +1426,12 @@
         }
       }
     },
+    "node_modules/@rollup/plugin-commonjs/node_modules/estree-walker": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+      "dev": true
+    },
     "node_modules/@rollup/plugin-json": {
       "version": "6.1.0",
       "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz",
@@ -1540,6 +1546,12 @@
         }
       }
     },
+    "node_modules/@rollup/pluginutils/node_modules/estree-walker": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+      "dev": true
+    },
     "node_modules/@rollup/rollup-android-arm-eabi": {
       "version": "4.34.0",
       "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.0.tgz",
@@ -2100,16 +2112,6 @@
         }
       }
     },
-    "node_modules/@vitest/mocker/node_modules/estree-walker": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
-      "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/estree": "^1.0.0"
-      }
-    },
     "node_modules/@vitest/pretty-format": {
       "version": "2.1.8",
       "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.8.tgz",
@@ -2195,6 +2197,13 @@
         "source-map-js": "^1.2.0"
       }
     },
+    "node_modules/@vue/compiler-core/node_modules/estree-walker": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+      "dev": true,
+      "optional": true
+    },
     "node_modules/@vue/compiler-dom": {
       "version": "3.5.13",
       "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz",
@@ -2226,6 +2235,13 @@
         "source-map-js": "^1.2.0"
       }
     },
+    "node_modules/@vue/compiler-sfc/node_modules/estree-walker": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+      "dev": true,
+      "optional": true
+    },
     "node_modules/@vue/compiler-ssr": {
       "version": "3.5.13",
       "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz",
@@ -4388,8 +4404,6 @@
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
       "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
-      "dev": true,
-      "license": "BSD-2-Clause",
       "dependencies": {
         "esprima": "^4.0.1",
         "estraverse": "^5.2.0",
@@ -4410,7 +4424,6 @@
       "version": "0.6.1",
       "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
       "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-      "dev": true,
       "license": "BSD-3-Clause",
       "optional": true,
       "engines": {
@@ -4568,7 +4581,6 @@
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
       "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
-      "dev": true,
       "license": "BSD-2-Clause",
       "bin": {
         "esparse": "bin/esparse.js",
@@ -4614,17 +4626,18 @@
       }
     },
     "node_modules/estree-walker": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
-      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+      "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
       "dev": true,
-      "license": "MIT"
+      "dependencies": {
+        "@types/estree": "^1.0.0"
+      }
     },
     "node_modules/esutils": {
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
       "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
-      "dev": true,
       "license": "BSD-2-Clause",
       "engines": {
         "node": ">=0.10.0"
diff --git a/package.json b/package.json
index dd9b44a6c0..90f53154ff 100644
--- a/package.json
+++ b/package.json
@@ -28,7 +28,7 @@
     "acorn": "^8.12.1",
     "acorn-walk": "^8.3.4",
     "colorjs.io": "^0.5.2",
-    "estraverse": "^5.3.0",
+    "escodegen": "^2.1.0",
     "file-saver": "^1.3.8",
     "gifenc": "^1.0.3",
     "i18next": "^19.0.2",

From 2d79ac11c23cc08e4081b438a5f9935ebd31ed44 Mon Sep 17 00:00:00 2001
From: lp407 <lukeplowden@gmail.com>
Date: Thu, 20 Feb 2025 17:26:07 +0000
Subject: [PATCH 21/54] first AST conversion (binary operators partly working)

---
 preview/global/sketch.js | 63 ++++++++++++++++++----------
 src/webgl/ShaderGen.js   | 88 +++++++++++++++++++++++++++++++++++++---
 2 files changed, 124 insertions(+), 27 deletions(-)

diff --git a/preview/global/sketch.js b/preview/global/sketch.js
index d5c9e20fff..951412c4b8 100644
--- a/preview/global/sketch.js
+++ b/preview/global/sketch.js
@@ -1,30 +1,51 @@
 let myShader;
 p5.disableFriendlyErrors = true;
+
+function calculateOffset() {
+  return 30;
+}
+
 function setup(){
   createCanvas(windowWidth, windowHeight, WEBGL);
 
+  // Raw example
+  myShader = baseMaterialShader().modify(() => {
+    const offset = calculateOffset();
+
+    getWorldPosition((pos) => {
+      let a = createVector3(1, 2, 3);
+      let b = createVector3(3, 4, 5);
+
+      a = (a * b + offset) / 10;
+
+      pos += a;
+
+      return pos;
+    });
+  });
+
+
   // Create and use the custom shader.
-  myShader = baseMaterialShader().modify(
-    () => {
-      const offset = uniform('offset', createVector3())
-      getWorldPosition((pos) => {
-        let a = createVector3(1, 2, 3);
-        let b = createVector3(3, 4, 5);
-        a = a.add(b);
-
-        let c = a.add(b);
-        c = c.add(offset);
-        c.x = b.x.add(1);
-
-
-        console.log("TEST:")
-        console.log(c.x);
-        pos = pos.add(c);
-
-        return pos;
-      })
-    }
-  );
+  // myShader = baseMaterialShader().modify(
+  //   () => {
+  //     const offset = uniform('offset', () => calculateOffset)
+
+  //     getWorldPosition((pos) => {
+  //       let a = createVector3(1, 2, 3);
+  //       let b = createVector3(3, 4, 5);
+  //       a = a.add(b);
+
+  //       let c = a.add(b);
+  //       c = c.add(offset);
+  //       c.x = b.x.add(1);
+
+
+  //       pos = pos.add(c);
+
+  //       return pos;
+  //     })
+  //   }
+  // );
 }
 
 function draw(){
diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index feeb1723b5..6cd9b4d8e6 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -6,7 +6,8 @@
 */
 
 import { parse } from 'acorn';
-import estraverse from 'estraverse';
+import * as walk from 'acorn-walk';
+import escodegen from 'escodegen';
 
 function shadergen(p5, fn) {
   let GLOBAL_SHADER;
@@ -16,12 +17,14 @@ function shadergen(p5, fn) {
   p5.Shader.prototype.modify = function(arg) {
 
     if (arg instanceof Function) {
-      const fnSource = arg.toString()
-      const ast = parse(fnSource, { ecmaVersion: 2021, locations: true });
-      const result = estraverse.traverse(ast, {
-        enter: (node) => console.log(node),
-      })
+      const code = arg.toString()
+      const ast = parse(code, { ecmaVersion: 2021, locations: true });
+      
+      walk.ancestor(ast, ASTCallbacks, null, {myData: 123});
 
+      const transformed = escodegen.generate(ast);
+      console.log(transformed)
+     
       // const program = new ShaderProgram(arg)
       // const newArg = program.generate();
       // console.log(newArg.vertex)
@@ -32,6 +35,79 @@ function shadergen(p5, fn) {
     }
   }
 
+  // Transpiler 
+
+  function replaceBinaryOperator(codeSource) {
+    switch (codeSource) {
+      case '+': return 'add';
+      case '-': return 'sub';
+      case '*': return 'mult';
+      case '/': return 'div';
+      case '%': return 'mod';
+    }
+  }
+
+  const ASTCallbacks = {
+    Literal(node, state, ancestors) {
+    },
+    AssignmentExpression(node, _state, ancestors) {
+      if (node.operator != '=') {
+        const rightReplacementNode = {
+          type: 'CallExpression',
+          callee: {
+            type: "MemberExpression",
+            object: {
+              type: "Identifier",
+              name: node.left.name
+            },
+            property: {
+              type: "Identifier",
+              name: replaceBinaryOperator(node.operator.replace('=','')),
+            },
+          },
+          arguments: [node.right]
+        }
+
+          node.operator = '=';
+          node.right = rightReplacementNode;
+        }
+      },
+    BinaryExpression(node, state, ancestors) {
+      // let i = ancestors.length - 1;
+      // let ancestor = ancestors[i]; // ancestor === node
+      // while (ancestor.type === 'BinaryExpression') {
+      //   ancestor = ancestors[i--];
+      // }
+
+      console.log("\n NEW NODE:")
+
+      const transformed = escodegen.generate(node);
+      const l = escodegen.generate(node.left);
+      const r = escodegen.generate(node.right);
+      console.log("Transformed: ", transformed);
+      console.log("Left: ", l);
+      console.log("Right: ", r);
+
+      console.log(node.left);
+
+      node.type = 'CallExpression';
+      console.log("OPERATOR: ", node.operator)
+      node.callee = {
+        type: "MemberExpression",
+        object: node.left,
+        property: {
+          type: "Identifier",
+          name: replaceBinaryOperator(node.operator),
+        },
+      };
+      node.arguments = [node.right];
+
+    },
+  }
+
+
+  // JS API
+
   class BaseNode {
     constructor(isInternal) {
       if (new.target === BaseNode) {

From 17af9126a4952c9b52cb8c850bb429229fefe4c5 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Fri, 28 Feb 2025 18:08:52 +0000
Subject: [PATCH 22/54] slight refactoras will need a more general addComponent
 function

---
 src/webgl/ShaderGen.js | 29 +++++++++++++++--------------
 1 file changed, 15 insertions(+), 14 deletions(-)

diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index 6cd9b4d8e6..e5d27b3351 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -357,21 +357,22 @@ function shadergen(p5, fn) {
       super(isInternal)
       this.name = name;
       this.type = type;
-      this.addComponents(
-        (() => {
-          switch (type) {
-            case 'vec2': return ['x', 'y'];
-            case 'vec3': return ['x', 'y', 'z'];
-            case 'vec4': return ['x', 'y', 'z', 'w'];
-            default: return [];
-          }
-        })()
-      );
-    }
-
-    addComponents(componentNames) {
+      this.autoAddVectorComponents();
+    }
+    
+    addComponent(componentName) {
+      this[componentName] = new ComponentNode(this, componentName, true);
+    }
+
+    autoAddVectorComponents() {
+      const options = {
+        vec2: ['x', 'y'],
+        vec3: ['x', 'y', 'z'],
+        vec4: ['x', 'y', 'z', 'w']
+      };
+      const componentNames = options[this.type] || [];
       for (let componentName of componentNames) {
-        this[componentName] = new ComponentNode(this, componentName, true);
+        this.addComponent(componentName);
       }
     }
 

From 297d806c0fdbb727d89c4b6958528f99c64c5b70 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Fri, 28 Feb 2025 18:09:23 +0000
Subject: [PATCH 23/54] add some more built in functions (see TODO, unfinished)

---
 src/webgl/ShaderGen.js | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index e5d27b3351..24e358716b 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -186,7 +186,23 @@ function shadergen(p5, fn) {
     sin()       { return new FunctionCallNode('sin', this, 'float'); }
     cos()       { return new FunctionCallNode('cos', this, 'float'); }
     radians()   { return new FunctionCallNode('radians', this, 'float'); }
+    abs()       { return new FunctionCallNode('abs',this, this.type) };
+    ceil()      { return new FunctionCallNode(); }
+
+    // TODO: 
+    // Add a whole lot of these functions. Probably should take them out of the primitive node and just attach them to global instead.
+    // https://docs.gl/el3/
     
+    max()       { return new FunctionCallNode(); }
+    min()       { return new FunctionCallNode(); }
+    ceil()      { return new FunctionCallNode(); }
+    round()     { return new FunctionCallNode(); }
+    roundEven() { return new FunctionCallNode(); }
+    sqrt()      { return new FunctionCallNode(); }
+    log()       { return new FunctionCallNode(); }
+    exp()       { return new FunctionCallNode(); }
+
+
     // Check that the types of the operands are compatible.
     // TODO: improve this with less branching if elses
     enforceType(other){

From 1593916100e9cc8eb437a7abaac8f894afc5a330 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Fri, 28 Feb 2025 18:10:06 +0000
Subject: [PATCH 24/54] comment for clarity

---
 src/webgl/ShaderGen.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index 24e358716b..9485bbb5f8 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -51,6 +51,7 @@ function shadergen(p5, fn) {
     Literal(node, state, ancestors) {
     },
     AssignmentExpression(node, _state, ancestors) {
+      // Operator overloading
       if (node.operator != '=') {
         const rightReplacementNode = {
           type: 'CallExpression',

From c4f3e4ee3e09e641097a2987e364ac49288095e0 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Fri, 28 Feb 2025 18:11:08 +0000
Subject: [PATCH 25/54] adding Conditionals (should cover ternaries and if
 statements)

---
 src/webgl/ShaderGen.js | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index 9485bbb5f8..d7fb99ad95 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -480,6 +480,41 @@ function shadergen(p5, fn) {
     }
   }
 
+  // TODO: finish If Node
+  class ConditionalNode {
+    constructor(value) {
+      this.value = value;
+      this.condition = null;
+      this.thenBranch = null;
+      this.elseBranch = null;
+    }
+    //helper 
+    checkType(value) {
+
+    }
+    // conditions
+    equalTo(value){}
+    greaterThan(value) {}
+    greaterThanEqualTo(value) {}
+    lessThan(value) {}
+    lessThanEqualTo(value) {}
+    // modifiers
+    not() {}
+    or() {}
+    and() {}
+    // returns
+    thenReturn(value) {}
+    elseReturn(value) {}
+    // Then?
+    then() {
+      GLOBAL_SHADER.context.declarations.push()
+    }
+  };
+
+  fn.if = function (value) {
+    return new ConditionalNode(value);
+  }
+
   // Helper functions
   function isShaderNode(node) {  
     return (node instanceof BaseNode); 

From 168a50b314367040a516c8879eb1452dc67b43e6 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Fri, 28 Feb 2025 18:11:47 +0000
Subject: [PATCH 26/54] whitespace

---
 src/webgl/ShaderGen.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index d7fb99ad95..6e845adade 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -672,6 +672,7 @@ function shadergen(p5, fn) {
   function getWorldPosition(func){
     GLOBAL_SHADER.getWorldPosition(func)
   }
+  
   function getFinalColor(func){
     GLOBAL_SHADER.getFinalColor(func)
   }

From 1f0a50fe34d82da28851e182af0b287961c54eca Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Sun, 2 Mar 2025 13:26:16 +0000
Subject: [PATCH 27/54] comment proxy for vectors until further work can be
 done on it

---
 src/webgl/ShaderGen.js | 53 +++++++++++++++++++++---------------------
 1 file changed, 27 insertions(+), 26 deletions(-)

diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index 6e845adade..f771fd6499 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -288,29 +288,29 @@ function shadergen(p5, fn) {
 
   // I am interested in the 'prop' value of get and set then
 
-  const VectorNodeHandler = {
-    swizzles: [
-      ['x', 'y', 'z', 'w'],
-      ['r', 'b', 'g', 'a'],
-      ['s', 't', 'p', 'q'],
-    ],
-    get(target, prop, receiver) {
-      if (prop in target) {
-         return Reflect.get(target, prop, receiver); 
-      } else {
-        console.log(prop);
-      }
-      let modifiedProp;
-      return Reflect.get(target, modifiedProp, receiver);
-    },
-    set(obj, prop, receiver) {
-      console.log("OBJect:",obj,"PROPERTY", prop, "RECEIVER", receiver);
-
-      if (prop in obj) { return Reflect.set(...arguments); }
-      let modifiedProp;
-      return Reflect.set(obj, modifiedProp, receiver);
-    }
-  }
+  // const VectorNodeHandler = {
+  //   swizzles: [
+  //     ['x', 'y', 'z', 'w'],
+  //     ['r', 'b', 'g', 'a'],
+  //     ['s', 't', 'p', 'q'],
+  //   ],
+  //   get(target, prop, receiver) {
+  //     if (prop in target) {
+  //        return Reflect.get(target, prop, receiver); 
+  //     } else {
+  //       console.log(prop);
+  //     }
+  //     let modifiedProp;
+  //     return Reflect.get(target, modifiedProp, receiver);
+  //   },
+  //   set(obj, prop, receiver) {
+  //     console.log("OBJect:",obj,"PROPERTY", prop, "RECEIVER", receiver);
+
+  //     if (prop in obj) { return Reflect.set(...arguments); }
+  //     let modifiedProp;
+  //     return Reflect.set(obj, modifiedProp, receiver);
+  //   }
+  // }
 
   class VectorNode extends BaseNode {
     constructor(values, type, isInternal = false) {
@@ -631,16 +631,17 @@ function shadergen(p5, fn) {
   }
   
   fn.createVector2 = function(x, y) {
-    return new Proxy(new VectorNode([x, y], 'vec2'), VectorNodeHandler);
+    // return new Proxy(new VectorNode([x, y], 'vec2'), VectorNodeHandler);
+    return new VectorNode([x, y], 'vec2');
   }
 
   fn.createVector3 = function(x, y, z) {
-    return new Proxy(new VectorNode([x, y, z], 'vec3'), VectorNodeHandler);
+    // return new Proxy(new VectorNode([x, y, z], 'vec3'), VectorNodeHandler);
     return new VectorNode([x, y, z], 'vec3');
   }
 
   fn.createVector4 = function(x, y, z, w) {
-    return new Proxy(new VectorNode([x, y, z, w], 'vec4'), VectorNodeHandler);
+    // return new Proxy(new VectorNode([x, y, z, w], 'vec4'), VectorNodeHandler);
     return new VectorNode([x, y, z, w], 'vec4');
   }
   

From 62107b71c54bd0569497daef99d08ccda8918f86 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Sun, 2 Mar 2025 13:26:26 +0000
Subject: [PATCH 28/54] switch to simple walk

---
 src/webgl/ShaderGen.js | 60 ++++++++++++++++--------------------------
 1 file changed, 22 insertions(+), 38 deletions(-)

diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index f771fd6499..9d83fc00fa 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -6,7 +6,7 @@
 */
 
 import { parse } from 'acorn';
-import * as walk from 'acorn-walk';
+import { simple } from 'acorn-walk';
 import escodegen from 'escodegen';
 
 function shadergen(p5, fn) {
@@ -14,24 +14,23 @@ function shadergen(p5, fn) {
   
   const oldModify = p5.Shader.prototype.modify
 
-  p5.Shader.prototype.modify = function(arg) {
-
-    if (arg instanceof Function) {
-      const code = arg.toString()
-      const ast = parse(code, { ecmaVersion: 2021, locations: true });
+  p5.Shader.prototype.modify = function(modifier) {
+    if (modifier instanceof Function) {
+      const code = modifier.toString()
+      const ast = parse(code, { ecmaVersion: 2021 /*, locations: true*/ });
       
-      walk.ancestor(ast, ASTCallbacks, null, {myData: 123});
+      simple(ast, ASTCallbacks);
 
-      const transformed = escodegen.generate(ast);
-      console.log(transformed)
+      const transpiledArg = escodegen.generate(ast);
+      console.log(transpiledArg)
      
-      // const program = new ShaderProgram(arg)
+      // const program = new ShaderProgram(modifier)
       // const newArg = program.generate();
       // console.log(newArg.vertex)
       // return oldModify.call(this, newArg);
     } 
     else {
-      return oldModify.call(this, arg)
+      return oldModify.call(this, modifier)
     }
   }
 
@@ -48,11 +47,14 @@ function shadergen(p5, fn) {
   }
 
   const ASTCallbacks = {
-    Literal(node, state, ancestors) {
+    // TODO: automatically making uniforms
+    Literal(node) {
     },
-    AssignmentExpression(node, _state, ancestors) {
-      // Operator overloading
-      if (node.operator != '=') {
+
+    // The callbacks for AssignmentExpression and BinaryExpression handle
+    // operator overloading including +=, *= assignment expressions 
+    AssignmentExpression(node) {
+      if (node.operator !== '=') {
         const rightReplacementNode = {
           type: 'CallExpression',
           callee: {
@@ -68,31 +70,12 @@ function shadergen(p5, fn) {
           },
           arguments: [node.right]
         }
-
           node.operator = '=';
           node.right = rightReplacementNode;
         }
       },
-    BinaryExpression(node, state, ancestors) {
-      // let i = ancestors.length - 1;
-      // let ancestor = ancestors[i]; // ancestor === node
-      // while (ancestor.type === 'BinaryExpression') {
-      //   ancestor = ancestors[i--];
-      // }
-
-      console.log("\n NEW NODE:")
-
-      const transformed = escodegen.generate(node);
-      const l = escodegen.generate(node.left);
-      const r = escodegen.generate(node.right);
-      console.log("Transformed: ", transformed);
-      console.log("Left: ", l);
-      console.log("Right: ", r);
-
-      console.log(node.left);
-
+    BinaryExpression(node) {
       node.type = 'CallExpression';
-      console.log("OPERATOR: ", node.operator)
       node.callee = {
         type: "MemberExpression",
         object: node.left,
@@ -102,12 +85,13 @@ function shadergen(p5, fn) {
         },
       };
       node.arguments = [node.right];
-
     },
   }
 
 
-  // JS API
+  // Javascript Node API.
+  // These classes are for expressing GLSL functions in Javascript with
+  // needing to  transpile the user's code.
 
   class BaseNode {
     constructor(isInternal) {
@@ -121,7 +105,7 @@ function shadergen(p5, fn) {
       this.usedIn = [];
       this.dependsOn = [];
       this.srcLine = null;
-      
+      // Stack Capture is used to get the original line of user code for Debug purposes
       if (isInternal === false) {
         try {
           throw new Error("StackCapture");

From 41068cbfc3b8f9f490398bacfa866128bff3f3d1 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Wed, 5 Mar 2025 11:17:36 +0000
Subject: [PATCH 29/54] getting the options object populated properly. Create
 uniform functions programatically

---
 preview/global/sketch.js | 50 ++++++++++-----------
 src/webgl/ShaderGen.js   | 93 ++++++++++++++++------------------------
 2 files changed, 63 insertions(+), 80 deletions(-)

diff --git a/preview/global/sketch.js b/preview/global/sketch.js
index 951412c4b8..4ccc11fe80 100644
--- a/preview/global/sketch.js
+++ b/preview/global/sketch.js
@@ -9,43 +9,43 @@ function setup(){
   createCanvas(windowWidth, windowHeight, WEBGL);
 
   // Raw example
-  myShader = baseMaterialShader().modify(() => {
-    const offset = calculateOffset();
+  // myShader = baseMaterialShader().modify(() => {
+  //   const offset = calculateOffset();
 
-    getWorldPosition((pos) => {
-      let a = createVector3(1, 2, 3);
-      let b = createVector3(3, 4, 5);
+  //   getWorldPosition((pos) => {
+  //     let a = createVector3(1, 2, 3);
+  //     let b = createVector3(3, 4, 5);
 
-      a = (a * b + offset) / 10;
+  //     a = (a * b + offset) / 10;
 
-      pos += a;
+  //     pos += a;
 
-      return pos;
-    });
-  });
+  //     return pos;
+  //   });
+  // });
 
 
   // Create and use the custom shader.
-  // myShader = baseMaterialShader().modify(
-  //   () => {
-  //     const offset = uniform('offset', () => calculateOffset)
+  myShader = baseMaterialShader().modify(
+    () => {
+      const offset = uniformFloat('offset', () => calculateOffset)
 
-  //     getWorldPosition((pos) => {
-  //       let a = createVector3(1, 2, 3);
-  //       let b = createVector3(3, 4, 5);
-  //       a = a.add(b);
+      getWorldPosition((pos) => {
+        let a = createVector3(1, 2, 3);
+        let b = createVector3(3, 4, 5);
+        a = a.add(b);
 
-  //       let c = a.add(b);
-  //       c = c.add(offset);
-  //       c.x = b.x.add(1);
+        let c = a.add(b);
+        c += c.add(offset);
+        c.x = b.x.add(1);
 
 
-  //       pos = pos.add(c);
+        pos = pos.add(c);
 
-  //       return pos;
-  //     })
-  //   }
-  // );
+        return pos;
+      })
+    }
+  );
 }
 
 function draw(){
diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index 9d83fc00fa..25a1ecef01 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -18,15 +18,17 @@ function shadergen(p5, fn) {
     if (modifier instanceof Function) {
       const code = modifier.toString()
       const ast = parse(code, { ecmaVersion: 2021 /*, locations: true*/ });
-      
       simple(ast, ASTCallbacks);
-
       const transpiledArg = escodegen.generate(ast);
-      console.log(transpiledArg)
-     
-      // const program = new ShaderProgram(modifier)
-      // const newArg = program.generate();
-      // console.log(newArg.vertex)
+      const transpiledFn = new Function(transpiledArg
+        .slice(transpiledArg.indexOf("{") + 1, transpiledArg.lastIndexOf("}"))
+      );
+      
+      const generator = new ShaderGenerator(transpiledFn, this)
+      const newArg = generator.callModifyFunction();
+      console.log(code);
+      console.log(transpiledArg);
+      console.log(newArg)
       // return oldModify.call(this, newArg);
     } 
     else {
@@ -111,6 +113,7 @@ function shadergen(p5, fn) {
           throw new Error("StackCapture");
         } catch (e) {
           const lines = e.stack.split("\n");
+          // console.log(lines);
           let index = 5;
           if (isBinaryOperatorNode(this)) { index--; };
           this.srcLine = lines[index].trim();
@@ -527,24 +530,18 @@ function shadergen(p5, fn) {
   // Shader program
   // This class is responsible for converting the nodes into an object containing GLSL code, to be used by p5.Shader.modify
 
-  class ShaderProgram {
-    constructor(modifyFunction) {
-      this.uniforms = {
-        int: {},
-        float: {},
-        vec2: {},
-        vec3: {},
-        vec4: {},
-        texture: {},
-      }
-      this.functions = {
-      }
-      this.resetGLSLContext();
+  class ShaderGenerator {
+    constructor(modifyFunction, shaderToModify) {
+      this.modifyFunction = modifyFunction;
+      this.shaderToModify = shaderToModify;
+      shaderToModify.inspectHooks();
       GLOBAL_SHADER = this;
-      this.generator = modifyFunction;
+      this.uniforms = {};
+      this.functions = {};
+      this.resetGLSLContext();
     }
-    generate() {
-      this.generator();
+    callModifyFunction() {
+      this.modifyFunction();
       return {
         uniforms: this.uniforms,
         functions: this.functions,
@@ -560,35 +557,6 @@ function shadergen(p5, fn) {
       }
     }
     // TODO:
-    uniformInt(name, defaultValue) {
-      this.uniforms.int[name] = defaultValue;
-      return new VariableNode(name, 'int');
-    }
-    uniformFloat(name, defaultValue) {
-      this.uniforms.float[name] = defaultValue;
-      return new VariableNode(name, 'float');
-    }
-    uniformVector2(name, defaultValue) {
-      this.uniforms.vec2[name] = defaultValue;
-      return new VariableNode(name, 'vec2');
-    }
-    uniformVector2(name, defaultValue) {
-      this.uniforms.vec3[name] = defaultValue;
-      return new VariableNode(name, 'vec3');
-    }
-    uniformVector2(name, defaultValue) {
-      this.uniforms.vec4[name] = defaultValue;
-      return new VariableNode(name, 'vec4');
-    }
-    uniformTexture(name, defaultValue) {
-      this.uniforms.texture[name] = defaultValue;
-      return new VariableNode(name, 'vec4');
-    }
-    uniform(name, defaultValue) {
-      this.uniforms[name] = defaultValue;
-      return new VariableNode(name, defaultValue.type);
-    }
-    
     buildFunction(argumentName, argumentType, callback) {
       let functionArgument = new VariableNode(argumentName, argumentType, true);
       const finalLine = callback(functionArgument).toGLSLBase(this.context);
@@ -649,9 +617,24 @@ function shadergen(p5, fn) {
     return new VariableNode('discard', 'keyword');
   }
   
-  fn.uniform = function(name, value) {
-    let result = GLOBAL_SHADER.uniform(name, value)
-    return result;
+  // Uniforms and attributes
+  const uniformFns = {
+    'int': 'Int',
+    'float': 'Float',
+    'vec2': 'Vector2',
+    'vec3': 'Vector3',
+    'vec4': 'Vector4',
+    'sampler2D': 'Texture',
+  };
+  for (const type in uniformFns) {
+    const uniformFnVariant = `uniform${uniformFns[type]}`;
+    ShaderGenerator.prototype[uniformFnVariant] = function(name, defaultValue) {
+      this.uniforms[`${type} ${name}`] = defaultValue;
+      return new VariableNode(name, type);
+    };
+    fn[uniformFnVariant] = function (name, value) { 
+      return GLOBAL_SHADER[uniformFnVariant](name, value); 
+    };
   }
   
   function getWorldPosition(func){

From 6c582b9b1bb21f5953c12317712f014f387dbe56 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Wed, 5 Mar 2025 16:07:46 +0000
Subject: [PATCH 30/54] build shader hooks from existing API

---
 preview/global/sketch.js |  54 +++++------
 src/webgl/ShaderGen.js   | 203 +++++++++++++++++++++++++--------------
 2 files changed, 157 insertions(+), 100 deletions(-)

diff --git a/preview/global/sketch.js b/preview/global/sketch.js
index 4ccc11fe80..cf62f93bc2 100644
--- a/preview/global/sketch.js
+++ b/preview/global/sketch.js
@@ -8,44 +8,42 @@ function calculateOffset() {
 function setup(){
   createCanvas(windowWidth, windowHeight, WEBGL);
 
-  // Raw example
-  // myShader = baseMaterialShader().modify(() => {
-  //   const offset = calculateOffset();
+  // // Raw example
+  myShader = baseMaterialShader().modify(() => {
 
-  //   getWorldPosition((pos) => {
-  //     let a = createVector3(1, 2, 3);
-  //     let b = createVector3(3, 4, 5);
+    const offset = uniformFloat(1);
 
-  //     a = (a * b + offset) / 10;
-
-  //     pos += a;
-
-  //     return pos;
-  //   });
-  // });
+    getFinalColor((col) => {
+      let a = createVector4(1, 2, 3, 4);
+      let b = createVector4(3, 4, 5, 6);
+      a = (a * b + offset) / 10;
+      col += a;
+      return col;
+    });
+  });
 
 
   // Create and use the custom shader.
-  myShader = baseMaterialShader().modify(
-    () => {
-      const offset = uniformFloat('offset', () => calculateOffset)
+  // myShader = baseMaterialShader().modify(
+  //   () => {
+  //     const offset = uniformFloat('offset', () => calculateOffset)
 
-      getWorldPosition((pos) => {
-        let a = createVector3(1, 2, 3);
-        let b = createVector3(3, 4, 5);
-        a = a.add(b);
+  //     getWorldPosition((pos) => {
+  //       let a = createVector3(1, 2, 3);
+  //       let b = createVector3(3, 4, 5);
+  //       a = a.add(b);
 
-        let c = a.add(b);
-        c += c.add(offset);
-        c.x = b.x.add(1);
+  //       let c = a.add(b);
+  //       c += c.add(offset);
+  //       c.x = b.x.add(1);
 
 
-        pos = pos.add(c);
+  //       pos = pos.add(c);
 
-        return pos;
-      })
-    }
-  );
+  //       return pos;
+  //     })
+  //   }
+  // );
 }
 
 function draw(){
diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index 25a1ecef01..f6000f409a 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -36,8 +36,7 @@ function shadergen(p5, fn) {
     }
   }
 
-  // Transpiler 
-
+  // AST Transpiler Callbacks and their helpers
   function replaceBinaryOperator(codeSource) {
     switch (codeSource) {
       case '+': return 'add';
@@ -50,8 +49,7 @@ function shadergen(p5, fn) {
 
   const ASTCallbacks = {
     // TODO: automatically making uniforms
-    Literal(node) {
-    },
+    
 
     // The callbacks for AssignmentExpression and BinaryExpression handle
     // operator overloading including +=, *= assignment expressions 
@@ -92,9 +90,8 @@ function shadergen(p5, fn) {
 
 
   // Javascript Node API.
-  // These classes are for expressing GLSL functions in Javascript with
+  // These classes are for expressing GLSL functions in Javascript without
   // needing to  transpile the user's code.
-
   class BaseNode {
     constructor(isInternal) {
       if (new.target === BaseNode) {
@@ -125,7 +122,7 @@ function shadergen(p5, fn) {
     toGLSLBase(context){
       if (this.useTempVar()) {
         return this.getTemp(context);
-      } 
+      }
       else {
         return this.toGLSL(context);
       }
@@ -171,25 +168,6 @@ function shadergen(p5, fn) {
     mult(other) { return new BinaryOperatorNode(this, this.enforceType(other), '*'); }
     div(other)  { return new BinaryOperatorNode(this, this.enforceType(other), '/'); }
     mod(other)  { return new ModulusNode(this, this.enforceType(other)); }
-    sin()       { return new FunctionCallNode('sin', this, 'float'); }
-    cos()       { return new FunctionCallNode('cos', this, 'float'); }
-    radians()   { return new FunctionCallNode('radians', this, 'float'); }
-    abs()       { return new FunctionCallNode('abs',this, this.type) };
-    ceil()      { return new FunctionCallNode(); }
-
-    // TODO: 
-    // Add a whole lot of these functions. Probably should take them out of the primitive node and just attach them to global instead.
-    // https://docs.gl/el3/
-    
-    max()       { return new FunctionCallNode(); }
-    min()       { return new FunctionCallNode(); }
-    ceil()      { return new FunctionCallNode(); }
-    round()     { return new FunctionCallNode(); }
-    roundEven() { return new FunctionCallNode(); }
-    sqrt()      { return new FunctionCallNode(); }
-    log()       { return new FunctionCallNode(); }
-    exp()       { return new FunctionCallNode(); }
-
 
     // Check that the types of the operands are compatible.
     // TODO: improve this with less branching if elses
@@ -302,13 +280,10 @@ function shadergen(p5, fn) {
   class VectorNode extends BaseNode {
     constructor(values, type, isInternal = false) {
       super(isInternal);
-
       this.components = ['x', 'y', 'z', 'w'].slice(0, values.length);
       this.components.forEach((component, i) => {
         this[component] = new FloatNode(values[i], true);
-        // this[component] = new ComponentNode(this, component, true);
       });
-
       this.type = type;
     }
     
@@ -468,16 +443,13 @@ function shadergen(p5, fn) {
   }
 
   // TODO: finish If Node
-  class ConditionalNode {
+  class ConditionalNode extends BaseNode {
     constructor(value) {
+      super(value);
       this.value = value;
       this.condition = null;
       this.thenBranch = null;
       this.elseBranch = null;
-    }
-    //helper 
-    checkType(value) {
-
     }
     // conditions
     equalTo(value){}
@@ -492,12 +464,21 @@ function shadergen(p5, fn) {
     // returns
     thenReturn(value) {}
     elseReturn(value) {}
-    // Then?
-    then() {
-      GLOBAL_SHADER.context.declarations.push()
-    }
+    // 
+    thenDiscard() {
+      new ConditionalDiscard(this.condition);
+    };
   };
 
+  class ConditionalDiscard extends Base{
+    constructor(condition){
+      this.condition = condition;
+    }
+    toGLSL(context) {
+      context.discardConditions.push(`if(${this.condition}{discard;})`);
+    }
+  }
+
   fn.if = function (value) {
     return new ConditionalNode(value);
   }
@@ -531,24 +512,61 @@ function shadergen(p5, fn) {
   // This class is responsible for converting the nodes into an object containing GLSL code, to be used by p5.Shader.modify
 
   class ShaderGenerator {
-    constructor(modifyFunction, shaderToModify) {
-      this.modifyFunction = modifyFunction;
-      this.shaderToModify = shaderToModify;
-      shaderToModify.inspectHooks();
+    constructor(modifyFunction, originalShader) {
       GLOBAL_SHADER = this;
-      this.uniforms = {};
-      this.functions = {};
+      this.modifyFunction = modifyFunction;
+      this.generateHookBuilders(originalShader);
+      this.output = {
+        uniforms: {},
+      }
       this.resetGLSLContext();
     }
+
     callModifyFunction() {
       this.modifyFunction();
-      return {
-        uniforms: this.uniforms,
-        functions: this.functions,
-        vertex: this.functions.getWorldPosition,
-        fragment: this.functions.getFinalColor,
+      return this.output;
+    }
+
+    generateHookBuilders(originalShader) {
+      const availableHooks = {
+        ...originalShader.hooks.vertex,
+        ...originalShader.hooks.fragment,
       }
+      
+      // Defines a function for each of the hooks for the shader we are modifying.
+      Object.keys(availableHooks).forEach((hookName) => {
+        const hookTypes = originalShader.hookTypes(hookName)
+        this[hookTypes.name] = function(userOverride) {
+          let hookArgs = []
+          let argsArray = [];
+
+          hookTypes.parameters.forEach((parameter, i) => {
+            hookArgs.push(
+              new VariableNode(parameter.name, parameter.type.typeName, true)
+            );
+            if (i === 0) {
+              argsArray.push(`${parameter.type.typeName} ${parameter.name}`);
+            } else {
+              argsArray.push(`, ${parameter.type.typeName} ${parameter.name}`);
+            }
+          })
+
+          const toGLSLResult = userOverride(...hookArgs).toGLSLBase(this.context);
+          let codeLines = [`(${argsArray.join(', ')}) {`, this.context.declarations.slice()].flat();
+          codeLines.push(`\n${hookTypes.returnType.typeName} finalReturnValue = ${toGLSLResult};
+                          \nreturn finalReturnValue;
+                          \n}`);
+          this.output[hookName] = codeLines.join('\n');
+          this.resetGLSLContext();
+        }
+
+        // Expose the Functions to global scope for users to use
+        window[hookTypes.name] = function(userOverride) {
+          GLOBAL_SHADER[hookTypes.name](userOverride); 
+        };
+      })
     }
+
     resetGLSLContext() { 
       this.context = {
         id: 0,
@@ -556,22 +574,6 @@ function shadergen(p5, fn) {
         declarations: [],
       }
     }
-    // TODO:
-    buildFunction(argumentName, argumentType, callback) {
-      let functionArgument = new VariableNode(argumentName, argumentType, true);
-      const finalLine = callback(functionArgument).toGLSLBase(this.context);
-      let codeLines = this.context.declarations.slice();
-      codeLines.push(`\n${argumentName} = ${finalLine}; \nreturn ${argumentName};`)
-      this.resetGLSLContext();
-      return codeLines.join("\n");
-    }
-
-    getWorldPosition(func) {
-      this.functions.getWorldPosition = this.buildFunction("pos", "vec3", func);
-    }
-    getFinalColor(func) {
-      this.functions.getFinalColor = this.buildFunction("col", "vec3", func);
-    }
   }
 
   // User functions
@@ -629,7 +631,7 @@ function shadergen(p5, fn) {
   for (const type in uniformFns) {
     const uniformFnVariant = `uniform${uniformFns[type]}`;
     ShaderGenerator.prototype[uniformFnVariant] = function(name, defaultValue) {
-      this.uniforms[`${type} ${name}`] = defaultValue;
+      this.output.uniforms[`${type} ${name}`] = defaultValue;
       return new VariableNode(name, type);
     };
     fn[uniformFnVariant] = function (name, value) { 
@@ -637,12 +639,69 @@ function shadergen(p5, fn) {
     };
   }
   
-  function getWorldPosition(func){
-    GLOBAL_SHADER.getWorldPosition(func)
-  }
-  
-  function getFinalColor(func){
-    GLOBAL_SHADER.getFinalColor(func)
+  // GLSL Built in functions
+  // TODO: 
+  // Add a whole lot of these functions. 
+  // https://docs.gl/el3/abs
+  const builtInFunctions = {
+    // Trigonometry
+    'acos': {},
+    'acosh': {},
+    'asin': {},
+    'asinh': {},
+    'atan': {},
+    'atanh': {},
+    'cos': {},
+    'cosh': {},
+    'degrees': {},
+    'radians': {},
+    'sin': {},
+    'sinh': {},
+    'tan': {},
+    'tanh': {},
+    // Mathematics
+    'abs': {},
+    'ceil': {},
+    'clamp': {},
+    'dFdx': {},
+    'dFdy': {},
+    'exp': {},
+    'exp2': {},
+    'floor': {},
+    'fma': {},
+    'fract': {},
+    'fwidth': {},
+    'inversesqrt': {},
+    'isinf': {},
+    'isnan': {},
+    'log': {},
+    'log2': {},
+    'max': {},
+    'min': {},
+    'mix': {},
+    'mod': {},
+    'modf': {},
+    'pow': {},
+    'round': {},
+    'roundEven': {},
+    'sign': {},
+    'smoothstep': {},
+    'sqrt': {},
+    'step': {},
+    'trunc': {},
+    // Vector
+    'cross': {},
+    'distance': {},
+    'dot': {},
+    'equal': {},
+    'faceforward': {},
+    'length': {},
+    'normalize': {},
+    'notEqual': {},
+    'reflect': {},
+    'refract': {},
+    // Texture sampling
+    'texture': {},
   }
 
   const oldTexture = p5.prototype.texture;

From 7bae49583d6e43b051577dd698d7e9f5395246f7 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Wed, 5 Mar 2025 16:41:53 +0000
Subject: [PATCH 31/54] added automatic uniform names

---
 src/webgl/ShaderGen.js | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index f6000f409a..1825b1931d 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -48,9 +48,15 @@ function shadergen(p5, fn) {
   }
 
   const ASTCallbacks = {
-    // TODO: automatically making uniforms
-    
-
+    VariableDeclarator(node) {
+      if (node.init.callee && node.init.callee.name.slice(0, 7) === 'uniform') {
+        const uniformNameLiteral = {
+          type: 'Literal',
+          value: node.id.name
+        }
+        node.init.arguments.unshift(uniformNameLiteral);
+      }
+    },
     // The callbacks for AssignmentExpression and BinaryExpression handle
     // operator overloading including +=, *= assignment expressions 
     AssignmentExpression(node) {
@@ -632,7 +638,8 @@ function shadergen(p5, fn) {
     const uniformFnVariant = `uniform${uniformFns[type]}`;
     ShaderGenerator.prototype[uniformFnVariant] = function(name, defaultValue) {
       this.output.uniforms[`${type} ${name}`] = defaultValue;
-      return new VariableNode(name, type);
+      let safeType = type === 'sampler2D' ? 'vec4' : type;
+      return new VariableNode(name, safeType);
     };
     fn[uniformFnVariant] = function (name, value) { 
       return GLOBAL_SHADER[uniformFnVariant](name, value); 

From d6092404b96aca095f584210abb1bbca32917948 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Tue, 11 Mar 2025 15:33:20 +0000
Subject: [PATCH 32/54] fixing component assignments for binary operators and
 variables

---
 src/webgl/ShaderGen.js | 360 ++++++++++++++++++-----------------------
 1 file changed, 161 insertions(+), 199 deletions(-)

diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index 1825b1931d..1f7099ce84 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -14,25 +14,35 @@ function shadergen(p5, fn) {
   
   const oldModify = p5.Shader.prototype.modify
 
-  p5.Shader.prototype.modify = function(modifier) {
-    if (modifier instanceof Function) {
-      const code = modifier.toString()
-      const ast = parse(code, { ecmaVersion: 2021 /*, locations: true*/ });
-      simple(ast, ASTCallbacks);
-      const transpiledArg = escodegen.generate(ast);
-      const transpiledFn = new Function(transpiledArg
-        .slice(transpiledArg.indexOf("{") + 1, transpiledArg.lastIndexOf("}"))
-      );
-      
-      const generator = new ShaderGenerator(transpiledFn, this)
-      const newArg = generator.callModifyFunction();
-      console.log(code);
-      console.log(transpiledArg);
-      console.log(newArg)
-      // return oldModify.call(this, newArg);
+  p5.Shader.prototype.modify = function(shaderModifier, options = { parser: true, srcLocations: false }) {
+    if (shaderModifier instanceof Function) {
+      let generatorFunction;
+      if (options.parser) {
+        const sourceString = shaderModifier.toString()
+        const ast = parse(sourceString, { 
+          ecmaVersion: 2021, 
+          locations: options.srcLocations 
+        });
+        simple(ast, ASTCallbacks);
+        const transpiledSource = escodegen.generate(ast);
+        generatorFunction = new Function(
+          transpiledSource.slice(
+            transpiledSource.indexOf("{") + 1,
+            transpiledSource.lastIndexOf("}")
+          )
+        );
+      } else {
+        generatorFunction = shaderModifier;
+      }
+      const generator = new ShaderGenerator(generatorFunction, this, options.srcLocations)
+      const generatedModifyArgument = generator.hookCallbacks();
+      console.log("SRC STRING: ", generatorFunction);
+      console.log("NEW OPTIONS:", generatedModifyArgument['vec4 getFinalColor'])
+
+      return oldModify.call(this, generatedModifyArgument);
     } 
     else {
-      return oldModify.call(this, modifier)
+      return oldModify.call(this, shaderModifier)
     }
   }
 
@@ -49,7 +59,7 @@ function shadergen(p5, fn) {
 
   const ASTCallbacks = {
     VariableDeclarator(node) {
-      if (node.init.callee && node.init.callee.name.slice(0, 7) === 'uniform') {
+      if (node.init.callee && node.init.callee.name.startsWith('uniform')) {
         const uniformNameLiteral = {
           type: 'Literal',
           value: node.id.name
@@ -61,18 +71,17 @@ function shadergen(p5, fn) {
     // operator overloading including +=, *= assignment expressions 
     AssignmentExpression(node) {
       if (node.operator !== '=') {
+        const methodName = replaceBinaryOperator(node.operator.replace('=',''));
         const rightReplacementNode = {
           type: 'CallExpression',
           callee: {
             type: "MemberExpression",
-            object: {
-              type: "Identifier",
-              name: node.left.name
-            },
+            object: node.left,
             property: {
               type: "Identifier",
-              name: replaceBinaryOperator(node.operator.replace('=','')),
+              name: methodName,
             },
+            computed: false,
           },
           arguments: [node.right]
         }
@@ -99,54 +108,42 @@ function shadergen(p5, fn) {
   // These classes are for expressing GLSL functions in Javascript without
   // needing to  transpile the user's code.
   class BaseNode {
-    constructor(isInternal) {
+    constructor(isInternal, type) {
       if (new.target === BaseNode) {
         throw new TypeError("Cannot construct BaseNode instances directly. This is an abstract class.");
       }
-      this.type = null;
-      
+      this.type = type;
+
       // For tracking recursion depth and creating temporary variables
       this.isInternal = isInternal;
       this.usedIn = [];
       this.dependsOn = [];
       this.srcLine = null;
       // Stack Capture is used to get the original line of user code for Debug purposes
-      if (isInternal === false) {
+      if (GLOBAL_SHADER.srcLocations === true && isInternal === false) {
         try {
           throw new Error("StackCapture");
         } catch (e) {
           const lines = e.stack.split("\n");
-          // console.log(lines);
-          let index = 5;
-          if (isBinaryOperatorNode(this)) { index--; };
-          this.srcLine = lines[index].trim();
+          let userSketchLineIndex = 5;
+          if (isBinaryOperatorNode(this)) { userSketchLineIndex--; };
+          this.srcLine = lines[userSketchLineIndex].trim();
         }
       }
     }
     
     // The base node implements a version of toGLSL which determines whether the generated code should be stored in a temporary variable.
     toGLSLBase(context){
-      if (this.useTempVar()) {
-        return this.getTemp(context);
+      if (this.shouldUseTemporaryVariable()) {
+        return this.getTemporaryVariable(context);
       }
       else {
         return this.toGLSL(context);
       }
     }
 
-    // useTempVar() {
-    //   if (isBinaryOperatorNode(this)) {
-    //     console.log(this.a);
-    //     console.log(this.b);
-    //     return false; 
-    //   }
-    //   if (this.isInternal) { return false; }
-    //   if (isVariableNode(this)) { return false; }
-    //   if (isVectorNode(this)) { return true; }
-    //   return (this.usedIn.length > 1);
-    // };
-    
-    useTempVar() {
+    shouldUseTemporaryVariable() {
+      if (this.temporaryVariable)
       if (this.isInternal || isVariableNode(this)) { return false; }
       let score = 0;
       score += isBinaryOperatorNode(this);
@@ -155,7 +152,7 @@ function shadergen(p5, fn) {
       return score > 3;
     }
     
-    getTemp(context) {
+    getTemporaryVariable(context) {
       if (!this.temporaryVariable) {
         this.temporaryVariable = `temp_${context.getNextID()}`;
         let line = "";
@@ -168,7 +165,7 @@ function shadergen(p5, fn) {
       return this.temporaryVariable;
     };
     
-    // TODO: Add more operations here
+    // Binary Operators
     add(other)  { return new BinaryOperatorNode(this, this.enforceType(other), '+'); }
     sub(other)  { return new BinaryOperatorNode(this, this.enforceType(other), '-'); }
     mult(other) { return new BinaryOperatorNode(this, this.enforceType(other), '*'); }
@@ -194,6 +191,22 @@ function shadergen(p5, fn) {
         return new this.constructor(other);
       }
     }
+
+    addComponent(componentName, type='float') {
+      this[componentName] = new ComponentNode(this, componentName, type, true);
+    }
+
+    autoAddVectorComponents() {
+      const options = {
+        vec2: ['x', 'y'],
+        vec3: ['x', 'y', 'z'],
+        vec4: ['x', 'y', 'z', 'w']
+      };
+      const componentNames = options[this.type] || [];
+      for (let componentName of componentNames) {
+        this.addComponent(componentName);
+      }
+    }
     
     toGLSL(context){
       throw new TypeError("Not supposed to call this function on BaseNode, which is an abstract class.");
@@ -203,9 +216,8 @@ function shadergen(p5, fn) {
   // Primitive Types
   class IntNode extends BaseNode {
     constructor(x = 0, isInternal = false) {
-      super(isInternal);
+      super(isInternal, 'int');
       this.x = x;
-      this.type = 'int';
     }
 
     toGLSL(context) {
@@ -224,9 +236,8 @@ function shadergen(p5, fn) {
   
   class FloatNode extends BaseNode {
     constructor(x = 0, isInternal = false){
-      super(isInternal);
+      super(isInternal, 'float');
       this.x = x;
-      this.type = 'float';
     }
 
     toGLSL(context) {
@@ -243,54 +254,14 @@ function shadergen(p5, fn) {
     }
   }
   
-  // TODO:
-  // There is a possibility that since we *always* use a temporary variable for vectors
-  // that we don't actually need a Float Node for every component. They could be component node's instead? 
-  // May need to then store the temporary variable name in this class.
-
-  // This would be the next step before adding all swizzles 
-
-  // I think it is also possible to make a proxy for vector nodes to check if the user accesses  or sets any 
-  // property .xyz / .rgb /.zxx etc as then we can automatically assign swizzles and only define .xyzw on the actual 
-  // object.
-
-  // Ultimately vectors are the focus of & most complex type in glsl (before we add matrices...) which justifies the complexity
-  // of this class.
-
-  // I am interested in the 'prop' value of get and set then
-
-  // const VectorNodeHandler = {
-  //   swizzles: [
-  //     ['x', 'y', 'z', 'w'],
-  //     ['r', 'b', 'g', 'a'],
-  //     ['s', 't', 'p', 'q'],
-  //   ],
-  //   get(target, prop, receiver) {
-  //     if (prop in target) {
-  //        return Reflect.get(target, prop, receiver); 
-  //     } else {
-  //       console.log(prop);
-  //     }
-  //     let modifiedProp;
-  //     return Reflect.get(target, modifiedProp, receiver);
-  //   },
-  //   set(obj, prop, receiver) {
-  //     console.log("OBJect:",obj,"PROPERTY", prop, "RECEIVER", receiver);
-
-  //     if (prop in obj) { return Reflect.set(...arguments); }
-  //     let modifiedProp;
-  //     return Reflect.set(obj, modifiedProp, receiver);
-  //   }
-  // }
 
   class VectorNode extends BaseNode {
     constructor(values, type, isInternal = false) {
-      super(isInternal);
+      super(isInternal, type);
       this.components = ['x', 'y', 'z', 'w'].slice(0, values.length);
       this.components.forEach((component, i) => {
         this[component] = new FloatNode(values[i], true);
       });
-      this.type = type;
     }
     
     toGLSL(context) {
@@ -308,11 +279,9 @@ function shadergen(p5, fn) {
   // Function Call Nodes
   class FunctionCallNode extends BaseNode {
     constructor(name, args, type, isInternal = false) {
-      super(isInternal);
+      super(isInternal, type);
       this.name = name;
       this.args = args;
-      this.type = type;
-
       // TODO:
       this.argumentTypes = {
 
@@ -320,12 +289,8 @@ function shadergen(p5, fn) {
     }
 
     deconstructArgs(context) {
-      if (this.args.constructor === Array) {
-        let argsString = `${this.args[0].toGLSLBase(context)}`
-        for (let arg of this.args.slice(1)) {
-          argsString += `, ${arg.toGLSLBase(context)}` 
-          return argsString;
-        }
+      if (Array.isArray(this.args)) {
+        return this.args.map((argNode) => argNode.toGLSLBase(context)).join(', ');
       } else {
         return `${this.args.toGLSLBase(context)}`;
       }
@@ -339,27 +304,10 @@ function shadergen(p5, fn) {
   // Variables and member variable nodes
   class VariableNode extends BaseNode {
     constructor(name, type, isInternal = false) {
-      super(isInternal)
+      super(isInternal, type)
       this.name = name;
-      this.type = type;
       this.autoAddVectorComponents();
     }
-    
-    addComponent(componentName) {
-      this[componentName] = new ComponentNode(this, componentName, true);
-    }
-
-    autoAddVectorComponents() {
-      const options = {
-        vec2: ['x', 'y'],
-        vec3: ['x', 'y', 'z'],
-        vec4: ['x', 'y', 'z', 'w']
-      };
-      const componentNames = options[this.type] || [];
-      for (let componentName of componentNames) {
-        this.addComponent(componentName);
-      }
-    }
 
     toGLSL(context) {
       return `${this.name}`;
@@ -367,14 +315,13 @@ function shadergen(p5, fn) {
   }
   
   class ComponentNode extends BaseNode {
-    constructor(parent, component, isInternal = false) {
-      super(isInternal);
+    constructor(parent, componentName, type, isInternal = false) {
+      super(isInternal, type);
       this.parent = parent;
-      this.component = component;
+      this.component = componentName;
       this.type = 'float';
     }
     toGLSL(context) {
-      // CURRENTLY BROKEN:
       // const parentName = this.parent.toGLSLBase(context);
       const parentName = this.parent.temporaryVariable ? this.parent.temporaryVariable : this.parent.name; 
       return `${parentName}.${this.component}`;
@@ -384,7 +331,7 @@ function shadergen(p5, fn) {
   // Binary Operator Nodes
   class BinaryOperatorNode extends BaseNode {
     constructor(a, b, operator, isInternal = false) {
-      super(isInternal);
+      super(isInternal, null);
       this.op = operator;
       this.a = a;
       this.b = b;
@@ -394,7 +341,7 @@ function shadergen(p5, fn) {
       this.type = this.determineType();
     }
     
-    // We know that both this.a and this.b are nodes because of PrimitiveNode.enforceType
+    // We know that both this.a and this.b are nodes because of BaseNode.enforceType
     determineType() {
       if (this.a.type === this.b.type) {
         return this.a.type;
@@ -476,7 +423,7 @@ function shadergen(p5, fn) {
     };
   };
 
-  class ConditionalDiscard extends Base{
+  class ConditionalDiscard extends BaseNode {
     constructor(condition){
       this.condition = condition;
     }
@@ -489,7 +436,7 @@ function shadergen(p5, fn) {
     return new ConditionalNode(value);
   }
 
-  // Helper functions
+  // Node Helper functions
   function isShaderNode(node) {  
     return (node instanceof BaseNode); 
   }
@@ -514,13 +461,14 @@ function shadergen(p5, fn) {
     return (node instanceof VariableNode || node instanceof ComponentNode || typeof(node.temporaryVariable) != 'undefined'); 
   }
 
-  // Shader program
+  // Shader Generator
   // This class is responsible for converting the nodes into an object containing GLSL code, to be used by p5.Shader.modify
 
   class ShaderGenerator {
-    constructor(modifyFunction, originalShader) {
+    constructor(modifyFunction, originalShader, srcLocations) {
       GLOBAL_SHADER = this;
       this.modifyFunction = modifyFunction;
+      this.srcLocations = srcLocations;
       this.generateHookBuilders(originalShader);
       this.output = {
         uniforms: {},
@@ -528,7 +476,7 @@ function shadergen(p5, fn) {
       this.resetGLSLContext();
     }
 
-    callModifyFunction() {
+    hookCallbacks() {
       this.modifyFunction();
       return this.output;
     }
@@ -556,6 +504,7 @@ function shadergen(p5, fn) {
               argsArray.push(`, ${parameter.type.typeName} ${parameter.name}`);
             }
           })
+          console.log(hookArgs)
 
           const toGLSLResult = userOverride(...hookArgs).toGLSLBase(this.context);
           let codeLines = [`(${argsArray.join(', ')}) {`, this.context.declarations.slice()].flat();
@@ -582,37 +531,18 @@ function shadergen(p5, fn) {
     }
   }
 
-  // User functions
-  fn.getWorldPosition = function(func){
-    GLOBAL_SHADER.getWorldPosition(func)
-  }
-  fn.getFinalColor = function(func){
-    GLOBAL_SHADER.getFinalColor(func)
-  }
-  
-  fn.createVector2 = function(x, y) {
-    // return new Proxy(new VectorNode([x, y], 'vec2'), VectorNodeHandler);
-    return new VectorNode([x, y], 'vec2');
-  }
-
-  fn.createVector3 = function(x, y, z) {
-    // return new Proxy(new VectorNode([x, y, z], 'vec3'), VectorNodeHandler);
-    return new VectorNode([x, y, z], 'vec3');
+  // User function helpers
+  function conformVectorParameters(value, vectorDimensions) {
+    // Allow arguments as arrays ([0,0,0,0]) or not (0,0,0,0)
+    value = value.flat();
+    // Populate arguments so uniformVector3(0) becomes [0,0,0] 
+    if (value.length === 1) {
+      value = Array(vectorDimensions).fill(value[0]);
+    }
+    return value;
   }
 
-  fn.createVector4 = function(x, y, z, w) {
-    // return new Proxy(new VectorNode([x, y, z, w], 'vec4'), VectorNodeHandler);
-    return new VectorNode([x, y, z, w], 'vec4');
-  }
-  
-  fn.createFloat = function(x) {
-    return new FloatNode(x);
-  }
-  
-  fn.createInt = function(x) {
-    return new IntNode(x);
-  }
-  
+  // User functions
   fn.instanceID = function() {
     return new VariableNode('gl_InstanceID', 'int');
   }
@@ -625,47 +555,76 @@ function shadergen(p5, fn) {
     return new VariableNode('discard', 'keyword');
   }
   
-  // Uniforms and attributes
-  const uniformFns = {
-    'int': 'Int',
-    'float': 'Float',
-    'vec2': 'Vector2',
-    'vec3': 'Vector3',
-    'vec4': 'Vector4',
-    'sampler2D': 'Texture',
+  // Generating uniformFloat, uniformVec, createFloat, etc functions
+  // Maps a GLSL type to the name suffix for method names
+  const GLSLTypesToSuffixes = {
+    int:    'Int',
+    float:  'Float',
+    vec2:   'Vector2',
+    vec3:   'Vector3',
+    vec4:   'Vector4',
+    sampler2D: 'Texture',
+  };
+  const nodeConstructors = {
+    int:   (value) => new IntNode(value),
+    float: (value) => new FloatNode(value),
+    vec2:  (value) => new VectorNode(value, 'vec2'),
+    vec3:  (value) => new VectorNode(value, 'vec3'),
+    vec4:  (value) => new VectorNode(value, 'vec4'),
   };
-  for (const type in uniformFns) {
-    const uniformFnVariant = `uniform${uniformFns[type]}`;
-    ShaderGenerator.prototype[uniformFnVariant] = function(name, defaultValue) {
-      this.output.uniforms[`${type} ${name}`] = defaultValue;
-      let safeType = type === 'sampler2D' ? 'vec4' : type;
+  for (const glslType in GLSLTypesToSuffixes) {
+    // Generate uniform*() Methods for creating uniforms
+    const typeIdentifier = GLSLTypesToSuffixes[glslType];
+    const uniformMethodName = `uniform${typeIdentifier}`;
+    ShaderGenerator.prototype[uniformMethodName] = function(...args) {
+      let [name, ...defaultValue] = args;
+      if(glslType.startsWith('vec')) {
+        defaultValue = conformVectorParameters(defaultValue, +glslType.slice(3));
+      }
+      this.output.uniforms[`${glslType} ${name}`] = defaultValue;
+      let safeType = glslType === 'sampler2D' ? 'vec4' : glslType;
       return new VariableNode(name, safeType);
     };
-    fn[uniformFnVariant] = function (name, value) { 
-      return GLOBAL_SHADER[uniformFnVariant](name, value); 
+    fn[uniformMethodName] = function (...args) { 
+      return GLOBAL_SHADER[uniformMethodName](...args); 
     };
+
+    // Generate the create*() Methods for creating variables in shaders
+    if (glslType === 'sampler2D') { continue; }
+    const createMethodName = `create${typeIdentifier}`;
+    fn[createMethodName] = function (...value) {
+      if(glslType.startsWith('vec')) {
+        value = conformVectorParameters(value, +glslType.slice(3));
+      } else {
+        value = value[0];
+      }
+      return nodeConstructors[glslType](value);
+    }
   }
   
   // GLSL Built in functions
   // TODO: 
   // Add a whole lot of these functions. 
   // https://docs.gl/el3/abs
+
+  // constructor(name, args, type, isInternal = false) {
+
   const builtInFunctions = {
     // Trigonometry
     'acos': {},
-    'acosh': {},
+    // 'acosh': {},
     'asin': {},
-    'asinh': {},
+    // 'asinh': {},
     'atan': {},
-    'atanh': {},
+    // 'atanh': {},
     'cos': {},
-    'cosh': {},
+    // 'cosh': {},
     'degrees': {},
     'radians': {},
     'sin': {},
-    'sinh': {},
+    // 'sinh': {},
     'tan': {},
-    'tanh': {},
+    // 'tanh': {},
     // Mathematics
     'abs': {},
     'ceil': {},
@@ -675,42 +634,45 @@ function shadergen(p5, fn) {
     'exp': {},
     'exp2': {},
     'floor': {},
-    'fma': {},
+    // 'fma': {},
     'fract': {},
-    'fwidth': {},
-    'inversesqrt': {},
-    'isinf': {},
-    'isnan': {},
-    'log': {},
-    'log2': {},
+    // 'fwidth': {},
+    // 'inversesqrt': {},
+    // 'isinf': {},
+    // 'isnan': {},
+    // 'log': {},
+    // 'log2': {},
     'max': {},
     'min': {},
     'mix': {},
-    'mod': {},
-    'modf': {},
+    // 'mod': {},
+    // 'modf': {},
     'pow': {},
-    'round': {},
-    'roundEven': {},
-    'sign': {},
+    // 'round': {},
+    // 'roundEven': {},
+    // 'sign': {},
     'smoothstep': {},
     'sqrt': {},
     'step': {},
-    'trunc': {},
+    // 'trunc': {},
     // Vector
     'cross': {},
     'distance': {},
     'dot': {},
-    'equal': {},
-    'faceforward': {},
+    // 'equal': {},
+    // 'faceforward': {},
     'length': {},
     'normalize': {},
-    'notEqual': {},
-    'reflect': {},
-    'refract': {},
+    // 'notEqual': {},
+    // 'reflect': {},
+    // 'refract': {},
     // Texture sampling
     'texture': {},
   }
 
+  // Object.entries(builtInFunctions).forEach(([glslFnName, properties]) => {
+  // })
+
   const oldTexture = p5.prototype.texture;
   p5.prototype.texture = function(...args) {
     if (isShaderNode(args[0])) {

From b473ac8d342f4ff3071f50aa99a7cf7bd5c5f444 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Tue, 11 Mar 2025 18:26:24 +0000
Subject: [PATCH 33/54] got the swizzle assignments working

---
 src/webgl/ShaderGen.js | 117 +++++++++++++++++++++++++++--------------
 1 file changed, 77 insertions(+), 40 deletions(-)

diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index 1f7099ce84..df54b495d1 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -35,7 +35,7 @@ function shadergen(p5, fn) {
         generatorFunction = shaderModifier;
       }
       const generator = new ShaderGenerator(generatorFunction, this, options.srcLocations)
-      const generatedModifyArgument = generator.hookCallbacks();
+      const generatedModifyArgument = generator.generate();
       console.log("SRC STRING: ", generatorFunction);
       console.log("NEW OPTIONS:", generatedModifyArgument['vec4 getFinalColor'])
 
@@ -113,7 +113,9 @@ function shadergen(p5, fn) {
         throw new TypeError("Cannot construct BaseNode instances directly. This is an abstract class.");
       }
       this.type = type;
-
+      this.componentNames = [];
+      this.swizzleAccessed = false;
+      this.swizzleChanged = false;
       // For tracking recursion depth and creating temporary variables
       this.isInternal = isInternal;
       this.usedIn = [];
@@ -131,7 +133,36 @@ function shadergen(p5, fn) {
         }
       }
     }
-    
+    // get type() {
+    //   return this._type;
+    // }
+
+    // set type(value) {
+    //   this._type = value;
+    // }
+
+    addVectorComponents() {
+      if (this.type.startsWith('vec')) {
+        const vectorDimensions = +this.type.slice(3);
+        this.componentNames = ['x', 'y', 'z', 'w'].slice(0, vectorDimensions);
+
+        for (let componentName of this.componentNames) {
+          // let value = new FloatNode()
+          let value = new ComponentNode(this, componentName, 'float', true);
+          Object.defineProperty(this, componentName, {
+            get() {
+              this.swizzleAccessed = true;
+              return value;
+            },
+            set(newValue) {
+              this.swizzleChanged = true;
+              value = newValue;
+            }
+          })
+        }
+      }
+    }
+
     // The base node implements a version of toGLSL which determines whether the generated code should be stored in a temporary variable.
     toGLSLBase(context){
       if (this.shouldUseTemporaryVariable()) {
@@ -143,7 +174,7 @@ function shadergen(p5, fn) {
     }
 
     shouldUseTemporaryVariable() {
-      if (this.temporaryVariable)
+      if (this.swizzleChanged) { return true; }
       if (this.isInternal || isVariableNode(this)) { return false; }
       let score = 0;
       score += isBinaryOperatorNode(this);
@@ -159,7 +190,18 @@ function shadergen(p5, fn) {
         if (this.srcLine) {
           line += `\n// From ${this.srcLine}\n`;
         }
-        line += this.type + " " + this.temporaryVariable + " = " + this.toGLSL(context) + ";";
+        if (this.swizzleChanged) {
+          const valueArgs = [];
+          for (let componentName of this.componentNames) {
+            valueArgs.push(this[componentName])
+          }
+          console.log(this, valueArgs)
+          const replacement = nodeConstructors[this.type](valueArgs)
+          line += this.type + " " + this.temporaryVariable + " = " + this.toGLSL(context) + ";";
+          line += `\n` + this.temporaryVariable + " = " + replacement.toGLSL(context) + ";";
+        } else {
+          line += this.type + " " + this.temporaryVariable + " = " + this.toGLSL(context) + ";";
+        }
         context.declarations.push(line);
       }
       return this.temporaryVariable;
@@ -191,22 +233,6 @@ function shadergen(p5, fn) {
         return new this.constructor(other);
       }
     }
-
-    addComponent(componentName, type='float') {
-      this[componentName] = new ComponentNode(this, componentName, type, true);
-    }
-
-    autoAddVectorComponents() {
-      const options = {
-        vec2: ['x', 'y'],
-        vec3: ['x', 'y', 'z'],
-        vec4: ['x', 'y', 'z', 'w']
-      };
-      const componentNames = options[this.type] || [];
-      for (let componentName of componentNames) {
-        this.addComponent(componentName);
-      }
-    }
     
     toGLSL(context){
       throw new TypeError("Not supposed to call this function on BaseNode, which is an abstract class.");
@@ -254,12 +280,11 @@ function shadergen(p5, fn) {
     }
   }
   
-
   class VectorNode extends BaseNode {
     constructor(values, type, isInternal = false) {
       super(isInternal, type);
-      this.components = ['x', 'y', 'z', 'w'].slice(0, values.length);
-      this.components.forEach((component, i) => {
+      this.componentNames = ['x', 'y', 'z', 'w'].slice(0, values.length);
+      this.componentNames.forEach((component, i) => {
         this[component] = new FloatNode(values[i], true);
       });
     }
@@ -267,8 +292,8 @@ function shadergen(p5, fn) {
     toGLSL(context) {
       let glslArgs = ``;
 
-      this.components.forEach((component, i) => {
-        const comma = i === this.components.length - 1 ? `` : `, `;
+      this.componentNames.forEach((component, i) => {
+        const comma = i === this.componentNames.length - 1 ? `` : `, `;
         glslArgs += `${this[component].toGLSLBase(context)}${comma}`;
       })
 
@@ -304,11 +329,10 @@ function shadergen(p5, fn) {
   // Variables and member variable nodes
   class VariableNode extends BaseNode {
     constructor(name, type, isInternal = false) {
-      super(isInternal, type)
+      super(isInternal, type);
       this.name = name;
-      this.autoAddVectorComponents();
+      this.addVectorComponents();
     }
-
     toGLSL(context) {
       return `${this.name}`;
     }
@@ -318,13 +342,13 @@ function shadergen(p5, fn) {
     constructor(parent, componentName, type, isInternal = false) {
       super(isInternal, type);
       this.parent = parent;
-      this.component = componentName;
-      this.type = 'float';
+      this.componentName = componentName;
+      this.type = type;
     }
     toGLSL(context) {
-      // const parentName = this.parent.toGLSLBase(context);
-      const parentName = this.parent.temporaryVariable ? this.parent.temporaryVariable : this.parent.name; 
-      return `${parentName}.${this.component}`;
+      const parentName = this.parent.toGLSLBase(context);
+      // const parentName = this.parent.temporaryVariable ? this.parent.temporaryVariable : this.parent.name; 
+      return `${parentName}.${this.componentName}`;
     }
   }
 
@@ -339,6 +363,7 @@ function shadergen(p5, fn) {
         operand.usedIn.push(this);
       }
       this.type = this.determineType();
+      this.addVectorComponents();
     }
     
     // We know that both this.a and this.b are nodes because of BaseNode.enforceType
@@ -476,8 +501,9 @@ function shadergen(p5, fn) {
       this.resetGLSLContext();
     }
 
-    hookCallbacks() {
+    generate() {
       this.modifyFunction();
+      console.log(this.output);
       return this.output;
     }
 
@@ -504,8 +530,6 @@ function shadergen(p5, fn) {
               argsArray.push(`, ${parameter.type.typeName} ${parameter.name}`);
             }
           })
-          console.log(hookArgs)
-
           const toGLSLResult = userOverride(...hookArgs).toGLSLBase(this.context);
           let codeLines = [`(${argsArray.join(', ')}) {`, this.context.declarations.slice()].flat();
           codeLines.push(`\n${hookTypes.returnType.typeName} finalReturnValue = ${toGLSLResult};
@@ -565,6 +589,7 @@ function shadergen(p5, fn) {
     vec4:   'Vector4',
     sampler2D: 'Texture',
   };
+
   const nodeConstructors = {
     int:   (value) => new IntNode(value),
     float: (value) => new FloatNode(value),
@@ -572,25 +597,37 @@ function shadergen(p5, fn) {
     vec3:  (value) => new VectorNode(value, 'vec3'),
     vec4:  (value) => new VectorNode(value, 'vec4'),
   };
+
   for (const glslType in GLSLTypesToSuffixes) {
     // Generate uniform*() Methods for creating uniforms
     const typeIdentifier = GLSLTypesToSuffixes[glslType];
     const uniformMethodName = `uniform${typeIdentifier}`;
+
     ShaderGenerator.prototype[uniformMethodName] = function(...args) {
       let [name, ...defaultValue] = args;
       if(glslType.startsWith('vec')) {
         defaultValue = conformVectorParameters(defaultValue, +glslType.slice(3));
+        this.output.uniforms[`${glslType} ${name}`] = defaultValue;
+      } 
+      else {
+        console.log("defaultValue: ",defaultValue);
+        console.log("defaultValue[0]: ",defaultValue[0])
+        this.output.uniforms[`${glslType} ${name}`] = defaultValue[0];
       }
-      this.output.uniforms[`${glslType} ${name}`] = defaultValue;
+
       let safeType = glslType === 'sampler2D' ? 'vec4' : glslType;
-      return new VariableNode(name, safeType);
+      const uniform = new VariableNode(name, safeType, false);
+      return uniform;
     };
+
     fn[uniformMethodName] = function (...args) { 
       return GLOBAL_SHADER[uniformMethodName](...args); 
     };
 
-    // Generate the create*() Methods for creating variables in shaders
+    // We don't need a createTexture method.
     if (glslType === 'sampler2D') { continue; }
+    
+    // Generate the create*() Methods for creating variables in shaders
     const createMethodName = `create${typeIdentifier}`;
     fn[createMethodName] = function (...value) {
       if(glslType.startsWith('vec')) {

From a0199d097c7e265174c9264347af237d7cc183d6 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Tue, 11 Mar 2025 18:26:36 +0000
Subject: [PATCH 34/54] dev sketch

---
 preview/global/sketch.js | 49 ++++++++++++++++++++++------------------
 1 file changed, 27 insertions(+), 22 deletions(-)

diff --git a/preview/global/sketch.js b/preview/global/sketch.js
index cf62f93bc2..d0218e9cea 100644
--- a/preview/global/sketch.js
+++ b/preview/global/sketch.js
@@ -1,52 +1,57 @@
 let myShader;
+let myShader2;
 p5.disableFriendlyErrors = true;
-
+function windowResized() {
+  resizeCanvas(windowWidth, windowHeight);
+}
 function calculateOffset() {
   return 30;
 }
 
 function setup(){
   createCanvas(windowWidth, windowHeight, WEBGL);
-
   // // Raw example
   myShader = baseMaterialShader().modify(() => {
-
-    const offset = uniformFloat(1);
-
+    const uCol = uniformVector4(0.1,0.1,0.1,1);
+    const time = uniformFloat(()=>millis);
     getFinalColor((col) => {
-      let a = createVector4(1, 2, 3, 4);
-      let b = createVector4(3, 4, 5, 6);
-      a = (a * b + offset) / 10;
-      col += a;
+      let x = createFloat(0.5);
+      col.x = createFloat(time);
+      col.w = 1;
+      col /= uCol;
       return col;
     });
-  });
-
+  }, { parser: true, srcLocations: true });
 
+  console.log(myShader)
   // Create and use the custom shader.
-  // myShader = baseMaterialShader().modify(
+  // myShader2 = baseMaterialShader().modify(
   //   () => {
-  //     const offset = uniformFloat('offset', () => calculateOffset)
+  //     // const offset = uniformFloat('offset', 1)
 
-  //     getWorldPosition((pos) => {
-  //       let a = createVector3(1, 2, 3);
-  //       let b = createVector3(3, 4, 5);
+  //     getFinalColor((pos) => {
+  //       let a = createVector4(1, 2, 3);
+  //       let b = createVector4(3, 4, 5);
   //       a = a.add(b);
 
   //       let c = a.add(b);
-  //       c += c.add(offset);
-  //       c.x = b.x.add(1);
-
+  //       // c += c.add(offset);
+  //       // c.x = b.x.add(1);
 
   //       pos = pos.add(c);
 
   //       return pos;
   //     })
-  //   }
-  // );
+  //   },  { parser: false, srcLocations: true });
 }
 
 function draw(){
   // Set the styles
-  background(0)
+  background(0);
+  // fill(0)
+  shader(myShader);
+  stroke('red')
+  fill(255,0,0)
+  // myShader.setUniform('uCol', [0.1,2,0,1])
+  sphere(100);
 }

From 79602a7406f7c75f00e1f831bfbe6559a6a622e0 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Tue, 11 Mar 2025 18:26:36 +0000
Subject: [PATCH 35/54] Console log the whole modify argument

---
 preview/global/sketch.js | 49 ++++++++++++++++++++++------------------
 src/webgl/ShaderGen.js   |  2 +-
 2 files changed, 28 insertions(+), 23 deletions(-)

diff --git a/preview/global/sketch.js b/preview/global/sketch.js
index cf62f93bc2..d0218e9cea 100644
--- a/preview/global/sketch.js
+++ b/preview/global/sketch.js
@@ -1,52 +1,57 @@
 let myShader;
+let myShader2;
 p5.disableFriendlyErrors = true;
-
+function windowResized() {
+  resizeCanvas(windowWidth, windowHeight);
+}
 function calculateOffset() {
   return 30;
 }
 
 function setup(){
   createCanvas(windowWidth, windowHeight, WEBGL);
-
   // // Raw example
   myShader = baseMaterialShader().modify(() => {
-
-    const offset = uniformFloat(1);
-
+    const uCol = uniformVector4(0.1,0.1,0.1,1);
+    const time = uniformFloat(()=>millis);
     getFinalColor((col) => {
-      let a = createVector4(1, 2, 3, 4);
-      let b = createVector4(3, 4, 5, 6);
-      a = (a * b + offset) / 10;
-      col += a;
+      let x = createFloat(0.5);
+      col.x = createFloat(time);
+      col.w = 1;
+      col /= uCol;
       return col;
     });
-  });
-
+  }, { parser: true, srcLocations: true });
 
+  console.log(myShader)
   // Create and use the custom shader.
-  // myShader = baseMaterialShader().modify(
+  // myShader2 = baseMaterialShader().modify(
   //   () => {
-  //     const offset = uniformFloat('offset', () => calculateOffset)
+  //     // const offset = uniformFloat('offset', 1)
 
-  //     getWorldPosition((pos) => {
-  //       let a = createVector3(1, 2, 3);
-  //       let b = createVector3(3, 4, 5);
+  //     getFinalColor((pos) => {
+  //       let a = createVector4(1, 2, 3);
+  //       let b = createVector4(3, 4, 5);
   //       a = a.add(b);
 
   //       let c = a.add(b);
-  //       c += c.add(offset);
-  //       c.x = b.x.add(1);
-
+  //       // c += c.add(offset);
+  //       // c.x = b.x.add(1);
 
   //       pos = pos.add(c);
 
   //       return pos;
   //     })
-  //   }
-  // );
+  //   },  { parser: false, srcLocations: true });
 }
 
 function draw(){
   // Set the styles
-  background(0)
+  background(0);
+  // fill(0)
+  shader(myShader);
+  stroke('red')
+  fill(255,0,0)
+  // myShader.setUniform('uCol', [0.1,2,0,1])
+  sphere(100);
 }
diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index df54b495d1..36d0606257 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -37,7 +37,7 @@ function shadergen(p5, fn) {
       const generator = new ShaderGenerator(generatorFunction, this, options.srcLocations)
       const generatedModifyArgument = generator.generate();
       console.log("SRC STRING: ", generatorFunction);
-      console.log("NEW OPTIONS:", generatedModifyArgument['vec4 getFinalColor'])
+      console.log("NEW OPTIONS:", generatedModifyArgument)
 
       return oldModify.call(this, generatedModifyArgument);
     } 

From 0797a1ba2b094e00ea91cbf8b4cf005211595e1e Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Tue, 11 Mar 2025 19:54:33 +0000
Subject: [PATCH 36/54] refactor / add qualifiers to hook arguments (i.e. in
 sampler2D etc). next to fix structs

---
 src/webgl/ShaderGen.js | 29 +++++++++++++----------------
 1 file changed, 13 insertions(+), 16 deletions(-)

diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index 36d0606257..1197ac0182 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -494,7 +494,7 @@ function shadergen(p5, fn) {
       GLOBAL_SHADER = this;
       this.modifyFunction = modifyFunction;
       this.srcLocations = srcLocations;
-      this.generateHookBuilders(originalShader);
+      this.generateHookOverrides(originalShader);
       this.output = {
         uniforms: {},
       }
@@ -507,31 +507,28 @@ function shadergen(p5, fn) {
       return this.output;
     }
 
-    generateHookBuilders(originalShader) {
+    generateHookOverrides(originalShader) {
       const availableHooks = {
         ...originalShader.hooks.vertex,
         ...originalShader.hooks.fragment,
       }
-      
       // Defines a function for each of the hooks for the shader we are modifying.
       Object.keys(availableHooks).forEach((hookName) => {
         const hookTypes = originalShader.hookTypes(hookName)
+        console.log(hookTypes);
         this[hookTypes.name] = function(userOverride) {
-          let hookArgs = []
-          let argsArray = [];
-
-          hookTypes.parameters.forEach((parameter, i) => {
-            hookArgs.push(
+          let argNodes = []
+          const argsArray = hookTypes.parameters.map((parameter) => {
+            argNodes.push(
               new VariableNode(parameter.name, parameter.type.typeName, true)
             );
-            if (i === 0) {
-              argsArray.push(`${parameter.type.typeName} ${parameter.name}`);
-            } else {
-              argsArray.push(`, ${parameter.type.typeName} ${parameter.name}`);
-            }
-          })
-          const toGLSLResult = userOverride(...hookArgs).toGLSLBase(this.context);
-          let codeLines = [`(${argsArray.join(', ')}) {`, this.context.declarations.slice()].flat();
+            const qualifiers = parameter.type.qualifiers.length > 0 ? parameter.type.qualifiers.join(' ') : '';
+            return `${qualifiers} ${parameter.type.typeName} ${parameter.name}`.trim();
+          });
+
+          const argsString = `(${argsArray.join(', ')}) {`;
+          const toGLSLResult = userOverride(...argNodes).toGLSLBase(this.context);
+          let codeLines = [ argsString, ...this.context.declarations ];
           codeLines.push(`\n${hookTypes.returnType.typeName} finalReturnValue = ${toGLSLResult};
                           \nreturn finalReturnValue;
                           \n}`);

From 98a78a7df34defe123fbecfcf0187d21f3d36333 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Wed, 12 Mar 2025 15:37:04 +0000
Subject: [PATCH 37/54] adding types to builtin functions

---
 src/webgl/ShaderGen.js | 105 ++++++++++++++++++++++++-----------------
 1 file changed, 62 insertions(+), 43 deletions(-)

diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index 1197ac0182..1fdd753c58 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -6,7 +6,7 @@
 */
 
 import { parse } from 'acorn';
-import { simple } from 'acorn-walk';
+import { ancestor } from 'acorn-walk';
 import escodegen from 'escodegen';
 
 function shadergen(p5, fn) {
@@ -23,7 +23,7 @@ function shadergen(p5, fn) {
           ecmaVersion: 2021, 
           locations: options.srcLocations 
         });
-        simple(ast, ASTCallbacks);
+        ancestor(ast, ASTCallbacks);
         const transpiledSource = escodegen.generate(ast);
         generatorFunction = new Function(
           transpiledSource.slice(
@@ -103,7 +103,6 @@ function shadergen(p5, fn) {
     },
   }
 
-
   // Javascript Node API.
   // These classes are for expressing GLSL functions in Javascript without
   // needing to  transpile the user's code.
@@ -114,7 +113,6 @@ function shadergen(p5, fn) {
       }
       this.type = type;
       this.componentNames = [];
-      this.swizzleAccessed = false;
       this.swizzleChanged = false;
       // For tracking recursion depth and creating temporary variables
       this.isInternal = isInternal;
@@ -151,7 +149,6 @@ function shadergen(p5, fn) {
           let value = new ComponentNode(this, componentName, 'float', true);
           Object.defineProperty(this, componentName, {
             get() {
-              this.swizzleAccessed = true;
               return value;
             },
             set(newValue) {
@@ -215,7 +212,6 @@ function shadergen(p5, fn) {
     mod(other)  { return new ModulusNode(this, this.enforceType(other)); }
 
     // Check that the types of the operands are compatible.
-    // TODO: improve this with less branching if elses
     enforceType(other){
       if (isShaderNode(other)){
         if ((isFloatNode(this) || isVectorNode(this)) && isIntNode(other)) {
@@ -228,7 +224,10 @@ function shadergen(p5, fn) {
           return new IntNode(other);
         }
         return new FloatNode(other);
-      } 
+      }
+      else if(Array.isArray(other)) {
+        return new VectorNode(other, `vec${other.length}`)
+      }
       else {
         return new this.constructor(other);
       }
@@ -474,7 +473,7 @@ function shadergen(p5, fn) {
     return (isShaderNode(node) && (node.type === 'float')); 
   }
 
-  function isVectorNode(node) { 
+  function isVectorNode(node) {
     return (isShaderNode(node) && (node.type === 'vec2'|| node.type === 'vec3' || node.type === 'vec4')); 
   }
 
@@ -507,17 +506,20 @@ function shadergen(p5, fn) {
       return this.output;
     }
 
+    // This method generates the hook overrides which the user calls in their modify function.
     generateHookOverrides(originalShader) {
       const availableHooks = {
         ...originalShader.hooks.vertex,
         ...originalShader.hooks.fragment,
       }
-      // Defines a function for each of the hooks for the shader we are modifying.
+
       Object.keys(availableHooks).forEach((hookName) => {
         const hookTypes = originalShader.hookTypes(hookName)
         console.log(hookTypes);
+
         this[hookTypes.name] = function(userOverride) {
           let argNodes = []
+
           const argsArray = hookTypes.parameters.map((parameter) => {
             argNodes.push(
               new VariableNode(parameter.name, parameter.type.typeName, true)
@@ -525,9 +527,14 @@ function shadergen(p5, fn) {
             const qualifiers = parameter.type.qualifiers.length > 0 ? parameter.type.qualifiers.join(' ') : '';
             return `${qualifiers} ${parameter.type.typeName} ${parameter.name}`.trim();
           });
-
           const argsString = `(${argsArray.join(', ')}) {`;
-          const toGLSLResult = userOverride(...argNodes).toGLSLBase(this.context);
+          
+          let returnedValue = userOverride(...argNodes);
+          if (!isShaderNode(returnedValue)) {
+              const expectedReturnType = hookTypes.returnType;
+              returnedValue = nodeConstructors[expectedReturnType.typeName](returnedValue)
+          }
+          const toGLSLResult = returnedValue.toGLSLBase(this.context);
           let codeLines = [ argsString, ...this.context.declarations ];
           codeLines.push(`\n${hookTypes.returnType.typeName} finalReturnValue = ${toGLSLResult};
                           \nreturn finalReturnValue;
@@ -602,13 +609,12 @@ function shadergen(p5, fn) {
 
     ShaderGenerator.prototype[uniformMethodName] = function(...args) {
       let [name, ...defaultValue] = args;
+
       if(glslType.startsWith('vec')) {
         defaultValue = conformVectorParameters(defaultValue, +glslType.slice(3));
         this.output.uniforms[`${glslType} ${name}`] = defaultValue;
       } 
       else {
-        console.log("defaultValue: ",defaultValue);
-        console.log("defaultValue[0]: ",defaultValue[0])
         this.output.uniforms[`${glslType} ${name}`] = defaultValue[0];
       }
 
@@ -637,59 +643,69 @@ function shadergen(p5, fn) {
   }
   
   // GLSL Built in functions
-  // TODO: 
   // Add a whole lot of these functions. 
   // https://docs.gl/el3/abs
+  // TODO: 
+  //  In reality many of these have multiple overrides which will need to address later.
+  // Also, their return types depend on the genType which will need to address urgently
+  //      genType clamp(genType x,
+  //                    genType minVal,
+  //                    genType maxVal);
+  //      genType clamp(genType x,
+  //                    float minVal,
+  //                    float maxVal);
+
 
-  // constructor(name, args, type, isInternal = false) {
 
   const builtInFunctions = {
-    // Trigonometry
-    'acos': {},
+    //////////// Trigonometry //////////
+    'acos': { args: ['genType'], returnType: 'float'},
     // 'acosh': {},
-    'asin': {},
+    'asin': { args: ['genType'], returnType: 'float'},
     // 'asinh': {},
-    'atan': {},
+    'atan': { args: ['genType'], returnType: 'float'},
     // 'atanh': {},
-    'cos': {},
+    'cos': { args: ['genType'], returnType: 'float', isp5Function: true},
     // 'cosh': {},
-    'degrees': {},
-    'radians': {},
-    'sin': {},
+    'degrees': { args: ['genType'], returnType: 'float'},
+    'radians': { args: ['genType'], returnType: 'float'},
+    'sin': { args: ['genType'], returnType: 'float' , isp5Function: true},
     // 'sinh': {},
-    'tan': {},
+    'tan': { args: ['genType'], returnType: 'float', isp5Function: true},
     // 'tanh': {},
-    // Mathematics
-    'abs': {},
-    'ceil': {},
-    'clamp': {},
-    'dFdx': {},
-    'dFdy': {},
-    'exp': {},
-    'exp2': {},
-    'floor': {},
+
+    ////////// Mathematics //////////
+    'abs': { args: ['genType'], returnType: 'float'},
+    'ceil': { args: ['genType'], returnType: 'float'},
+    'clamp': { args: ['genType', 'genType', 'genType'], returnType: 'float'},
+    // 'dFdx': {},
+    // 'dFdy': {},
+    'exp': { args: ['genType'], returnType: 'float'},
+    'exp2': { args: ['genType'], returnType: 'float'},
+    'floor': { args: ['genType'], returnType: 'float'},
     // 'fma': {},
-    'fract': {},
+    'fract': { args: ['genType'], returnType: 'float'},
     // 'fwidth': {},
     // 'inversesqrt': {},
     // 'isinf': {},
     // 'isnan': {},
     // 'log': {},
     // 'log2': {},
-    'max': {},
-    'min': {},
-    'mix': {},
+    'max': { args: ['genType'], returnType: 'genType'},
+    'min': { args: ['genType'], returnType: 'genType'},
+    'mix': { args: ['genType'], returnType: 'genType'},
     // 'mod': {},
     // 'modf': {},
-    'pow': {},
+    'pow': { args: ['genType'], returnType: 'float'},
     // 'round': {},
     // 'roundEven': {},
     // 'sign': {},
-    'smoothstep': {},
-    'sqrt': {},
-    'step': {},
+    'smoothstep': { args: ['genType', 'genType', 'genType'], returnType: 'float'},
+    'sqrt': { args: ['genType'], returnType: 'genType'},
+    'step': { args: ['genType', 'genType'], returnType: 'genType'},
     // 'trunc': {},
-    // Vector
+
+    ////////// Vector //////////
     'cross': {},
     'distance': {},
     'dot': {},
@@ -704,7 +720,10 @@ function shadergen(p5, fn) {
     'texture': {},
   }
 
-  // Object.entries(builtInFunctions).forEach(([glslFnName, properties]) => {
+  // Object.entries(builtInFunctions).forEach(([functionName, properties]) => {
+  //   fn[functionName] = function () {
+  //     new FunctionCallNode(functionName, args, type, isInternal = false)
+  //   }
   // })
 
   const oldTexture = p5.prototype.texture;

From b8072f985abcea2a113307ffe51f2fd660954fdf Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Wed, 12 Mar 2025 15:37:19 +0000
Subject: [PATCH 38/54] sketch example

---
 preview/global/sketch.js | 72 +++++++++++++++++++++-------------------
 1 file changed, 37 insertions(+), 35 deletions(-)

diff --git a/preview/global/sketch.js b/preview/global/sketch.js
index d0218e9cea..7477adfa50 100644
--- a/preview/global/sketch.js
+++ b/preview/global/sketch.js
@@ -1,5 +1,6 @@
 let myShader;
 let myShader2;
+
 p5.disableFriendlyErrors = true;
 function windowResized() {
   resizeCanvas(windowWidth, windowHeight);
@@ -8,50 +9,51 @@ function calculateOffset() {
   return 30;
 }
 
+function myCol() {
+  const col = (sin(millis() * 0.001) + 1)/2;
+  return col;
+}
+
 function setup(){
   createCanvas(windowWidth, windowHeight, WEBGL);
-  // // Raw example
+
   myShader = baseMaterialShader().modify(() => {
-    const uCol = uniformVector4(0.1,0.1,0.1,1);
-    const time = uniformFloat(()=>millis);
-    getFinalColor((col) => {
-      let x = createFloat(0.5);
-      col.x = createFloat(time);
-      col.w = 1;
-      col /= uCol;
-      return col;
+    const uCol = uniformVector4(1,0, 0,1);
+    const time = uniformFloat(() => millis());
+    getFinalColor((color) => {
+      color = uCol;
+      color.y = 1;
+      let x = createVector4(time);
+      x.x += createFloat(2);
+      color += x; 
+      return color;
     });
+    // getWorldInputs((Inputs) => {
+    //   console.log(Inputs)
+    // })
   }, { parser: true, srcLocations: true });
-
-  console.log(myShader)
-  // Create and use the custom shader.
-  // myShader2 = baseMaterialShader().modify(
-  //   () => {
-  //     // const offset = uniformFloat('offset', 1)
-
-  //     getFinalColor((pos) => {
-  //       let a = createVector4(1, 2, 3);
-  //       let b = createVector4(3, 4, 5);
-  //       a = a.add(b);
-
-  //       let c = a.add(b);
-  //       // c += c.add(offset);
-  //       // c.x = b.x.add(1);
-
-  //       pos = pos.add(c);
-
-  //       return pos;
-  //     })
-  //   },  { parser: false, srcLocations: true });
 }
 
 function draw(){
-  // Set the styles
   background(0);
-  // fill(0)
   shader(myShader);
-  stroke('red')
-  fill(255,0,0)
-  // myShader.setUniform('uCol', [0.1,2,0,1])
+  fill(0,0,0)
   sphere(100);
 }
+
+`(vec4 color) {
+  // From at <computed> [as uniformVector4] (http://localhost:5173/p5.js:86002:25)
+  vec4 temp_0 = uCol;
+  temp_0 = vec4(temp_0.x, 1.0000, temp_0.z, temp_0.w);
+  vec4 finalReturnValue = temp_0;
+  return finalReturnValue;
+}`
+`
+(vec4 color) {
+
+// From at <computed> [as uniformVector4] (http://localhost:5173/p5.js:86002:25)
+vec4 temp_0 = uCol;
+temp_0 = vec4(temp_0.x, 1.0000, temp_0.z, temp_0.w);
+vec4 finalReturnValue = temp_0 + vec4(0.0000 + 2.0000, 0.0000, 0.0000, 0.0000);
+return finalReturnValue;
+}`
\ No newline at end of file

From 926f79feced8d9f0fb780007f484097cf1d6a579 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Thu, 13 Mar 2025 13:18:05 +0000
Subject: [PATCH 39/54] allow literals on lhs before transpile, dont transpile
 uniform default vals

---
 src/webgl/ShaderGen.js | 46 ++++++++++++++++++++++++++++++++++++------
 1 file changed, 40 insertions(+), 6 deletions(-)

diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index 1fdd753c58..54157f2a9e 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -38,7 +38,6 @@ function shadergen(p5, fn) {
       const generatedModifyArgument = generator.generate();
       console.log("SRC STRING: ", generatorFunction);
       console.log("NEW OPTIONS:", generatedModifyArgument)
-
       return oldModify.call(this, generatedModifyArgument);
     } 
     else {
@@ -58,7 +57,7 @@ function shadergen(p5, fn) {
   }
 
   const ASTCallbacks = {
-    VariableDeclarator(node) {
+    VariableDeclarator(node, _state, _ancestors) {
       if (node.init.callee && node.init.callee.name.startsWith('uniform')) {
         const uniformNameLiteral = {
           type: 'Literal',
@@ -69,7 +68,7 @@ function shadergen(p5, fn) {
     },
     // The callbacks for AssignmentExpression and BinaryExpression handle
     // operator overloading including +=, *= assignment expressions 
-    AssignmentExpression(node) {
+    AssignmentExpression(node, _state, _ancestors) {
       if (node.operator !== '=') {
         const methodName = replaceBinaryOperator(node.operator.replace('=',''));
         const rightReplacementNode = {
@@ -89,7 +88,35 @@ function shadergen(p5, fn) {
           node.right = rightReplacementNode;
         }
       },
-    BinaryExpression(node) {
+    BinaryExpression(node, _state, ancestors) {
+      // Don't convert uniform default values to node methods, as 
+      // they should be evaluated at runtime, not compiled.
+      const isUniform = (ancestor) => {
+        return ancestor.type === 'CallExpression'
+          && ancestor.callee?.type === 'Identifier'
+          && ancestor.callee?.name.startsWith('uniform');
+      }
+      if (ancestors.some(isUniform)) {
+        return;
+      }
+      // If the left hand side of an expression is one of these types,
+      // we should construct a node from it.
+      const unsafeTypes = ["Literal", "ArrayExpression"]
+      if (unsafeTypes.includes(node.left.type)) {
+        const leftReplacementNode = {
+          type: "CallExpression",
+          callee: {
+            type: "Identifier",
+            name: "makeNode",
+          },
+          arguments: [node.left, node.right]
+        }
+        node.left = leftReplacementNode;
+        console.log(escodegen.generate(leftReplacementNode))
+      }
+
+      // Replace the binary operator with a call expression
+      // in other words a call to BaseNode.mult(), .div() etc.
       node.type = 'CallExpression';
       node.callee = {
         type: "MemberExpression",
@@ -103,6 +130,14 @@ function shadergen(p5, fn) {
     },
   }
 
+  // This unfinished function lets you do 1 * 10
+  // and turns it into float.mult(10)
+  fn.makeNode = function(leftValue, rightValue) {
+    if (typeof leftValue === 'number') {
+      return new FloatNode(leftValue);
+    }
+  }
+
   // Javascript Node API.
   // These classes are for expressing GLSL functions in Javascript without
   // needing to  transpile the user's code.
@@ -192,7 +227,6 @@ function shadergen(p5, fn) {
           for (let componentName of this.componentNames) {
             valueArgs.push(this[componentName])
           }
-          console.log(this, valueArgs)
           const replacement = nodeConstructors[this.type](valueArgs)
           line += this.type + " " + this.temporaryVariable + " = " + this.toGLSL(context) + ";";
           line += `\n` + this.temporaryVariable + " = " + replacement.toGLSL(context) + ";";
@@ -515,7 +549,7 @@ function shadergen(p5, fn) {
 
       Object.keys(availableHooks).forEach((hookName) => {
         const hookTypes = originalShader.hookTypes(hookName)
-        console.log(hookTypes);
+        // console.log(hookTypes);
 
         this[hookTypes.name] = function(userOverride) {
           let argNodes = []

From 01ef185bce77d293631b7ff9ce71c883e0de38b6 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Thu, 13 Mar 2025 15:16:32 +0000
Subject: [PATCH 40/54] example sketch

---
 preview/global/sketch.js | 53 +++++++++++++++++++++++++---------------
 1 file changed, 33 insertions(+), 20 deletions(-)

diff --git a/preview/global/sketch.js b/preview/global/sketch.js
index 0ae3b8faa1..46b7d00a22 100644
--- a/preview/global/sketch.js
+++ b/preview/global/sketch.js
@@ -1,13 +1,9 @@
-let myShader;
-
-let myShader2;
 p5.disableFriendlyErrors = true;
 function windowResized() {
   resizeCanvas(windowWidth, windowHeight);
 }
-function calculateOffset() {
-  return 30;
-}
+
+let myShader;
 
 function myCol() {
   const col = (sin(millis() * 0.001) + 1)/2;
@@ -16,25 +12,42 @@ function myCol() {
 
 function setup(){
   createCanvas(windowWidth, windowHeight, WEBGL);
-
   myShader = baseMaterialShader().modify(() => {
-
-    const offset = uniformFloat(1);
-
+    const time = uniformFloat(() => sin(millis()*0.001));
     getFinalColor((col) => {
-      let a = createVector4(1, 2, 3, 4);
-      let b = createVector4(3, 4, 5, 6);
-      a = (a * b + offset) / 10;
-      col += a;
+      col.x = uvCoords().x;
+      col.y = uvCoords().y;
       return col;
     });
-    // getWorldInputs((Inputs) => {
-    //   console.log(Inputs)
-    // })
-  }, { parser: true, srcLocations: true });
+    getWorldInputs((inputs) => {
+      inputs.position.x += time * inputs.position.y;
+      return inputs;
+    })
+  }, { parser: true, srcLocations: false });
 }
 
 function draw(){
-  // Set the styles
-  background(0)
+  orbitControl();
+  background(0);
+  shader(myShader);
+  noStroke();
+  fill(255,0,0)
+  sphere(100);
 }
+
+// `(vec4 color) {
+//   // From at <computed> [as uniformVector4] (http://localhost:5173/p5.js:86002:25)
+//   vec4 temp_0 = uCol;
+//   temp_0 = vec4(temp_0.x, 1.0000, temp_0.z, temp_0.w);
+//   vec4 finalReturnValue = temp_0;
+//   return finalReturnValue;
+// }`
+// `
+// (vec4 color) {
+
+// // From at <computed> [as uniformVector4] (http://localhost:5173/p5.js:86002:25)
+// vec4 temp_0 = uCol;
+// temp_0 = vec4(temp_0.x, 1.0000, temp_0.z, temp_0.w);
+// vec4 finalReturnValue = temp_0 + vec4(0.0000 + 2.0000, 0.0000, 0.0000, 0.0000);
+// return finalReturnValue;
+// }`
\ No newline at end of file

From 9cac0726c5e7a4a60970f5c98655376def14133e Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Thu, 13 Mar 2025 15:16:47 +0000
Subject: [PATCH 41/54] input structs now working

---
 src/webgl/ShaderGen.js | 87 ++++++++++++++++++++++++++++--------------
 1 file changed, 59 insertions(+), 28 deletions(-)

diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index 54157f2a9e..febb2a1192 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -112,7 +112,6 @@ function shadergen(p5, fn) {
           arguments: [node.left, node.right]
         }
         node.left = leftReplacementNode;
-        console.log(escodegen.generate(leftReplacementNode))
       }
 
       // Replace the binary operator with a call expression
@@ -141,6 +140,7 @@ function shadergen(p5, fn) {
   // Javascript Node API.
   // These classes are for expressing GLSL functions in Javascript without
   // needing to  transpile the user's code.
+
   class BaseNode {
     constructor(isInternal, type) {
       if (new.target === BaseNode) {
@@ -248,6 +248,9 @@ function shadergen(p5, fn) {
     // Check that the types of the operands are compatible.
     enforceType(other){
       if (isShaderNode(other)){
+        if (!isGLSLNativeType(other.type)) {
+          throw new TypeError (`You've tried to perform an operation on a struct of type: ${other.type}. Try accessing a member on that struct with '.'`)
+        }
         if ((isFloatNode(this) || isVectorNode(this)) && isIntNode(other)) {
           return new FloatNode(other)
         }
@@ -366,6 +369,7 @@ function shadergen(p5, fn) {
       this.name = name;
       this.addVectorComponents();
     }
+
     toGLSL(context) {
       return `${this.name}`;
     }
@@ -519,13 +523,20 @@ function shadergen(p5, fn) {
     return (node instanceof VariableNode || node instanceof ComponentNode || typeof(node.temporaryVariable) != 'undefined'); 
   }
 
+    // Helper function to check if a type is a user defined struct or native type
+    function isGLSLNativeType(typeName) {
+      // Supported types for now
+      const glslNativeTypes = ['int', 'float', 'vec2', 'vec3', 'vec4', 'sampler2D'];
+      return glslNativeTypes.includes(typeName);
+    }
+
   // Shader Generator
   // This class is responsible for converting the nodes into an object containing GLSL code, to be used by p5.Shader.modify
 
   class ShaderGenerator {
-    constructor(modifyFunction, originalShader, srcLocations) {
+    constructor(userCallback, originalShader, srcLocations) {
       GLOBAL_SHADER = this;
-      this.modifyFunction = modifyFunction;
+      this.userCallback = userCallback;
       this.srcLocations = srcLocations;
       this.generateHookOverrides(originalShader);
       this.output = {
@@ -535,8 +546,7 @@ function shadergen(p5, fn) {
     }
 
     generate() {
-      this.modifyFunction();
-      console.log(this.output);
+      this.userCallback();
       return this.output;
     }
 
@@ -548,31 +558,52 @@ function shadergen(p5, fn) {
       }
 
       Object.keys(availableHooks).forEach((hookName) => {
-        const hookTypes = originalShader.hookTypes(hookName)
-        // console.log(hookTypes);
-
-        this[hookTypes.name] = function(userOverride) {
+        const hookTypes = originalShader.hookTypes(hookName);
+        this[hookTypes.name] = function(userCallback) {
+          // Create the initial nodes which are passed to the user callback
+          // Also generate a string of the arguments for the code generation
           let argNodes = []
-
-          const argsArray = hookTypes.parameters.map((parameter) => {
-            argNodes.push(
-              new VariableNode(parameter.name, parameter.type.typeName, true)
-            );
+          let argsArray = [];
+          hookTypes.parameters.forEach((parameter) => {
+            if (!isGLSLNativeType(parameter.type.typeName)) {
+              let structArg = {};
+              parameter.type.properties.forEach((property) => {
+                structArg[property.name] = new VariableNode(`${parameter.name}.${property.name}`, property.type.typeName, true);
+              });
+              argNodes.push(structArg);
+            } else {
+              argNodes.push(
+                new VariableNode(parameter.name, parameter.type.typeName, true)
+              );
+            }
             const qualifiers = parameter.type.qualifiers.length > 0 ? parameter.type.qualifiers.join(' ') : '';
-            return `${qualifiers} ${parameter.type.typeName} ${parameter.name}`.trim();
-          });
-          const argsString = `(${argsArray.join(', ')}) {`;
+            argsArray.push(`${qualifiers} ${parameter.type.typeName} ${parameter.name}`.trim())
+          })
           
-          let returnedValue = userOverride(...argNodes);
-          if (!isShaderNode(returnedValue)) {
-              const expectedReturnType = hookTypes.returnType;
+          let returnedValue = userCallback(...argNodes);
+          const expectedReturnType = hookTypes.returnType;
+          const toGLSLResults = {};
+          // If the expected return type is a struct we need to evaluate each of its properties
+          if (!isGLSLNativeType(expectedReturnType.typeName)) {
+            Object.entries(returnedValue).forEach(([propertyName, propertyNode]) => {
+              toGLSLResults[propertyName] = propertyNode.toGLSLBase(this.context);
+            })
+          } else {
+            // We can accept raw numbers or arrays otherwise
+            if (!isShaderNode(returnedValue)) {
               returnedValue = nodeConstructors[expectedReturnType.typeName](returnedValue)
+            }
+            toGLSLResults['notAProperty'] = returnedValue.toGLSLBase(this.context);
           }
-          const toGLSLResult = returnedValue.toGLSLBase(this.context);
-          let codeLines = [ argsString, ...this.context.declarations ];
-          codeLines.push(`\n${hookTypes.returnType.typeName} finalReturnValue = ${toGLSLResult};
-                          \nreturn finalReturnValue;
-                          \n}`);
+          
+          // Build the final GLSL string:
+          let codeLines = [ `(${argsArray.join(', ')}) {`, ...this.context.declarations ];
+          codeLines.push(`${hookTypes.returnType.typeName} finalReturnValue;`);
+          Object.entries(toGLSLResults).forEach(([propertyName, result]) => {
+            const propString = expectedReturnType.properties ? `.${propertyName}` : '';
+            codeLines.push(`finalReturnValue${propString} = ${result};`)
+          })
+          codeLines.push('return finalReturnValue;', '}');
           this.output[hookName] = codeLines.join('\n');
           this.resetGLSLContext();
         }
@@ -619,7 +650,7 @@ function shadergen(p5, fn) {
   
   // Generating uniformFloat, uniformVec, createFloat, etc functions
   // Maps a GLSL type to the name suffix for method names
-  const GLSLTypesToSuffixes = {
+  const GLSLTypesToIdentifiers = {
     int:    'Int',
     float:  'Float',
     vec2:   'Vector2',
@@ -636,9 +667,9 @@ function shadergen(p5, fn) {
     vec4:  (value) => new VectorNode(value, 'vec4'),
   };
 
-  for (const glslType in GLSLTypesToSuffixes) {
+  for (const glslType in GLSLTypesToIdentifiers) {
     // Generate uniform*() Methods for creating uniforms
-    const typeIdentifier = GLSLTypesToSuffixes[glslType];
+    const typeIdentifier = GLSLTypesToIdentifiers[glslType];
     const uniformMethodName = `uniform${typeIdentifier}`;
 
     ShaderGenerator.prototype[uniformMethodName] = function(...args) {

From 951ee60cb1e6f6274d67dc1a018453b290ba7d98 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Thu, 13 Mar 2025 15:16:47 +0000
Subject: [PATCH 42/54] input structs now working

---
 src/webgl/ShaderGen.js | 98 +++++++++++++++++++++++++++++-------------
 1 file changed, 69 insertions(+), 29 deletions(-)

diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
index 54157f2a9e..5e44c5de19 100644
--- a/src/webgl/ShaderGen.js
+++ b/src/webgl/ShaderGen.js
@@ -112,7 +112,6 @@ function shadergen(p5, fn) {
           arguments: [node.left, node.right]
         }
         node.left = leftReplacementNode;
-        console.log(escodegen.generate(leftReplacementNode))
       }
 
       // Replace the binary operator with a call expression
@@ -141,6 +140,7 @@ function shadergen(p5, fn) {
   // Javascript Node API.
   // These classes are for expressing GLSL functions in Javascript without
   // needing to  transpile the user's code.
+
   class BaseNode {
     constructor(isInternal, type) {
       if (new.target === BaseNode) {
@@ -248,6 +248,9 @@ function shadergen(p5, fn) {
     // Check that the types of the operands are compatible.
     enforceType(other){
       if (isShaderNode(other)){
+        if (!isGLSLNativeType(other.type)) {
+          throw new TypeError (`You've tried to perform an operation on a struct of type: ${other.type}. Try accessing a member on that struct with '.'`)
+        }
         if ((isFloatNode(this) || isVectorNode(this)) && isIntNode(other)) {
           return new FloatNode(other)
         }
@@ -366,6 +369,7 @@ function shadergen(p5, fn) {
       this.name = name;
       this.addVectorComponents();
     }
+
     toGLSL(context) {
       return `${this.name}`;
     }
@@ -519,13 +523,20 @@ function shadergen(p5, fn) {
     return (node instanceof VariableNode || node instanceof ComponentNode || typeof(node.temporaryVariable) != 'undefined'); 
   }
 
+    // Helper function to check if a type is a user defined struct or native type
+    function isGLSLNativeType(typeName) {
+      // Supported types for now
+      const glslNativeTypes = ['int', 'float', 'vec2', 'vec3', 'vec4', 'sampler2D'];
+      return glslNativeTypes.includes(typeName);
+    }
+
   // Shader Generator
   // This class is responsible for converting the nodes into an object containing GLSL code, to be used by p5.Shader.modify
 
   class ShaderGenerator {
-    constructor(modifyFunction, originalShader, srcLocations) {
+    constructor(userCallback, originalShader, srcLocations) {
       GLOBAL_SHADER = this;
-      this.modifyFunction = modifyFunction;
+      this.userCallback = userCallback;
       this.srcLocations = srcLocations;
       this.generateHookOverrides(originalShader);
       this.output = {
@@ -535,8 +546,7 @@ function shadergen(p5, fn) {
     }
 
     generate() {
-      this.modifyFunction();
-      console.log(this.output);
+      this.userCallback();
       return this.output;
     }
 
@@ -548,31 +558,61 @@ function shadergen(p5, fn) {
       }
 
       Object.keys(availableHooks).forEach((hookName) => {
-        const hookTypes = originalShader.hookTypes(hookName)
-        // console.log(hookTypes);
-
-        this[hookTypes.name] = function(userOverride) {
-          let argNodes = []
-
-          const argsArray = hookTypes.parameters.map((parameter) => {
-            argNodes.push(
-              new VariableNode(parameter.name, parameter.type.typeName, true)
-            );
+        const hookTypes = originalShader.hookTypes(hookName);
+        this[hookTypes.name] = function(userCallback) {
+          // Create the initial nodes which are passed to the user callback
+          // Also generate a string of the arguments for the code generation
+          const argNodes = []
+          const argsArray = [];
+
+          hookTypes.parameters.forEach((parameter) => {
+            // For hooks with structs as input we should pass an object populated with variable nodes 
+            if (!isGLSLNativeType(parameter.type.typeName)) {
+              const structArg = {};
+              parameter.type.properties.forEach((property) => {
+                structArg[property.name] = new VariableNode(`${parameter.name}.${property.name}`, property.type.typeName, true);
+              });
+              argNodes.push(structArg);
+            } else {
+              argNodes.push(
+                new VariableNode(parameter.name, parameter.type.typeName, true)
+              );
+            }
             const qualifiers = parameter.type.qualifiers.length > 0 ? parameter.type.qualifiers.join(' ') : '';
-            return `${qualifiers} ${parameter.type.typeName} ${parameter.name}`.trim();
-          });
-          const argsString = `(${argsArray.join(', ')}) {`;
+            argsArray.push(`${qualifiers} ${parameter.type.typeName} ${parameter.name}`.trim())
+          })
           
-          let returnedValue = userOverride(...argNodes);
-          if (!isShaderNode(returnedValue)) {
-              const expectedReturnType = hookTypes.returnType;
+          let returnedValue = userCallback(...argNodes);
+          const expectedReturnType = hookTypes.returnType;
+          const toGLSLResults = {};
+
+          // If the expected return type is a struct we need to evaluate each of its properties
+          if (!isGLSLNativeType(expectedReturnType.typeName)) {
+            Object.entries(returnedValue).forEach(([propertyName, propertyNode]) => {
+              toGLSLResults[propertyName] = propertyNode.toGLSLBase(this.context);
+            })
+          } else {
+            // We can accept raw numbers or arrays otherwise
+            if (!isShaderNode(returnedValue)) {
               returnedValue = nodeConstructors[expectedReturnType.typeName](returnedValue)
+            }
+            toGLSLResults['notAProperty'] = returnedValue.toGLSLBase(this.context);
           }
-          const toGLSLResult = returnedValue.toGLSLBase(this.context);
-          let codeLines = [ argsString, ...this.context.declarations ];
-          codeLines.push(`\n${hookTypes.returnType.typeName} finalReturnValue = ${toGLSLResult};
-                          \nreturn finalReturnValue;
-                          \n}`);
+          
+          // Build the final GLSL string.
+          // The order of this code is a bit confusing, we need to call toGLSLBase
+          let codeLines = [ 
+            `(${argsArray.join(', ')}) {`,
+            ...this.context.declarations,
+            `${hookTypes.returnType.typeName} finalReturnValue;`
+          ];
+
+          Object.entries(toGLSLResults).forEach(([propertyName, result]) => {
+            const propString = expectedReturnType.properties ? `.${propertyName}` : '';
+            codeLines.push(`finalReturnValue${propString} = ${result};`)
+          })
+
+          codeLines.push('return finalReturnValue;', '}');
           this.output[hookName] = codeLines.join('\n');
           this.resetGLSLContext();
         }
@@ -619,7 +659,7 @@ function shadergen(p5, fn) {
   
   // Generating uniformFloat, uniformVec, createFloat, etc functions
   // Maps a GLSL type to the name suffix for method names
-  const GLSLTypesToSuffixes = {
+  const GLSLTypesToIdentifiers = {
     int:    'Int',
     float:  'Float',
     vec2:   'Vector2',
@@ -636,9 +676,9 @@ function shadergen(p5, fn) {
     vec4:  (value) => new VectorNode(value, 'vec4'),
   };
 
-  for (const glslType in GLSLTypesToSuffixes) {
+  for (const glslType in GLSLTypesToIdentifiers) {
     // Generate uniform*() Methods for creating uniforms
-    const typeIdentifier = GLSLTypesToSuffixes[glslType];
+    const typeIdentifier = GLSLTypesToIdentifiers[glslType];
     const uniformMethodName = `uniform${typeIdentifier}`;
 
     ShaderGenerator.prototype[uniformMethodName] = function(...args) {

From a6101c7c7f846a2781d35ca2600d9603e084a6cf Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Thu, 13 Mar 2025 15:35:55 +0000
Subject: [PATCH 43/54] fix merge issues

---
 src/webgl/ShaderGenerator.js | 830 +++++++++++++++++++++++++++++++++++
 1 file changed, 830 insertions(+)
 create mode 100644 src/webgl/ShaderGenerator.js

diff --git a/src/webgl/ShaderGenerator.js b/src/webgl/ShaderGenerator.js
new file mode 100644
index 0000000000..0da6b2f523
--- /dev/null
+++ b/src/webgl/ShaderGenerator.js
@@ -0,0 +1,830 @@
+/**
+* @module 3D
+* @submodule ShaderGenerator
+* @for p5
+* @requires core
+*/
+
+import { parse } from 'acorn';
+import { ancestor } from 'acorn-walk';
+import escodegen from 'escodegen';
+
+function shadergen(p5, fn) {
+  let GLOBAL_SHADER;
+  
+  const oldModify = p5.Shader.prototype.modify
+
+  p5.Shader.prototype.modify = function(shaderModifier, options = { parser: true, srcLocations: false }) {
+    if (shaderModifier instanceof Function) {
+      let generatorFunction;
+      if (options.parser) {
+        const sourceString = shaderModifier.toString()
+        const ast = parse(sourceString, { 
+          ecmaVersion: 2021, 
+          locations: options.srcLocations 
+        });
+        ancestor(ast, ASTCallbacks);
+        const transpiledSource = escodegen.generate(ast);
+        generatorFunction = new Function(
+          transpiledSource.slice(
+            transpiledSource.indexOf("{") + 1,
+            transpiledSource.lastIndexOf("}")
+          )
+        );
+      } else {
+        generatorFunction = shaderModifier;
+      }
+      const generator = new ShaderGenerator(generatorFunction, this, options.srcLocations)
+      const generatedModifyArgument = generator.generate();
+      console.log("SRC STRING: ", generatorFunction);
+      console.log("NEW OPTIONS:", generatedModifyArgument)
+      return oldModify.call(this, generatedModifyArgument);
+    } 
+    else {
+      return oldModify.call(this, shaderModifier)
+    }
+  }
+
+  // AST Transpiler Callbacks and their helpers
+  function replaceBinaryOperator(codeSource) {
+    switch (codeSource) {
+      case '+': return 'add';
+      case '-': return 'sub';
+      case '*': return 'mult';
+      case '/': return 'div';
+      case '%': return 'mod';
+    }
+  }
+
+  const ASTCallbacks = {
+    VariableDeclarator(node, _state, _ancestors) {
+      if (node.init.callee && node.init.callee.name.startsWith('uniform')) {
+        const uniformNameLiteral = {
+          type: 'Literal',
+          value: node.id.name
+        }
+        node.init.arguments.unshift(uniformNameLiteral);
+      }
+    },
+    // The callbacks for AssignmentExpression and BinaryExpression handle
+    // operator overloading including +=, *= assignment expressions 
+    AssignmentExpression(node, _state, _ancestors) {
+      if (node.operator !== '=') {
+        const methodName = replaceBinaryOperator(node.operator.replace('=',''));
+        const rightReplacementNode = {
+          type: 'CallExpression',
+          callee: {
+            type: "MemberExpression",
+            object: node.left,
+            property: {
+              type: "Identifier",
+              name: methodName,
+            },
+            computed: false,
+          },
+          arguments: [node.right]
+        }
+          node.operator = '=';
+          node.right = rightReplacementNode;
+        }
+      },
+    BinaryExpression(node, _state, ancestors) {
+      // Don't convert uniform default values to node methods, as 
+      // they should be evaluated at runtime, not compiled.
+      const isUniform = (ancestor) => {
+        return ancestor.type === 'CallExpression'
+          && ancestor.callee?.type === 'Identifier'
+          && ancestor.callee?.name.startsWith('uniform');
+      }
+      if (ancestors.some(isUniform)) {
+        return;
+      }
+      // If the left hand side of an expression is one of these types,
+      // we should construct a node from it.
+      const unsafeTypes = ["Literal", "ArrayExpression"]
+      if (unsafeTypes.includes(node.left.type)) {
+        const leftReplacementNode = {
+          type: "CallExpression",
+          callee: {
+            type: "Identifier",
+            name: "makeNode",
+          },
+          arguments: [node.left, node.right]
+        }
+        node.left = leftReplacementNode;
+      }
+
+      // Replace the binary operator with a call expression
+      // in other words a call to BaseNode.mult(), .div() etc.
+      node.type = 'CallExpression';
+      node.callee = {
+        type: "MemberExpression",
+        object: node.left,
+        property: {
+          type: "Identifier",
+          name: replaceBinaryOperator(node.operator),
+        },
+      };
+      node.arguments = [node.right];
+    },
+  }
+
+  // This unfinished function lets you do 1 * 10
+  // and turns it into float.mult(10)
+  fn.makeNode = function(leftValue, rightValue) {
+    if (typeof leftValue === 'number') {
+      return new FloatNode(leftValue);
+    }
+  }
+
+  // Javascript Node API.
+  // These classes are for expressing GLSL functions in Javascript without
+  // needing to  transpile the user's code.
+
+
+  class BaseNode {
+    constructor(isInternal, type) {
+      if (new.target === BaseNode) {
+        throw new TypeError("Cannot construct BaseNode instances directly. This is an abstract class.");
+      }
+      this.type = type;
+      this.componentNames = [];
+      this.swizzleChanged = false;
+      // For tracking recursion depth and creating temporary variables
+      this.isInternal = isInternal;
+      this.usedIn = [];
+      this.dependsOn = [];
+      this.srcLine = null;
+      // Stack Capture is used to get the original line of user code for Debug purposes
+      if (GLOBAL_SHADER.srcLocations === true && isInternal === false) {
+        try {
+          throw new Error("StackCapture");
+        } catch (e) {
+          const lines = e.stack.split("\n");
+          let userSketchLineIndex = 5;
+          if (isBinaryOperatorNode(this)) { userSketchLineIndex--; };
+          this.srcLine = lines[userSketchLineIndex].trim();
+        }
+      }
+    }
+    // get type() {
+    //   return this._type;
+    // }
+
+    // set type(value) {
+    //   this._type = value;
+    // }
+
+    addVectorComponents() {
+      if (this.type.startsWith('vec')) {
+        const vectorDimensions = +this.type.slice(3);
+        this.componentNames = ['x', 'y', 'z', 'w'].slice(0, vectorDimensions);
+
+        for (let componentName of this.componentNames) {
+          // let value = new FloatNode()
+          let value = new ComponentNode(this, componentName, 'float', true);
+          Object.defineProperty(this, componentName, {
+            get() {
+              return value;
+            },
+            set(newValue) {
+              this.swizzleChanged = true;
+              value = newValue;
+            }
+          })
+        }
+      }
+    }
+
+    // The base node implements a version of toGLSL which determines whether the generated code should be stored in a temporary variable.
+    toGLSLBase(context){
+      if (this.shouldUseTemporaryVariable()) {
+        return this.getTemporaryVariable(context);
+      }
+      else {
+        return this.toGLSL(context);
+      }
+    }
+
+    shouldUseTemporaryVariable() {
+      if (this.swizzleChanged) { return true; }
+      if (this.isInternal || isVariableNode(this)) { return false; }
+      let score = 0;
+      score += isBinaryOperatorNode(this);
+      score += isVectorNode(this) * 2;
+      score += this.usedIn.length;
+      return score > 3;
+    }
+    
+    getTemporaryVariable(context) {
+      if (!this.temporaryVariable) {
+        this.temporaryVariable = `temp_${context.getNextID()}`;
+        let line = "";
+        if (this.srcLine) {
+          line += `\n// From ${this.srcLine}\n`;
+        }
+        if (this.swizzleChanged) {
+          const valueArgs = [];
+          for (let componentName of this.componentNames) {
+            valueArgs.push(this[componentName])
+          }
+          const replacement = nodeConstructors[this.type](valueArgs)
+          line += this.type + " " + this.temporaryVariable + " = " + this.toGLSL(context) + ";";
+          line += `\n` + this.temporaryVariable + " = " + replacement.toGLSL(context) + ";";
+        } else {
+          line += this.type + " " + this.temporaryVariable + " = " + this.toGLSL(context) + ";";
+        }
+        context.declarations.push(line);
+      }
+      return this.temporaryVariable;
+    };
+    
+    // Binary Operators
+    add(other)  { return new BinaryOperatorNode(this, this.enforceType(other), '+'); }
+    sub(other)  { return new BinaryOperatorNode(this, this.enforceType(other), '-'); }
+    mult(other) { return new BinaryOperatorNode(this, this.enforceType(other), '*'); }
+    div(other)  { return new BinaryOperatorNode(this, this.enforceType(other), '/'); }
+    mod(other)  { return new ModulusNode(this, this.enforceType(other)); }
+
+    // Check that the types of the operands are compatible.
+    enforceType(other){
+      if (isShaderNode(other)){
+        if (!isGLSLNativeType(other.type)) {
+          throw new TypeError (`You've tried to perform an operation on a struct of type: ${other.type}. Try accessing a member on that struct with '.'`)
+        }
+        if (!isGLSLNativeType(other.type)) {
+          throw new TypeError (`You've tried to perform an operation on a struct of type: ${other.type}. Try accessing a member on that struct with '.'`)
+        }
+        if ((isFloatNode(this) || isVectorNode(this)) && isIntNode(other)) {
+          return new FloatNode(other)
+        }
+        return other;
+      }
+      else if(typeof other === 'number') {
+        if (isIntNode(this)) {
+          return new IntNode(other);
+        }
+        return new FloatNode(other);
+      }
+      else if(Array.isArray(other)) {
+        return new VectorNode(other, `vec${other.length}`)
+      }
+      else {
+        return new this.constructor(other);
+      }
+    }
+    
+    toGLSL(context){
+      throw new TypeError("Not supposed to call this function on BaseNode, which is an abstract class.");
+    }
+  }
+
+  // Primitive Types
+  class IntNode extends BaseNode {
+    constructor(x = 0, isInternal = false) {
+      super(isInternal, 'int');
+      this.x = x;
+    }
+
+    toGLSL(context) {
+      if (isShaderNode(this.x)) {
+        let code = this.x.toGLSLBase(context);
+        return isIntNode(this.x.type) ? code : `int(${code})`;
+      }
+      else if (typeof this.x === "number") {
+        return `${Math.floor(this.x)}`;
+      }
+      else {
+        return `int(${this.x})`;
+      }
+    }
+  }
+  
+  class FloatNode extends BaseNode {
+    constructor(x = 0, isInternal = false){
+      super(isInternal, 'float');
+      this.x = x;
+    }
+
+    toGLSL(context) {
+      if (isShaderNode(this.x)) {
+        let code = this.x.toGLSLBase(context);
+        return isFloatNode(this.x) ? code : `float(${code})`;
+      }
+      else if (typeof this.x === "number") {
+        return `${this.x.toFixed(4)}`;
+      }
+      else {
+        return `float(${this.x})`;
+      }
+    }
+  }
+  
+  class VectorNode extends BaseNode {
+    constructor(values, type, isInternal = false) {
+      super(isInternal, type);
+      this.componentNames = ['x', 'y', 'z', 'w'].slice(0, values.length);
+      this.componentNames.forEach((component, i) => {
+        this[component] = new FloatNode(values[i], true);
+      });
+    }
+    
+    toGLSL(context) {
+      let glslArgs = ``;
+
+      this.componentNames.forEach((component, i) => {
+        const comma = i === this.componentNames.length - 1 ? `` : `, `;
+        glslArgs += `${this[component].toGLSLBase(context)}${comma}`;
+      })
+
+      return `${this.type}(${glslArgs})`;
+    }
+  }
+
+  // Function Call Nodes
+  class FunctionCallNode extends BaseNode {
+    constructor(name, args, type, isInternal = false) {
+      super(isInternal, type);
+      this.name = name;
+      this.args = args;
+      // TODO:
+      this.argumentTypes = args;
+    }
+
+    deconstructArgs(context) {
+      if (Array.isArray(this.args)) {
+        return this.args.map((argNode) => argNode.toGLSLBase(context)).join(', ');
+      } else {
+        return `${this.args.toGLSLBase(context)}`;
+      }
+    }
+
+    toGLSL(context) {
+      return `${this.name}(${this.deconstructArgs(context)})`;
+    }
+  }
+  
+  // Variables and member variable nodes
+  class VariableNode extends BaseNode {
+    constructor(name, type, isInternal = false) {
+      super(isInternal, type);
+      this.name = name;
+      this.addVectorComponents();
+    }
+
+
+    toGLSL(context) {
+      return `${this.name}`;
+    }
+  }
+  
+  class ComponentNode extends BaseNode {
+    constructor(parent, componentName, type, isInternal = false) {
+      super(isInternal, type);
+      this.parent = parent;
+      this.componentName = componentName;
+      this.type = type;
+    }
+    toGLSL(context) {
+      const parentName = this.parent.toGLSLBase(context);
+      // const parentName = this.parent.temporaryVariable ? this.parent.temporaryVariable : this.parent.name; 
+      return `${parentName}.${this.componentName}`;
+    }
+  }
+
+  // Binary Operator Nodes
+  class BinaryOperatorNode extends BaseNode {
+    constructor(a, b, operator, isInternal = false) {
+      super(isInternal, null);
+      this.op = operator;
+      this.a = a;
+      this.b = b;
+      for (const operand of [a, b]) {
+        operand.usedIn.push(this);
+      }
+      this.type = this.determineType();
+      this.addVectorComponents();
+    }
+    
+    // We know that both this.a and this.b are nodes because of BaseNode.enforceType
+    determineType() {
+      if (this.a.type === this.b.type) {
+        return this.a.type;
+      } 
+      else if (isVectorNode(this.a) && isFloatNode(this.b)) {
+        return this.a.type;
+      } 
+      else if (isVectorNode(this.b) && isFloatNode(this.a)) {
+        return this.b.type;
+      } 
+      else if (isFloatNode(this.a) && isIntNode(this.b) 
+        || isIntNode(this.a) && isFloatNode(this.b)
+      ) {
+        return 'float';
+      }
+      else {
+        throw new Error("Incompatible types for binary operator");
+      }
+    }
+
+    processOperand(operand, context) {
+      if (operand.temporaryVariable) { return operand.temporaryVariable; }
+      let code = operand.toGLSLBase(context);      
+      if (isBinaryOperatorNode(operand) && !operand.temporaryVariable) {
+        code = `(${code})`;
+      }
+      if (this.type === 'float' && isIntNode(operand)) {
+        code = `float(${code})`;
+      }
+      return code;
+    }
+
+    toGLSL(context) {
+      const a = this.processOperand(this.a, context);
+      const b = this.processOperand(this.b, context);
+      return `${a} ${this.op} ${b}`;
+    }
+  }
+
+  // TODO: Correct the implementation for floats/ genType etc
+  class ModulusNode extends BinaryOperatorNode {
+    constructor(a, b) {
+      super(a, b);
+    }
+    toGLSL(context) {
+      // Switch on type between % or mod()
+      if (isVectorNode(this) || isFloatNode(this)) {
+        return `mod(${this.a.toGLSLBase(context)}, ${this.b.toGLSLBase(context)})`;
+      }
+      return `${this.processOperand(context, this.a)} % ${this.processOperand(context, this.b)}`;
+    }
+  }
+
+  // TODO: finish If Node
+  class ConditionalNode extends BaseNode {
+    constructor(value) {
+      super(value);
+      this.value = value;
+      this.condition = null;
+      this.thenBranch = null;
+      this.elseBranch = null;
+    }
+    // conditions
+    equalTo(value){}
+    greaterThan(value) {}
+    greaterThanEqualTo(value) {}
+    lessThan(value) {}
+    lessThanEqualTo(value) {}
+    // modifiers
+    not() {}
+    or() {}
+    and() {}
+    // returns
+    thenReturn(value) {}
+    elseReturn(value) {}
+    // 
+    thenDiscard() {
+      new ConditionalDiscard(this.condition);
+    };
+  };
+
+  class ConditionalDiscard extends BaseNode {
+    constructor(condition){
+      this.condition = condition;
+    }
+    toGLSL(context) {
+      context.discardConditions.push(`if(${this.condition}{discard;})`);
+    }
+  }
+
+  fn.if = function (value) {
+    return new ConditionalNode(value);
+  }
+
+  // Node Helper functions
+  function isShaderNode(node) {  
+    return (node instanceof BaseNode); 
+  }
+
+  function isIntNode(node) { 
+    return (isShaderNode(node) && (node.type === 'int')); 
+  }
+
+  function isFloatNode(node) { 
+    return (isShaderNode(node) && (node.type === 'float')); 
+  }
+
+  function isVectorNode(node) {
+    return (isShaderNode(node) && (node.type === 'vec2'|| node.type === 'vec3' || node.type === 'vec4')); 
+  }
+
+  function isBinaryOperatorNode(node) {
+    return (node instanceof BinaryOperatorNode);
+  }
+
+  function isVariableNode(node) { 
+    return (node instanceof VariableNode || node instanceof ComponentNode || typeof(node.temporaryVariable) != 'undefined'); 
+  }
+
+    // Helper function to check if a type is a user defined struct or native type
+    function isGLSLNativeType(typeName) {
+      // Supported types for now
+      const glslNativeTypes = ['int', 'float', 'vec2', 'vec3', 'vec4', 'sampler2D'];
+      return glslNativeTypes.includes(typeName);
+    }
+  }
+
+    // Helper function to check if a type is a user defined struct or native type
+    function isGLSLNativeType(typeName) {
+      // Supported types for now
+      const glslNativeTypes = ['int', 'float', 'vec2', 'vec3', 'vec4', 'sampler2D'];
+      return glslNativeTypes.includes(typeName);
+    }
+
+  // Shader Generator
+  // This class is responsible for converting the nodes into an object containing GLSL code, to be used by p5.Shader.modify
+
+  class ShaderGenerator {
+    constructor(userCallback, originalShader, srcLocations) {
+      GLOBAL_SHADER = this;
+      this.userCallback = userCallback;
+      this.userCallback = userCallback;
+      this.srcLocations = srcLocations;
+      this.generateHookOverrides(originalShader);
+      this.output = {
+        uniforms: {},
+      }
+      this.resetGLSLContext();
+    }
+
+    generate() {
+      this.userCallback();
+      return this.output;
+    }
+
+    // This method generates the hook overrides which the user calls in their modify function.
+    generateHookOverrides(originalShader) {
+      const availableHooks = {
+        ...originalShader.hooks.vertex,
+        ...originalShader.hooks.fragment,
+      }
+
+      Object.keys(availableHooks).forEach((hookName) => {
+        const hookTypes = originalShader.hookTypes(hookName);
+        this[hookTypes.name] = function(userCallback) {
+          // Create the initial nodes which are passed to the user callback
+          // Also generate a string of the arguments for the code generation
+          const argNodes = []
+          const argsArray = [];
+
+          hookTypes.parameters.forEach((parameter) => {
+            // For hooks with structs as input we should pass an object populated with variable nodes 
+            if (!isGLSLNativeType(parameter.type.typeName)) {
+              const structArg = {};
+              parameter.type.properties.forEach((property) => {
+                structArg[property.name] = new VariableNode(`${parameter.name}.${property.name}`, property.type.typeName, true);
+              });
+              argNodes.push(structArg);
+            } else {
+              argNodes.push(
+                new VariableNode(parameter.name, parameter.type.typeName, true)
+              );
+            }
+            const qualifiers = parameter.type.qualifiers.length > 0 ? parameter.type.qualifiers.join(' ') : '';
+            argsArray.push(`${qualifiers} ${parameter.type.typeName} ${parameter.name}`.trim())
+          })
+          
+          let returnedValue = userCallback(...argNodes);
+          const expectedReturnType = hookTypes.returnType;
+          const toGLSLResults = {};
+
+          // If the expected return type is a struct we need to evaluate each of its properties
+          if (!isGLSLNativeType(expectedReturnType.typeName)) {
+            Object.entries(returnedValue).forEach(([propertyName, propertyNode]) => {
+              toGLSLResults[propertyName] = propertyNode.toGLSLBase(this.context);
+            })
+          } else {
+            // We can accept raw numbers or arrays otherwise
+            if (!isShaderNode(returnedValue)) {
+              returnedValue = nodeConstructors[expectedReturnType.typeName](returnedValue)
+            }
+            toGLSLResults['notAProperty'] = returnedValue.toGLSLBase(this.context);
+          }
+          
+          // Build the final GLSL string.
+          // The order of this code is a bit confusing, we need to call toGLSLBase
+          let codeLines = [ 
+            `(${argsArray.join(', ')}) {`,
+            ...this.context.declarations,
+            `${hookTypes.returnType.typeName} finalReturnValue;`
+          ];
+
+          Object.entries(toGLSLResults).forEach(([propertyName, result]) => {
+            const propString = expectedReturnType.properties ? `.${propertyName}` : '';
+            codeLines.push(`finalReturnValue${propString} = ${result};`)
+          })
+
+          codeLines.push('return finalReturnValue;', '}');
+          this.output[hookName] = codeLines.join('\n');
+          this.resetGLSLContext();
+        }
+
+        // Expose the Functions to global scope for users to use
+        window[hookTypes.name] = function(userOverride) {
+          GLOBAL_SHADER[hookTypes.name](userOverride); 
+        };
+      })
+    }
+
+    resetGLSLContext() { 
+      this.context = {
+        id: 0,
+        getNextID: function() { return this.id++ },
+        declarations: [],
+      }
+    }
+  }
+
+  // User function helpers
+  function conformVectorParameters(value, vectorDimensions) {
+    // Allow arguments as arrays ([0,0,0,0]) or not (0,0,0,0)
+    value = value.flat();
+    // Populate arguments so uniformVector3(0) becomes [0,0,0] 
+    if (value.length === 1) {
+      value = Array(vectorDimensions).fill(value[0]);
+    }
+    return value;
+  }
+
+  // User functions
+  fn.instanceID = function() {
+    return new VariableNode('gl_InstanceID', 'int');
+  }
+  
+  fn.uvCoords = function() {
+    return new VariableNode('vTexCoord', 'vec2');
+  }
+
+  fn.discard = function() {
+    return new VariableNode('discard', 'keyword');
+  }
+  
+  // Generating uniformFloat, uniformVec, createFloat, etc functions
+  // Maps a GLSL type to the name suffix for method names
+  const GLSLTypesToIdentifiers = {
+    int:    'Int',
+    float:  'Float',
+    vec2:   'Vector2',
+    vec3:   'Vector3',
+    vec4:   'Vector4',
+    sampler2D: 'Texture',
+  };
+
+  const nodeConstructors = {
+    int:   (value) => new IntNode(value),
+    float: (value) => new FloatNode(value),
+    vec2:  (value) => new VectorNode(value, 'vec2'),
+    vec3:  (value) => new VectorNode(value, 'vec3'),
+    vec4:  (value) => new VectorNode(value, 'vec4'),
+  };
+
+  for (const glslType in GLSLTypesToIdentifiers) {
+  for (const glslType in GLSLTypesToIdentifiers) {
+    // Generate uniform*() Methods for creating uniforms
+    const typeIdentifier = GLSLTypesToIdentifiers[glslType];
+    const uniformMethodName = `uniform${typeIdentifier}`;
+
+    ShaderGenerator.prototype[uniformMethodName] = function(...args) {
+      let [name, ...defaultValue] = args;
+
+      if(glslType.startsWith('vec')) {
+        defaultValue = conformVectorParameters(defaultValue, +glslType.slice(3));
+        this.output.uniforms[`${glslType} ${name}`] = defaultValue;
+      } 
+      else {
+        this.output.uniforms[`${glslType} ${name}`] = defaultValue[0];
+      }
+
+      let safeType = glslType === 'sampler2D' ? 'vec4' : glslType;
+      const uniform = new VariableNode(name, safeType, false);
+      return uniform;
+    };
+
+    fn[uniformMethodName] = function (...args) { 
+      return GLOBAL_SHADER[uniformMethodName](...args); 
+    };
+
+    // We don't need a createTexture method.
+    if (glslType === 'sampler2D') { continue; }
+    
+    // Generate the create*() Methods for creating variables in shaders
+    const createMethodName = `create${typeIdentifier}`;
+    fn[createMethodName] = function (...value) {
+      if(glslType.startsWith('vec')) {
+        value = conformVectorParameters(value, +glslType.slice(3));
+      } else {
+        value = value[0];
+      }
+      return nodeConstructors[glslType](value);
+    }
+  }
+  
+  // GLSL Built in functions
+  // Add a whole lot of these functions. 
+  // https://docs.gl/el3/abs
+  // TODO: 
+  //  In reality many of these have multiple overrides which will need to address later.
+  // Also, their return types depend on the genType which will need to address urgently
+  //      genType clamp(genType x,
+  //                    genType minVal,
+  //                    genType maxVal);
+  //      genType clamp(genType x,
+  //                    float minVal,
+  //                    float maxVal);
+
+
+
+  const builtInFunctions = {
+    //////////// Trigonometry //////////
+    'acos': { args: ['genType'], returnType: 'float'},
+    // 'acosh': {},
+    'asin': { args: ['genType'], returnType: 'float'},
+    // 'asinh': {},
+    'atan': { args: ['genType'], returnType: 'float'},
+    // 'atanh': {},
+    'cos': { args: ['genType'], returnType: 'float', isp5Function: true},
+    // 'cosh': {},
+    'degrees': { args: ['genType'], returnType: 'float'},
+    'radians': { args: ['genType'], returnType: 'float'},
+    'sin': { args: ['genType'], returnType: 'float' , isp5Function: true},
+    // 'sinh': {},
+    'tan': { args: ['genType'], returnType: 'float', isp5Function: true},
+    // 'tanh': {},
+
+    ////////// Mathematics //////////
+    'abs': { args: ['genType'], returnType: 'float'},
+    'ceil': { args: ['genType'], returnType: 'float'},
+    'clamp': { args: ['genType', 'genType', 'genType'], returnType: 'float'},
+    // 'dFdx': {},
+    // 'dFdy': {},
+    'exp': { args: ['genType'], returnType: 'float'},
+    'exp2': { args: ['genType'], returnType: 'float'},
+    'floor': { args: ['genType'], returnType: 'float'},
+    // 'fma': {},
+    'fract': { args: ['genType'], returnType: 'float'},
+    // 'fwidth': {},
+    // 'inversesqrt': {},
+    // 'isinf': {},
+    // 'isnan': {},
+    // 'log': {},
+    // 'log2': {},
+    'max': { args: ['genType'], returnType: 'genType'},
+    'min': { args: ['genType'], returnType: 'genType'},
+    'mix': { args: ['genType'], returnType: 'genType'},
+    // 'mod': {},
+    // 'modf': {},
+    'pow': { args: ['genType'], returnType: 'float'},
+    // 'round': {},
+    // 'roundEven': {},
+    // 'sign': {},
+    'smoothstep': { args: ['genType', 'genType', 'genType'], returnType: 'float'},
+    'sqrt': { args: ['genType'], returnType: 'genType'},
+    'step': { args: ['genType', 'genType'], returnType: 'genType'},
+    // 'trunc': {},
+
+    ////////// Vector //////////
+    'cross': {},
+    'distance': {},
+    'dot': {},
+    // 'equal': {},
+    // 'faceforward': {},
+    'length': {},
+    'normalize': {},
+    // 'notEqual': {},
+    // 'reflect': {},
+    // 'refract': {},
+    // Texture sampling
+    'texture': {},
+  }
+
+  // Object.entries(builtInFunctions).forEach(([functionName, properties]) => {
+  //   fn[functionName] = function () {
+  //     new FunctionCallNode(functionName, args, type, isInternal = false)
+  //   }
+  // })
+
+  const oldTexture = p5.prototype.texture;
+  p5.prototype.texture = function(...args) {
+    if (isShaderNode(args[0])) {
+      return new FunctionCallNode('texture', args, 'vec4');
+    } else {
+      return oldTexture.apply(this, args);
+    }
+  }
+}
+
+export default shadergen;
+
+if (typeof p5 !== 'undefined') {
+  p5.registerAddon(shadergen)
+}
\ No newline at end of file

From 435a7187d2928ea8385e840e8e4b9081d7ef6226 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Thu, 13 Mar 2025 16:29:37 +0000
Subject: [PATCH 44/54] add builtin GLSL functions & change file name

---
 src/webgl/ShaderGen.js       | 835 -----------------------------------
 src/webgl/ShaderGenerator.js | 155 +++----
 src/webgl/index.js           |   4 +-
 3 files changed, 82 insertions(+), 912 deletions(-)
 delete mode 100644 src/webgl/ShaderGen.js

diff --git a/src/webgl/ShaderGen.js b/src/webgl/ShaderGen.js
deleted file mode 100644
index d06b7dbd65..0000000000
--- a/src/webgl/ShaderGen.js
+++ /dev/null
@@ -1,835 +0,0 @@
-/**
-* @module 3D
-* @submodule ShaderGenerator
-* @for p5
-* @requires core
-*/
-
-import { parse } from 'acorn';
-import { ancestor } from 'acorn-walk';
-import escodegen from 'escodegen';
-
-function shadergen(p5, fn) {
-  let GLOBAL_SHADER;
-  
-  const oldModify = p5.Shader.prototype.modify
-
-  p5.Shader.prototype.modify = function(shaderModifier, options = { parser: true, srcLocations: false }) {
-    if (shaderModifier instanceof Function) {
-      let generatorFunction;
-      if (options.parser) {
-        const sourceString = shaderModifier.toString()
-        const ast = parse(sourceString, { 
-          ecmaVersion: 2021, 
-          locations: options.srcLocations 
-        });
-        ancestor(ast, ASTCallbacks);
-        const transpiledSource = escodegen.generate(ast);
-        generatorFunction = new Function(
-          transpiledSource.slice(
-            transpiledSource.indexOf("{") + 1,
-            transpiledSource.lastIndexOf("}")
-          )
-        );
-      } else {
-        generatorFunction = shaderModifier;
-      }
-      const generator = new ShaderGenerator(generatorFunction, this, options.srcLocations)
-      const generatedModifyArgument = generator.generate();
-      console.log("SRC STRING: ", generatorFunction);
-      console.log("NEW OPTIONS:", generatedModifyArgument)
-      return oldModify.call(this, generatedModifyArgument);
-    } 
-    else {
-      return oldModify.call(this, shaderModifier)
-    }
-  }
-
-  // AST Transpiler Callbacks and their helpers
-  function replaceBinaryOperator(codeSource) {
-    switch (codeSource) {
-      case '+': return 'add';
-      case '-': return 'sub';
-      case '*': return 'mult';
-      case '/': return 'div';
-      case '%': return 'mod';
-    }
-  }
-
-  const ASTCallbacks = {
-    VariableDeclarator(node, _state, _ancestors) {
-      if (node.init.callee && node.init.callee.name.startsWith('uniform')) {
-        const uniformNameLiteral = {
-          type: 'Literal',
-          value: node.id.name
-        }
-        node.init.arguments.unshift(uniformNameLiteral);
-      }
-    },
-    // The callbacks for AssignmentExpression and BinaryExpression handle
-    // operator overloading including +=, *= assignment expressions 
-    AssignmentExpression(node, _state, _ancestors) {
-      if (node.operator !== '=') {
-        const methodName = replaceBinaryOperator(node.operator.replace('=',''));
-        const rightReplacementNode = {
-          type: 'CallExpression',
-          callee: {
-            type: "MemberExpression",
-            object: node.left,
-            property: {
-              type: "Identifier",
-              name: methodName,
-            },
-            computed: false,
-          },
-          arguments: [node.right]
-        }
-          node.operator = '=';
-          node.right = rightReplacementNode;
-        }
-      },
-    BinaryExpression(node, _state, ancestors) {
-      // Don't convert uniform default values to node methods, as 
-      // they should be evaluated at runtime, not compiled.
-      const isUniform = (ancestor) => {
-        return ancestor.type === 'CallExpression'
-          && ancestor.callee?.type === 'Identifier'
-          && ancestor.callee?.name.startsWith('uniform');
-      }
-      if (ancestors.some(isUniform)) {
-        return;
-      }
-      // If the left hand side of an expression is one of these types,
-      // we should construct a node from it.
-      const unsafeTypes = ["Literal", "ArrayExpression"]
-      if (unsafeTypes.includes(node.left.type)) {
-        const leftReplacementNode = {
-          type: "CallExpression",
-          callee: {
-            type: "Identifier",
-            name: "makeNode",
-          },
-          arguments: [node.left, node.right]
-        }
-        node.left = leftReplacementNode;
-      }
-
-      // Replace the binary operator with a call expression
-      // in other words a call to BaseNode.mult(), .div() etc.
-      node.type = 'CallExpression';
-      node.callee = {
-        type: "MemberExpression",
-        object: node.left,
-        property: {
-          type: "Identifier",
-          name: replaceBinaryOperator(node.operator),
-        },
-      };
-      node.arguments = [node.right];
-    },
-  }
-
-  // This unfinished function lets you do 1 * 10
-  // and turns it into float.mult(10)
-  fn.makeNode = function(leftValue, rightValue) {
-    if (typeof leftValue === 'number') {
-      return new FloatNode(leftValue);
-    }
-  }
-
-  // Javascript Node API.
-  // These classes are for expressing GLSL functions in Javascript without
-  // needing to  transpile the user's code.
-
-
-  class BaseNode {
-    constructor(isInternal, type) {
-      if (new.target === BaseNode) {
-        throw new TypeError("Cannot construct BaseNode instances directly. This is an abstract class.");
-      }
-      this.type = type;
-      this.componentNames = [];
-      this.swizzleChanged = false;
-      // For tracking recursion depth and creating temporary variables
-      this.isInternal = isInternal;
-      this.usedIn = [];
-      this.dependsOn = [];
-      this.srcLine = null;
-      // Stack Capture is used to get the original line of user code for Debug purposes
-      if (GLOBAL_SHADER.srcLocations === true && isInternal === false) {
-        try {
-          throw new Error("StackCapture");
-        } catch (e) {
-          const lines = e.stack.split("\n");
-          let userSketchLineIndex = 5;
-          if (isBinaryOperatorNode(this)) { userSketchLineIndex--; };
-          this.srcLine = lines[userSketchLineIndex].trim();
-        }
-      }
-    }
-    // get type() {
-    //   return this._type;
-    // }
-
-    // set type(value) {
-    //   this._type = value;
-    // }
-
-    addVectorComponents() {
-      if (this.type.startsWith('vec')) {
-        const vectorDimensions = +this.type.slice(3);
-        this.componentNames = ['x', 'y', 'z', 'w'].slice(0, vectorDimensions);
-
-        for (let componentName of this.componentNames) {
-          // let value = new FloatNode()
-          let value = new ComponentNode(this, componentName, 'float', true);
-          Object.defineProperty(this, componentName, {
-            get() {
-              return value;
-            },
-            set(newValue) {
-              this.swizzleChanged = true;
-              value = newValue;
-            }
-          })
-        }
-      }
-    }
-
-    // The base node implements a version of toGLSL which determines whether the generated code should be stored in a temporary variable.
-    toGLSLBase(context){
-      if (this.shouldUseTemporaryVariable()) {
-        return this.getTemporaryVariable(context);
-      }
-      else {
-        return this.toGLSL(context);
-      }
-    }
-
-    shouldUseTemporaryVariable() {
-      if (this.swizzleChanged) { return true; }
-      if (this.isInternal || isVariableNode(this)) { return false; }
-      let score = 0;
-      score += isBinaryOperatorNode(this);
-      score += isVectorNode(this) * 2;
-      score += this.usedIn.length;
-      return score > 3;
-    }
-    
-    getTemporaryVariable(context) {
-      if (!this.temporaryVariable) {
-        this.temporaryVariable = `temp_${context.getNextID()}`;
-        let line = "";
-        if (this.srcLine) {
-          line += `\n// From ${this.srcLine}\n`;
-        }
-        if (this.swizzleChanged) {
-          const valueArgs = [];
-          for (let componentName of this.componentNames) {
-            valueArgs.push(this[componentName])
-          }
-          const replacement = nodeConstructors[this.type](valueArgs)
-          line += this.type + " " + this.temporaryVariable + " = " + this.toGLSL(context) + ";";
-          line += `\n` + this.temporaryVariable + " = " + replacement.toGLSL(context) + ";";
-        } else {
-          line += this.type + " " + this.temporaryVariable + " = " + this.toGLSL(context) + ";";
-        }
-        context.declarations.push(line);
-      }
-      return this.temporaryVariable;
-    };
-    
-    // Binary Operators
-    add(other)  { return new BinaryOperatorNode(this, this.enforceType(other), '+'); }
-    sub(other)  { return new BinaryOperatorNode(this, this.enforceType(other), '-'); }
-    mult(other) { return new BinaryOperatorNode(this, this.enforceType(other), '*'); }
-    div(other)  { return new BinaryOperatorNode(this, this.enforceType(other), '/'); }
-    mod(other)  { return new ModulusNode(this, this.enforceType(other)); }
-
-    // Check that the types of the operands are compatible.
-    enforceType(other){
-      if (isShaderNode(other)){
-        if (!isGLSLNativeType(other.type)) {
-          throw new TypeError (`You've tried to perform an operation on a struct of type: ${other.type}. Try accessing a member on that struct with '.'`)
-        }
-        if (!isGLSLNativeType(other.type)) {
-          throw new TypeError (`You've tried to perform an operation on a struct of type: ${other.type}. Try accessing a member on that struct with '.'`)
-        }
-        if ((isFloatNode(this) || isVectorNode(this)) && isIntNode(other)) {
-          return new FloatNode(other)
-        }
-        return other;
-      }
-      else if(typeof other === 'number') {
-        if (isIntNode(this)) {
-          return new IntNode(other);
-        }
-        return new FloatNode(other);
-      }
-      else if(Array.isArray(other)) {
-        return new VectorNode(other, `vec${other.length}`)
-      }
-      else {
-        return new this.constructor(other);
-      }
-    }
-    
-    toGLSL(context){
-      throw new TypeError("Not supposed to call this function on BaseNode, which is an abstract class.");
-    }
-  }
-
-  // Primitive Types
-  class IntNode extends BaseNode {
-    constructor(x = 0, isInternal = false) {
-      super(isInternal, 'int');
-      this.x = x;
-    }
-
-    toGLSL(context) {
-      if (isShaderNode(this.x)) {
-        let code = this.x.toGLSLBase(context);
-        return isIntNode(this.x.type) ? code : `int(${code})`;
-      }
-      else if (typeof this.x === "number") {
-        return `${Math.floor(this.x)}`;
-      }
-      else {
-        return `int(${this.x})`;
-      }
-    }
-  }
-  
-  class FloatNode extends BaseNode {
-    constructor(x = 0, isInternal = false){
-      super(isInternal, 'float');
-      this.x = x;
-    }
-
-    toGLSL(context) {
-      if (isShaderNode(this.x)) {
-        let code = this.x.toGLSLBase(context);
-        return isFloatNode(this.x) ? code : `float(${code})`;
-      }
-      else if (typeof this.x === "number") {
-        return `${this.x.toFixed(4)}`;
-      }
-      else {
-        return `float(${this.x})`;
-      }
-    }
-  }
-  
-  class VectorNode extends BaseNode {
-    constructor(values, type, isInternal = false) {
-      super(isInternal, type);
-      this.componentNames = ['x', 'y', 'z', 'w'].slice(0, values.length);
-      this.componentNames.forEach((component, i) => {
-        this[component] = new FloatNode(values[i], true);
-      });
-    }
-    
-    toGLSL(context) {
-      let glslArgs = ``;
-
-      this.componentNames.forEach((component, i) => {
-        const comma = i === this.componentNames.length - 1 ? `` : `, `;
-        glslArgs += `${this[component].toGLSLBase(context)}${comma}`;
-      })
-
-      return `${this.type}(${glslArgs})`;
-    }
-  }
-
-  // Function Call Nodes
-  class FunctionCallNode extends BaseNode {
-    constructor(name, args, type, isInternal = false) {
-      super(isInternal, type);
-      this.name = name;
-      this.args = args;
-      // TODO:
-      this.argumentTypes = {
-
-      };
-    }
-
-    deconstructArgs(context) {
-      if (Array.isArray(this.args)) {
-        return this.args.map((argNode) => argNode.toGLSLBase(context)).join(', ');
-      } else {
-        return `${this.args.toGLSLBase(context)}`;
-      }
-    }
-
-    toGLSL(context) {
-      return `${this.name}(${this.deconstructArgs(context)})`;
-    }
-  }
-  
-  // Variables and member variable nodes
-  class VariableNode extends BaseNode {
-    constructor(name, type, isInternal = false) {
-      super(isInternal, type);
-      this.name = name;
-      this.addVectorComponents();
-    }
-
-
-    toGLSL(context) {
-      return `${this.name}`;
-    }
-  }
-  
-  class ComponentNode extends BaseNode {
-    constructor(parent, componentName, type, isInternal = false) {
-      super(isInternal, type);
-      this.parent = parent;
-      this.componentName = componentName;
-      this.type = type;
-    }
-    toGLSL(context) {
-      const parentName = this.parent.toGLSLBase(context);
-      // const parentName = this.parent.temporaryVariable ? this.parent.temporaryVariable : this.parent.name; 
-      return `${parentName}.${this.componentName}`;
-    }
-  }
-
-  // Binary Operator Nodes
-  class BinaryOperatorNode extends BaseNode {
-    constructor(a, b, operator, isInternal = false) {
-      super(isInternal, null);
-      this.op = operator;
-      this.a = a;
-      this.b = b;
-      for (const operand of [a, b]) {
-        operand.usedIn.push(this);
-      }
-      this.type = this.determineType();
-      this.addVectorComponents();
-    }
-    
-    // We know that both this.a and this.b are nodes because of BaseNode.enforceType
-    determineType() {
-      if (this.a.type === this.b.type) {
-        return this.a.type;
-      } 
-      else if (isVectorNode(this.a) && isFloatNode(this.b)) {
-        return this.a.type;
-      } 
-      else if (isVectorNode(this.b) && isFloatNode(this.a)) {
-        return this.b.type;
-      } 
-      else if (isFloatNode(this.a) && isIntNode(this.b) 
-        || isIntNode(this.a) && isFloatNode(this.b)
-      ) {
-        return 'float';
-      }
-      else {
-        throw new Error("Incompatible types for binary operator");
-      }
-    }
-
-    processOperand(operand, context) {
-      if (operand.temporaryVariable) { return operand.temporaryVariable; }
-      let code = operand.toGLSLBase(context);      
-      if (isBinaryOperatorNode(operand) && !operand.temporaryVariable) {
-        code = `(${code})`;
-      }
-      if (this.type === 'float' && isIntNode(operand)) {
-        code = `float(${code})`;
-      }
-      return code;
-    }
-
-    toGLSL(context) {
-      const a = this.processOperand(this.a, context);
-      const b = this.processOperand(this.b, context);
-      return `${a} ${this.op} ${b}`;
-    }
-  }
-
-  // TODO: Correct the implementation for floats/ genType etc
-  class ModulusNode extends BinaryOperatorNode {
-    constructor(a, b) {
-      super(a, b);
-    }
-    toGLSL(context) {
-      // Switch on type between % or mod()
-      if (isVectorNode(this) || isFloatNode(this)) {
-        return `mod(${this.a.toGLSLBase(context)}, ${this.b.toGLSLBase(context)})`;
-      }
-      return `${this.processOperand(context, this.a)} % ${this.processOperand(context, this.b)}`;
-    }
-  }
-
-  // TODO: finish If Node
-  class ConditionalNode extends BaseNode {
-    constructor(value) {
-      super(value);
-      this.value = value;
-      this.condition = null;
-      this.thenBranch = null;
-      this.elseBranch = null;
-    }
-    // conditions
-    equalTo(value){}
-    greaterThan(value) {}
-    greaterThanEqualTo(value) {}
-    lessThan(value) {}
-    lessThanEqualTo(value) {}
-    // modifiers
-    not() {}
-    or() {}
-    and() {}
-    // returns
-    thenReturn(value) {}
-    elseReturn(value) {}
-    // 
-    thenDiscard() {
-      new ConditionalDiscard(this.condition);
-    };
-  };
-
-  class ConditionalDiscard extends BaseNode {
-    constructor(condition){
-      this.condition = condition;
-    }
-    toGLSL(context) {
-      context.discardConditions.push(`if(${this.condition}{discard;})`);
-    }
-  }
-
-  fn.if = function (value) {
-    return new ConditionalNode(value);
-  }
-
-  // Node Helper functions
-  function isShaderNode(node) {  
-    return (node instanceof BaseNode); 
-  }
-
-  function isIntNode(node) { 
-    return (isShaderNode(node) && (node.type === 'int')); 
-  }
-
-  function isFloatNode(node) { 
-    return (isShaderNode(node) && (node.type === 'float')); 
-  }
-
-  function isVectorNode(node) {
-    return (isShaderNode(node) && (node.type === 'vec2'|| node.type === 'vec3' || node.type === 'vec4')); 
-  }
-
-  function isBinaryOperatorNode(node) {
-    return (node instanceof BinaryOperatorNode);
-  }
-
-  function isVariableNode(node) { 
-    return (node instanceof VariableNode || node instanceof ComponentNode || typeof(node.temporaryVariable) != 'undefined'); 
-  }
-
-    // Helper function to check if a type is a user defined struct or native type
-    function isGLSLNativeType(typeName) {
-      // Supported types for now
-      const glslNativeTypes = ['int', 'float', 'vec2', 'vec3', 'vec4', 'sampler2D'];
-      return glslNativeTypes.includes(typeName);
-    }
-  }
-
-    // Helper function to check if a type is a user defined struct or native type
-    function isGLSLNativeType(typeName) {
-      // Supported types for now
-      const glslNativeTypes = ['int', 'float', 'vec2', 'vec3', 'vec4', 'sampler2D'];
-      return glslNativeTypes.includes(typeName);
-    }
-
-  // Shader Generator
-  // This class is responsible for converting the nodes into an object containing GLSL code, to be used by p5.Shader.modify
-
-  class ShaderGenerator {
-    constructor(userCallback, originalShader, srcLocations) {
-    constructor(userCallback, originalShader, srcLocations) {
-      GLOBAL_SHADER = this;
-      this.userCallback = userCallback;
-      this.userCallback = userCallback;
-      this.srcLocations = srcLocations;
-      this.generateHookOverrides(originalShader);
-      this.output = {
-        uniforms: {},
-      }
-      this.resetGLSLContext();
-    }
-
-    generate() {
-      this.userCallback();
-      return this.output;
-    }
-
-    // This method generates the hook overrides which the user calls in their modify function.
-    generateHookOverrides(originalShader) {
-      const availableHooks = {
-        ...originalShader.hooks.vertex,
-        ...originalShader.hooks.fragment,
-      }
-
-      Object.keys(availableHooks).forEach((hookName) => {
-        const hookTypes = originalShader.hookTypes(hookName);
-        this[hookTypes.name] = function(userCallback) {
-          // Create the initial nodes which are passed to the user callback
-          // Also generate a string of the arguments for the code generation
-          const argNodes = []
-          const argsArray = [];
-
-          hookTypes.parameters.forEach((parameter) => {
-            // For hooks with structs as input we should pass an object populated with variable nodes 
-            if (!isGLSLNativeType(parameter.type.typeName)) {
-              const structArg = {};
-              parameter.type.properties.forEach((property) => {
-                structArg[property.name] = new VariableNode(`${parameter.name}.${property.name}`, property.type.typeName, true);
-              });
-              argNodes.push(structArg);
-            } else {
-              argNodes.push(
-                new VariableNode(parameter.name, parameter.type.typeName, true)
-              );
-            }
-            const qualifiers = parameter.type.qualifiers.length > 0 ? parameter.type.qualifiers.join(' ') : '';
-            argsArray.push(`${qualifiers} ${parameter.type.typeName} ${parameter.name}`.trim())
-          })
-          
-          let returnedValue = userCallback(...argNodes);
-          const expectedReturnType = hookTypes.returnType;
-          const toGLSLResults = {};
-
-          // If the expected return type is a struct we need to evaluate each of its properties
-          if (!isGLSLNativeType(expectedReturnType.typeName)) {
-            Object.entries(returnedValue).forEach(([propertyName, propertyNode]) => {
-              toGLSLResults[propertyName] = propertyNode.toGLSLBase(this.context);
-            })
-          } else {
-            // We can accept raw numbers or arrays otherwise
-            if (!isShaderNode(returnedValue)) {
-              returnedValue = nodeConstructors[expectedReturnType.typeName](returnedValue)
-            }
-            toGLSLResults['notAProperty'] = returnedValue.toGLSLBase(this.context);
-          }
-          
-          // Build the final GLSL string.
-          // The order of this code is a bit confusing, we need to call toGLSLBase
-          let codeLines = [ 
-            `(${argsArray.join(', ')}) {`,
-            ...this.context.declarations,
-            `${hookTypes.returnType.typeName} finalReturnValue;`
-          ];
-
-          Object.entries(toGLSLResults).forEach(([propertyName, result]) => {
-            const propString = expectedReturnType.properties ? `.${propertyName}` : '';
-            codeLines.push(`finalReturnValue${propString} = ${result};`)
-          })
-
-          codeLines.push('return finalReturnValue;', '}');
-          this.output[hookName] = codeLines.join('\n');
-          this.resetGLSLContext();
-        }
-
-        // Expose the Functions to global scope for users to use
-        window[hookTypes.name] = function(userOverride) {
-          GLOBAL_SHADER[hookTypes.name](userOverride); 
-        };
-      })
-    }
-
-    resetGLSLContext() { 
-      this.context = {
-        id: 0,
-        getNextID: function() { return this.id++ },
-        declarations: [],
-      }
-    }
-  }
-
-  // User function helpers
-  function conformVectorParameters(value, vectorDimensions) {
-    // Allow arguments as arrays ([0,0,0,0]) or not (0,0,0,0)
-    value = value.flat();
-    // Populate arguments so uniformVector3(0) becomes [0,0,0] 
-    if (value.length === 1) {
-      value = Array(vectorDimensions).fill(value[0]);
-    }
-    return value;
-  }
-
-  // User functions
-  fn.instanceID = function() {
-    return new VariableNode('gl_InstanceID', 'int');
-  }
-  
-  fn.uvCoords = function() {
-    return new VariableNode('vTexCoord', 'vec2');
-  }
-
-  fn.discard = function() {
-    return new VariableNode('discard', 'keyword');
-  }
-  
-  // Generating uniformFloat, uniformVec, createFloat, etc functions
-  // Maps a GLSL type to the name suffix for method names
-  const GLSLTypesToIdentifiers = {
-  const GLSLTypesToIdentifiers = {
-    int:    'Int',
-    float:  'Float',
-    vec2:   'Vector2',
-    vec3:   'Vector3',
-    vec4:   'Vector4',
-    sampler2D: 'Texture',
-  };
-
-  const nodeConstructors = {
-    int:   (value) => new IntNode(value),
-    float: (value) => new FloatNode(value),
-    vec2:  (value) => new VectorNode(value, 'vec2'),
-    vec3:  (value) => new VectorNode(value, 'vec3'),
-    vec4:  (value) => new VectorNode(value, 'vec4'),
-  };
-
-  for (const glslType in GLSLTypesToIdentifiers) {
-  for (const glslType in GLSLTypesToIdentifiers) {
-    // Generate uniform*() Methods for creating uniforms
-    const typeIdentifier = GLSLTypesToIdentifiers[glslType];
-    const typeIdentifier = GLSLTypesToIdentifiers[glslType];
-    const uniformMethodName = `uniform${typeIdentifier}`;
-
-    ShaderGenerator.prototype[uniformMethodName] = function(...args) {
-      let [name, ...defaultValue] = args;
-
-      if(glslType.startsWith('vec')) {
-        defaultValue = conformVectorParameters(defaultValue, +glslType.slice(3));
-        this.output.uniforms[`${glslType} ${name}`] = defaultValue;
-      } 
-      else {
-        this.output.uniforms[`${glslType} ${name}`] = defaultValue[0];
-      }
-
-      let safeType = glslType === 'sampler2D' ? 'vec4' : glslType;
-      const uniform = new VariableNode(name, safeType, false);
-      return uniform;
-    };
-
-    fn[uniformMethodName] = function (...args) { 
-      return GLOBAL_SHADER[uniformMethodName](...args); 
-    };
-
-    // We don't need a createTexture method.
-    if (glslType === 'sampler2D') { continue; }
-    
-    // Generate the create*() Methods for creating variables in shaders
-    const createMethodName = `create${typeIdentifier}`;
-    fn[createMethodName] = function (...value) {
-      if(glslType.startsWith('vec')) {
-        value = conformVectorParameters(value, +glslType.slice(3));
-      } else {
-        value = value[0];
-      }
-      return nodeConstructors[glslType](value);
-    }
-  }
-  
-  // GLSL Built in functions
-  // Add a whole lot of these functions. 
-  // https://docs.gl/el3/abs
-  // TODO: 
-  //  In reality many of these have multiple overrides which will need to address later.
-  // Also, their return types depend on the genType which will need to address urgently
-  //      genType clamp(genType x,
-  //                    genType minVal,
-  //                    genType maxVal);
-  //      genType clamp(genType x,
-  //                    float minVal,
-  //                    float maxVal);
-
-
-
-  const builtInFunctions = {
-    //////////// Trigonometry //////////
-    'acos': { args: ['genType'], returnType: 'float'},
-    // 'acosh': {},
-    'asin': { args: ['genType'], returnType: 'float'},
-    // 'asinh': {},
-    'atan': { args: ['genType'], returnType: 'float'},
-    // 'atanh': {},
-    'cos': { args: ['genType'], returnType: 'float', isp5Function: true},
-    // 'cosh': {},
-    'degrees': { args: ['genType'], returnType: 'float'},
-    'radians': { args: ['genType'], returnType: 'float'},
-    'sin': { args: ['genType'], returnType: 'float' , isp5Function: true},
-    // 'sinh': {},
-    'tan': { args: ['genType'], returnType: 'float', isp5Function: true},
-    // 'tanh': {},
-
-    ////////// Mathematics //////////
-    'abs': { args: ['genType'], returnType: 'float'},
-    'ceil': { args: ['genType'], returnType: 'float'},
-    'clamp': { args: ['genType', 'genType', 'genType'], returnType: 'float'},
-    // 'dFdx': {},
-    // 'dFdy': {},
-    'exp': { args: ['genType'], returnType: 'float'},
-    'exp2': { args: ['genType'], returnType: 'float'},
-    'floor': { args: ['genType'], returnType: 'float'},
-    // 'fma': {},
-    'fract': { args: ['genType'], returnType: 'float'},
-    // 'fwidth': {},
-    // 'inversesqrt': {},
-    // 'isinf': {},
-    // 'isnan': {},
-    // 'log': {},
-    // 'log2': {},
-    'max': { args: ['genType'], returnType: 'genType'},
-    'min': { args: ['genType'], returnType: 'genType'},
-    'mix': { args: ['genType'], returnType: 'genType'},
-    // 'mod': {},
-    // 'modf': {},
-    'pow': { args: ['genType'], returnType: 'float'},
-    // 'round': {},
-    // 'roundEven': {},
-    // 'sign': {},
-    'smoothstep': { args: ['genType', 'genType', 'genType'], returnType: 'float'},
-    'sqrt': { args: ['genType'], returnType: 'genType'},
-    'step': { args: ['genType', 'genType'], returnType: 'genType'},
-    // 'trunc': {},
-
-    ////////// Vector //////////
-    'cross': {},
-    'distance': {},
-    'dot': {},
-    // 'equal': {},
-    // 'faceforward': {},
-    'length': {},
-    'normalize': {},
-    // 'notEqual': {},
-    // 'reflect': {},
-    // 'refract': {},
-    // Texture sampling
-    'texture': {},
-  }
-
-  // Object.entries(builtInFunctions).forEach(([functionName, properties]) => {
-  //   fn[functionName] = function () {
-  //     new FunctionCallNode(functionName, args, type, isInternal = false)
-  //   }
-  // })
-
-  const oldTexture = p5.prototype.texture;
-  p5.prototype.texture = function(...args) {
-    if (isShaderNode(args[0])) {
-      return new FunctionCallNode('texture', args, 'vec4');
-    } else {
-      return oldTexture.apply(this, args);
-    }
-  }
-}
-
-export default shadergen;
-
-if (typeof p5 !== 'undefined') {
-  p5.registerAddon(shadergen)
-}
\ No newline at end of file
diff --git a/src/webgl/ShaderGenerator.js b/src/webgl/ShaderGenerator.js
index 0da6b2f523..554c6ede97 100644
--- a/src/webgl/ShaderGenerator.js
+++ b/src/webgl/ShaderGenerator.js
@@ -9,7 +9,7 @@ import { parse } from 'acorn';
 import { ancestor } from 'acorn-walk';
 import escodegen from 'escodegen';
 
-function shadergen(p5, fn) {
+function shadergenerator(p5, fn) {
   let GLOBAL_SHADER;
   
   const oldModify = p5.Shader.prototype.modify
@@ -343,12 +343,17 @@ function shadergen(p5, fn) {
 
   // Function Call Nodes
   class FunctionCallNode extends BaseNode {
-    constructor(name, args, type, isInternal = false) {
-      super(isInternal, type);
+    constructor(name, args, properties, isInternal = false) {
+      let returnType = properties.returnType;
+      if (returnType === 'genType') {
+        returnType = args[0].type;
+        console.log("GENTYPE")
+      }
+      super(isInternal, returnType);
       this.name = name;
       this.args = args;
-      // TODO:
-      this.argumentTypes = args;
+      this.argumentTypes = properties.args;
+      this.addVectorComponents();
     }
 
     deconstructArgs(context) {
@@ -531,7 +536,6 @@ function shadergen(p5, fn) {
       // Supported types for now
       const glslNativeTypes = ['int', 'float', 'vec2', 'vec3', 'vec4', 'sampler2D'];
       return glslNativeTypes.includes(typeName);
-    }
   }
 
     // Helper function to check if a type is a user defined struct or native type
@@ -688,7 +692,6 @@ function shadergen(p5, fn) {
     vec4:  (value) => new VectorNode(value, 'vec4'),
   };
 
-  for (const glslType in GLSLTypesToIdentifiers) {
   for (const glslType in GLSLTypesToIdentifiers) {
     // Generate uniform*() Methods for creating uniforms
     const typeIdentifier = GLSLTypesToIdentifiers[glslType];
@@ -704,9 +707,7 @@ function shadergen(p5, fn) {
       else {
         this.output.uniforms[`${glslType} ${name}`] = defaultValue[0];
       }
-
-      let safeType = glslType === 'sampler2D' ? 'vec4' : glslType;
-      const uniform = new VariableNode(name, safeType, false);
+      const uniform = new VariableNode(name, glslType, false);
       return uniform;
     };
 
@@ -732,7 +733,6 @@ function shadergen(p5, fn) {
   // GLSL Built in functions
   // Add a whole lot of these functions. 
   // https://docs.gl/el3/abs
-  // TODO: 
   //  In reality many of these have multiple overrides which will need to address later.
   // Also, their return types depend on the genType which will need to address urgently
   //      genType clamp(genType x,
@@ -741,90 +741,95 @@ function shadergen(p5, fn) {
   //      genType clamp(genType x,
   //                    float minVal,
   //                    float maxVal);
-
-
-
   const builtInFunctions = {
     //////////// Trigonometry //////////
-    'acos': { args: ['genType'], returnType: 'float'},
-    // 'acosh': {},
-    'asin': { args: ['genType'], returnType: 'float'},
-    // 'asinh': {},
-    'atan': { args: ['genType'], returnType: 'float'},
-    // 'atanh': {},
-    'cos': { args: ['genType'], returnType: 'float', isp5Function: true},
-    // 'cosh': {},
-    'degrees': { args: ['genType'], returnType: 'float'},
-    'radians': { args: ['genType'], returnType: 'float'},
-    'sin': { args: ['genType'], returnType: 'float' , isp5Function: true},
-    // 'sinh': {},
-    'tan': { args: ['genType'], returnType: 'float', isp5Function: true},
-    // 'tanh': {},
+    'acos': { args: ['genType'], returnType: 'genType', isp5Function: true},
+    'acosh': { args: ['genType'], returnType: 'genType', isp5Function: false},
+    'asin': { args: ['genType'], returnType: 'genType', isp5Function: true},
+    'asinh': { args: ['genType'], returnType: 'genType', isp5Function: false},
+    'atan': { args: ['genType', 'genType'], returnType: 'genType', isp5Function: false},
+    'atanh': { args: ['genType'], returnType: 'genType', isp5Function: false},
+    'cos': { args: ['genType'], returnType: 'genType', isp5Function: true},
+    'cosh': { args: ['genType'], returnType: 'genType', isp5Function: false},
+    'degrees': { args: ['genType'], returnType: 'genType', isp5Function: true},
+    'radians': { args: ['genType'], returnType: 'genType', isp5Function: true},
+    'sin': { args: ['genType'], returnType: 'genType' , isp5Function: true},
+    'sinh': { args: ['genType'], returnType: 'genType', isp5Function: false},
+    'tan': { args: ['genType'], returnType: 'genType', isp5Function: true},
+    'tanh': { args: ['genType'], returnType: 'genType', isp5Function: false},
 
     ////////// Mathematics //////////
-    'abs': { args: ['genType'], returnType: 'float'},
-    'ceil': { args: ['genType'], returnType: 'float'},
-    'clamp': { args: ['genType', 'genType', 'genType'], returnType: 'float'},
-    // 'dFdx': {},
-    // 'dFdy': {},
-    'exp': { args: ['genType'], returnType: 'float'},
-    'exp2': { args: ['genType'], returnType: 'float'},
-    'floor': { args: ['genType'], returnType: 'float'},
-    // 'fma': {},
-    'fract': { args: ['genType'], returnType: 'float'},
-    // 'fwidth': {},
-    // 'inversesqrt': {},
+    'abs': { args: ['genType'], returnType: 'genType', isp5Function: true},
+    'ceil': { args: ['genType'], returnType: 'genType', isp5Function: true},
+    'clamp': { args: ['genType', 'genType', 'genType'], returnType: 'genType', isp5Function: false},
+    'dFdx': { args: ['genType'], returnType: 'genType', isp5Function: false},
+    'dFdy': { args: ['genType'], returnType: 'genType', isp5Function: false},
+    'exp': { args: ['genType'], returnType: 'genType', isp5Function: true},
+    'exp2': { args: ['genType'], returnType: 'genType', isp5Function: false},
+    'floor': { args: ['genType'], returnType: 'genType', isp5Function: true},
+    'fma': { args: ['genType', 'genType', 'genType'], returnType: 'genType', isp5Function: false},
+    'fract': { args: ['genType'], returnType: 'genType', isp5Function: true},
+    'fwidth': { args: ['genType'], returnType: 'genType', isp5Function: false},
+    'inversesqrt': { args: ['genType'], returnType: 'genType', isp5Function: true},
     // 'isinf': {},
     // 'isnan': {},
-    // 'log': {},
-    // 'log2': {},
-    'max': { args: ['genType'], returnType: 'genType'},
-    'min': { args: ['genType'], returnType: 'genType'},
-    'mix': { args: ['genType'], returnType: 'genType'},
+    'log': { args: ['genType'], returnType: 'genType', isp5Function: true},
+    'log2': { args: ['genType'], returnType: 'genType', isp5Function: false},
+    'max': { args: ['genType'], returnType: 'genType', isp5Function: true},
+    'min': { args: ['genType'], returnType: 'genType', isp5Function: true},
+    'mix': { args: ['genType'], returnType: 'genType', isp5Function: false},
     // 'mod': {},
     // 'modf': {},
-    'pow': { args: ['genType'], returnType: 'float'},
-    // 'round': {},
-    // 'roundEven': {},
+    'pow': { args: ['genType'], returnType: 'genType', isp5Function: true},
+    'round': { args: ['genType'], returnType: 'genType', isp5Function: true},
+    'roundEven': { args: ['genType'], returnType: 'genType', isp5Function: false},
     // 'sign': {},
-    'smoothstep': { args: ['genType', 'genType', 'genType'], returnType: 'float'},
-    'sqrt': { args: ['genType'], returnType: 'genType'},
-    'step': { args: ['genType', 'genType'], returnType: 'genType'},
-    // 'trunc': {},
+    'smoothstep': { args: ['genType', 'genType', 'genType'], returnType: 'genType', isp5Function: false},
+    'sqrt': { args: ['genType'], returnType: 'genType', isp5Function: true},
+    'step': { args: ['genType', 'genType'], returnType: 'genType', isp5Function: false},
+    'trunc': { args: ['genType'], returnType: 'genType', isp5Function: false},
 
     ////////// Vector //////////
-    'cross': {},
-    'distance': {},
-    'dot': {},
+    'cross': { args: ['vec3', 'vec3'], returnType: 'vec3', isp5Function: true},
+    'distance': { args: ['genType', 'genType'], returnType: 'float', isp5Function: true},
+    'dot': { args: ['genType', 'genType'], returnType: 'float', isp5Function: true},
     // 'equal': {},
-    // 'faceforward': {},
-    'length': {},
-    'normalize': {},
+    'faceforward': { args: ['genType', 'genType', 'genType'], returnType: 'genType', isp5Function: false},
+    'length': { args: ['genType'], returnType: 'float', isp5Function: false},
+    'normalize': { args: ['genType'], returnType: 'genType', isp5Function: true},
     // 'notEqual': {},
-    // 'reflect': {},
-    // 'refract': {},
+    'reflect': { args: ['genType', 'genType'], returnType: 'genType', isp5Function: false},
+    'refract': { args: ['genType', 'genType', 'float'], returnType: 'genType', isp5Function: false},
     // Texture sampling
-    'texture': {},
+    'texture': {args: ['sampler2D', 'vec2'], returnType: 'vec4', isp5Function: true},
   }
 
-  // Object.entries(builtInFunctions).forEach(([functionName, properties]) => {
-  //   fn[functionName] = function () {
-  //     new FunctionCallNode(functionName, args, type, isInternal = false)
-  //   }
-  // })
-
-  const oldTexture = p5.prototype.texture;
-  p5.prototype.texture = function(...args) {
-    if (isShaderNode(args[0])) {
-      return new FunctionCallNode('texture', args, 'vec4');
+  Object.entries(builtInFunctions).forEach(([functionName, properties]) => {
+    if (properties.isp5Function) {
+      const originalFn = fn[functionName];
+      
+      fn[functionName] = function (...args) {
+        return new FunctionCallNode(functionName, args, properties)
+      }
     } else {
-      return oldTexture.apply(this, args);
+      fn[functionName] = function (...args) {
+        return new FunctionCallNode(functionName, args, properties);
+      }
     }
-  }
+  })
+
+  // const oldTexture = p5.prototype.texture;
+  // p5.prototype.texture = function(...args) {
+  //   if (isShaderNode(args[0])) {
+  //     return new FunctionCallNode('texture', args, 'vec4');
+  //   } else {
+  //     return oldTexture.apply(this, args);
+  //   }
+  // }
 }
 
-export default shadergen;
+export default shadergenerator;
 
 if (typeof p5 !== 'undefined') {
-  p5.registerAddon(shadergen)
+  p5.registerAddon(shadergenerator)
 }
\ No newline at end of file
diff --git a/src/webgl/index.js b/src/webgl/index.js
index 2ce0957fa6..7ba587b132 100644
--- a/src/webgl/index.js
+++ b/src/webgl/index.js
@@ -14,7 +14,7 @@ import shader from './p5.Shader';
 import camera from './p5.Camera';
 import texture from './p5.Texture';
 import rendererGL from './p5.RendererGL';
-import shadergen from './ShaderGen';
+import shadergenerator from './ShaderGenerator';
 
 export default function(p5){
   rendererGL(p5, p5.prototype);
@@ -33,5 +33,5 @@ export default function(p5){
   dataArray(p5, p5.prototype);
   shader(p5, p5.prototype);
   texture(p5, p5.prototype);
-  shadergen(p5, p5.prototype);
+  shadergenerator(p5, p5.prototype);
 }

From 9932797a19ee73ad407fd3b99935886ecca6166a Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Thu, 13 Mar 2025 16:35:22 +0000
Subject: [PATCH 45/54] sketch example

---
 preview/global/sketch.js | 33 +++++++--------------------------
 1 file changed, 7 insertions(+), 26 deletions(-)

diff --git a/preview/global/sketch.js b/preview/global/sketch.js
index 46b7d00a22..e616d5ac2e 100644
--- a/preview/global/sketch.js
+++ b/preview/global/sketch.js
@@ -2,28 +2,26 @@ p5.disableFriendlyErrors = true;
 function windowResized() {
   resizeCanvas(windowWidth, windowHeight);
 }
-
 let myShader;
 
-function myCol() {
-  const col = (sin(millis() * 0.001) + 1)/2;
-  return col;
-}
-
 function setup(){
   createCanvas(windowWidth, windowHeight, WEBGL);
+
   myShader = baseMaterialShader().modify(() => {
-    const time = uniformFloat(() => sin(millis()*0.001));
+    const time = uniformFloat(() => millis());
+
     getFinalColor((col) => {
       col.x = uvCoords().x;
       col.y = uvCoords().y;
+      col.z = 1;
       return col;
     });
+
     getWorldInputs((inputs) => {
-      inputs.position.x += time * inputs.position.y;
+      inputs.position.y += 20 * sin(time * 0.001 + inputs.position.x * 0.05);
       return inputs;
     })
-  }, { parser: true, srcLocations: false });
+  });
 }
 
 function draw(){
@@ -34,20 +32,3 @@ function draw(){
   fill(255,0,0)
   sphere(100);
 }
-
-// `(vec4 color) {
-//   // From at <computed> [as uniformVector4] (http://localhost:5173/p5.js:86002:25)
-//   vec4 temp_0 = uCol;
-//   temp_0 = vec4(temp_0.x, 1.0000, temp_0.z, temp_0.w);
-//   vec4 finalReturnValue = temp_0;
-//   return finalReturnValue;
-// }`
-// `
-// (vec4 color) {
-
-// // From at <computed> [as uniformVector4] (http://localhost:5173/p5.js:86002:25)
-// vec4 temp_0 = uCol;
-// temp_0 = vec4(temp_0.x, 1.0000, temp_0.z, temp_0.w);
-// vec4 finalReturnValue = temp_0 + vec4(0.0000 + 2.0000, 0.0000, 0.0000, 0.0000);
-// return finalReturnValue;
-// }`
\ No newline at end of file

From a41f7732aef4fc7d923044718b9c88bdf4df06e0 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Fri, 14 Mar 2025 15:33:28 +0000
Subject: [PATCH 46/54] remove console log

---
 src/webgl/ShaderGenerator.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/webgl/ShaderGenerator.js b/src/webgl/ShaderGenerator.js
index 554c6ede97..cc7db4277d 100644
--- a/src/webgl/ShaderGenerator.js
+++ b/src/webgl/ShaderGenerator.js
@@ -347,7 +347,6 @@ function shadergenerator(p5, fn) {
       let returnType = properties.returnType;
       if (returnType === 'genType') {
         returnType = args[0].type;
-        console.log("GENTYPE")
       }
       super(isInternal, returnType);
       this.name = name;
@@ -575,6 +574,7 @@ function shadergenerator(p5, fn) {
 
       Object.keys(availableHooks).forEach((hookName) => {
         const hookTypes = originalShader.hookTypes(hookName);
+        console.log(hookTypes);
         this[hookTypes.name] = function(userCallback) {
           // Create the initial nodes which are passed to the user callback
           // Also generate a string of the arguments for the code generation

From 2655de2203205b50b670fb44b80b34bb1fd17a7f Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Fri, 14 Mar 2025 17:17:33 +0000
Subject: [PATCH 47/54] infer types from function calls

---
 src/webgl/ShaderGenerator.js | 31 ++++++++++++++++++++++++-------
 1 file changed, 24 insertions(+), 7 deletions(-)

diff --git a/src/webgl/ShaderGenerator.js b/src/webgl/ShaderGenerator.js
index cc7db4277d..8a27a76634 100644
--- a/src/webgl/ShaderGenerator.js
+++ b/src/webgl/ShaderGenerator.js
@@ -58,7 +58,7 @@ function shadergenerator(p5, fn) {
 
   const ASTCallbacks = {
     VariableDeclarator(node, _state, _ancestors) {
-      if (node.init.callee && node.init.callee.name.startsWith('uniform')) {
+      if (node.init.callee && node.init.callee.name?.startsWith('uniform')) {
         const uniformNameLiteral = {
           type: 'Literal',
           value: node.id.name
@@ -344,11 +344,28 @@ function shadergenerator(p5, fn) {
   // Function Call Nodes
   class FunctionCallNode extends BaseNode {
     constructor(name, args, properties, isInternal = false) {
-      let returnType = properties.returnType;
-      if (returnType === 'genType') {
-        returnType = args[0].type;
+      let inferredType = args.find((arg, i) => {
+        properties.args[i] === 'genType'
+        && isShaderNode(arg)
+      })?.type;
+      if (!inferredType) {
+        let arrayArg = args.find(arg => Array.isArray(arg));
+        inferredType = arrayArg ? `vec${arrayArg.length}` : undefined;
+      }
+      if (!inferredType) {
+        inferredType = 'float'; 
+      }
+      args = args.map((arg, i) => {
+        if (!isShaderNode(arg)) {
+          const typeName = properties.args[i] === 'genType' ? inferredType : properties.args[i];
+          arg = nodeConstructors[typeName](arg);
+        }
+        return arg;
+      })
+      if (properties.returnType === 'genType') {
+        properties.returnType = inferredType;
       }
-      super(isInternal, returnType);
+      super(isInternal, properties.returnType);
       this.name = name;
       this.args = args;
       this.argumentTypes = properties.args;
@@ -741,7 +758,7 @@ function shadergenerator(p5, fn) {
   //      genType clamp(genType x,
   //                    float minVal,
   //                    float maxVal);
-  const builtInFunctions = {
+  const builtInGLSLFunctions = {
     //////////// Trigonometry //////////
     'acos': { args: ['genType'], returnType: 'genType', isp5Function: true},
     'acosh': { args: ['genType'], returnType: 'genType', isp5Function: false},
@@ -804,7 +821,7 @@ function shadergenerator(p5, fn) {
     'texture': {args: ['sampler2D', 'vec2'], returnType: 'vec4', isp5Function: true},
   }
 
-  Object.entries(builtInFunctions).forEach(([functionName, properties]) => {
+  Object.entries(builtInGLSLFunctions).forEach(([functionName, properties]) => {
     if (properties.isp5Function) {
       const originalFn = fn[functionName];
       

From 3d9156b547288984aa9d412cb52b8a6f9f99d424 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Fri, 14 Mar 2025 17:17:59 +0000
Subject: [PATCH 48/54] filter shader example

---
 preview/global/sketch.js | 31 +++++++++++++++++++++++++------
 1 file changed, 25 insertions(+), 6 deletions(-)

diff --git a/preview/global/sketch.js b/preview/global/sketch.js
index e616d5ac2e..f9cede49d5 100644
--- a/preview/global/sketch.js
+++ b/preview/global/sketch.js
@@ -1,12 +1,29 @@
-p5.disableFriendlyErrors = true;
+// p5.disableFriendlyErrors = true;
 function windowResized() {
   resizeCanvas(windowWidth, windowHeight);
 }
 let myShader;
+let filterShader;
+let video;
 
-function setup(){
+
+async function setup(){
   createCanvas(windowWidth, windowHeight, WEBGL);
 
+  // filterShader = baseFi
+
+  filterShader = baseFilterShader().modify(() => {
+    const time = uniformFloat(() => millis());
+
+    getColor((input, canvasContents) => {
+      let myColor = texture(canvasContents, uvCoords());
+      const d = distance(input.texCoord, [0.5, 0.5]);
+      myColor.x = smoothstep(0, 0.5, d);
+      myColor.y = sin(time*0.001)/2;
+      return myColor;
+    })
+  });
+
   myShader = baseMaterialShader().modify(() => {
     const time = uniformFloat(() => millis());
 
@@ -25,10 +42,12 @@ function setup(){
 }
 
 function draw(){
-  orbitControl();
+  // orbitControl();
   background(0);
-  shader(myShader);
   noStroke();
-  fill(255,0,0)
-  sphere(100);
+  fill('blue')
+  sphere(100)
+  shader(myShader);
+  filter(filterShader);
+  // filterShader.setUniform('time', millis());
 }

From 9b78c4b321e49cf26a5a6c95563fb45632d16fff Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Fri, 14 Mar 2025 17:34:30 +0000
Subject: [PATCH 49/54] set default uniforms on filters outside of WebGL mode

---
 src/image/filterRenderer2D.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/image/filterRenderer2D.js b/src/image/filterRenderer2D.js
index 243652d5f8..5e2dcc3107 100644
--- a/src/image/filterRenderer2D.js
+++ b/src/image/filterRenderer2D.js
@@ -234,7 +234,8 @@ class FilterRenderer2D {
     this._shader.setUniform('canvasSize', [this.pInst.width, this.pInst.height]);
     this._shader.setUniform('radius', Math.max(1, this.filterParameter));
     this._shader.setUniform('filterParameter', this.filterParameter);
-
+    this._shader.setDefaultUniforms();
+    
     this.pInst.states.setValue('rectMode', constants.CORNER);
     this.pInst.states.setValue('imageMode', constants.CORNER);
     this.pInst.blendMode(constants.BLEND);

From 91ea2aca51f4a962044247417fc520a072f2548d Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Fri, 14 Mar 2025 18:00:28 +0000
Subject: [PATCH 50/54] fix user fill shaders overriding filters

---
 src/webgl/p5.RendererGL.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js
index 4f988c425c..3e1b440cc4 100644
--- a/src/webgl/p5.RendererGL.js
+++ b/src/webgl/p5.RendererGL.js
@@ -636,7 +636,7 @@ class RendererGL extends Renderer {
     this._useVertexColor = geometry.vertexColors.length > 0;
 
     const shader =
-      this._drawingFilter && this.states.userFillShader
+      !this._drawingFilter && this.states.userFillShader
         ? this.states.userFillShader
         : this._getFillShader();
     shader.bindShader();

From 9c4aef8cf6d2415b57ca09805da60f7b0f239a97 Mon Sep 17 00:00:00 2001
From: Dave Pagurek <davepagurek@gmail.com>
Date: Fri, 14 Mar 2025 19:12:25 -0400
Subject: [PATCH 51/54] Change FES behaviour while executing a shader generator

---
 src/webgl/ShaderGenerator.js | 137 +++++++++++++++++++++--------------
 1 file changed, 84 insertions(+), 53 deletions(-)

diff --git a/src/webgl/ShaderGenerator.js b/src/webgl/ShaderGenerator.js
index 8a27a76634..b98ae876f4 100644
--- a/src/webgl/ShaderGenerator.js
+++ b/src/webgl/ShaderGenerator.js
@@ -11,7 +11,7 @@ import escodegen from 'escodegen';
 
 function shadergenerator(p5, fn) {
   let GLOBAL_SHADER;
-  
+
   const oldModify = p5.Shader.prototype.modify
 
   p5.Shader.prototype.modify = function(shaderModifier, options = { parser: true, srcLocations: false }) {
@@ -19,9 +19,9 @@ function shadergenerator(p5, fn) {
       let generatorFunction;
       if (options.parser) {
         const sourceString = shaderModifier.toString()
-        const ast = parse(sourceString, { 
-          ecmaVersion: 2021, 
-          locations: options.srcLocations 
+        const ast = parse(sourceString, {
+          ecmaVersion: 2021,
+          locations: options.srcLocations
         });
         ancestor(ast, ASTCallbacks);
         const transpiledSource = escodegen.generate(ast);
@@ -39,7 +39,7 @@ function shadergenerator(p5, fn) {
       console.log("SRC STRING: ", generatorFunction);
       console.log("NEW OPTIONS:", generatedModifyArgument)
       return oldModify.call(this, generatedModifyArgument);
-    } 
+    }
     else {
       return oldModify.call(this, shaderModifier)
     }
@@ -67,7 +67,7 @@ function shadergenerator(p5, fn) {
       }
     },
     // The callbacks for AssignmentExpression and BinaryExpression handle
-    // operator overloading including +=, *= assignment expressions 
+    // operator overloading including +=, *= assignment expressions
     AssignmentExpression(node, _state, _ancestors) {
       if (node.operator !== '=') {
         const methodName = replaceBinaryOperator(node.operator.replace('=',''));
@@ -89,7 +89,7 @@ function shadergenerator(p5, fn) {
         }
       },
     BinaryExpression(node, _state, ancestors) {
-      // Don't convert uniform default values to node methods, as 
+      // Don't convert uniform default values to node methods, as
       // they should be evaluated at runtime, not compiled.
       const isUniform = (ancestor) => {
         return ancestor.type === 'CallExpression'
@@ -215,7 +215,7 @@ function shadergenerator(p5, fn) {
       score += this.usedIn.length;
       return score > 3;
     }
-    
+
     getTemporaryVariable(context) {
       if (!this.temporaryVariable) {
         this.temporaryVariable = `temp_${context.getNextID()}`;
@@ -238,7 +238,7 @@ function shadergenerator(p5, fn) {
       }
       return this.temporaryVariable;
     };
-    
+
     // Binary Operators
     add(other)  { return new BinaryOperatorNode(this, this.enforceType(other), '+'); }
     sub(other)  { return new BinaryOperatorNode(this, this.enforceType(other), '-'); }
@@ -273,7 +273,7 @@ function shadergenerator(p5, fn) {
         return new this.constructor(other);
       }
     }
-    
+
     toGLSL(context){
       throw new TypeError("Not supposed to call this function on BaseNode, which is an abstract class.");
     }
@@ -299,7 +299,7 @@ function shadergenerator(p5, fn) {
       }
     }
   }
-  
+
   class FloatNode extends BaseNode {
     constructor(x = 0, isInternal = false){
       super(isInternal, 'float');
@@ -319,7 +319,7 @@ function shadergenerator(p5, fn) {
       }
     }
   }
-  
+
   class VectorNode extends BaseNode {
     constructor(values, type, isInternal = false) {
       super(isInternal, type);
@@ -328,7 +328,7 @@ function shadergenerator(p5, fn) {
         this[component] = new FloatNode(values[i], true);
       });
     }
-    
+
     toGLSL(context) {
       let glslArgs = ``;
 
@@ -353,7 +353,7 @@ function shadergenerator(p5, fn) {
         inferredType = arrayArg ? `vec${arrayArg.length}` : undefined;
       }
       if (!inferredType) {
-        inferredType = 'float'; 
+        inferredType = 'float';
       }
       args = args.map((arg, i) => {
         if (!isShaderNode(arg)) {
@@ -384,7 +384,7 @@ function shadergenerator(p5, fn) {
       return `${this.name}(${this.deconstructArgs(context)})`;
     }
   }
-  
+
   // Variables and member variable nodes
   class VariableNode extends BaseNode {
     constructor(name, type, isInternal = false) {
@@ -398,7 +398,7 @@ function shadergenerator(p5, fn) {
       return `${this.name}`;
     }
   }
-  
+
   class ComponentNode extends BaseNode {
     constructor(parent, componentName, type, isInternal = false) {
       super(isInternal, type);
@@ -408,7 +408,7 @@ function shadergenerator(p5, fn) {
     }
     toGLSL(context) {
       const parentName = this.parent.toGLSLBase(context);
-      // const parentName = this.parent.temporaryVariable ? this.parent.temporaryVariable : this.parent.name; 
+      // const parentName = this.parent.temporaryVariable ? this.parent.temporaryVariable : this.parent.name;
       return `${parentName}.${this.componentName}`;
     }
   }
@@ -426,19 +426,19 @@ function shadergenerator(p5, fn) {
       this.type = this.determineType();
       this.addVectorComponents();
     }
-    
+
     // We know that both this.a and this.b are nodes because of BaseNode.enforceType
     determineType() {
       if (this.a.type === this.b.type) {
         return this.a.type;
-      } 
+      }
       else if (isVectorNode(this.a) && isFloatNode(this.b)) {
         return this.a.type;
-      } 
+      }
       else if (isVectorNode(this.b) && isFloatNode(this.a)) {
         return this.b.type;
-      } 
-      else if (isFloatNode(this.a) && isIntNode(this.b) 
+      }
+      else if (isFloatNode(this.a) && isIntNode(this.b)
         || isIntNode(this.a) && isFloatNode(this.b)
       ) {
         return 'float';
@@ -450,7 +450,7 @@ function shadergenerator(p5, fn) {
 
     processOperand(operand, context) {
       if (operand.temporaryVariable) { return operand.temporaryVariable; }
-      let code = operand.toGLSLBase(context);      
+      let code = operand.toGLSLBase(context);
       if (isBinaryOperatorNode(operand) && !operand.temporaryVariable) {
         code = `(${code})`;
       }
@@ -503,7 +503,7 @@ function shadergenerator(p5, fn) {
     // returns
     thenReturn(value) {}
     elseReturn(value) {}
-    // 
+    //
     thenDiscard() {
       new ConditionalDiscard(this.condition);
     };
@@ -523,28 +523,28 @@ function shadergenerator(p5, fn) {
   }
 
   // Node Helper functions
-  function isShaderNode(node) {  
-    return (node instanceof BaseNode); 
+  function isShaderNode(node) {
+    return (node instanceof BaseNode);
   }
 
-  function isIntNode(node) { 
-    return (isShaderNode(node) && (node.type === 'int')); 
+  function isIntNode(node) {
+    return (isShaderNode(node) && (node.type === 'int'));
   }
 
-  function isFloatNode(node) { 
-    return (isShaderNode(node) && (node.type === 'float')); 
+  function isFloatNode(node) {
+    return (isShaderNode(node) && (node.type === 'float'));
   }
 
   function isVectorNode(node) {
-    return (isShaderNode(node) && (node.type === 'vec2'|| node.type === 'vec3' || node.type === 'vec4')); 
+    return (isShaderNode(node) && (node.type === 'vec2'|| node.type === 'vec3' || node.type === 'vec4'));
   }
 
   function isBinaryOperatorNode(node) {
     return (node instanceof BinaryOperatorNode);
   }
 
-  function isVariableNode(node) { 
-    return (node instanceof VariableNode || node instanceof ComponentNode || typeof(node.temporaryVariable) != 'undefined'); 
+  function isVariableNode(node) {
+    return (node instanceof VariableNode || node instanceof ComponentNode || typeof(node.temporaryVariable) != 'undefined');
   }
 
     // Helper function to check if a type is a user defined struct or native type
@@ -570,15 +570,26 @@ function shadergenerator(p5, fn) {
       this.userCallback = userCallback;
       this.userCallback = userCallback;
       this.srcLocations = srcLocations;
+      this.cleanup = () => {};
       this.generateHookOverrides(originalShader);
       this.output = {
         uniforms: {},
       }
       this.resetGLSLContext();
+      this.isGenerating = false;
     }
 
     generate() {
+      const prevFESDisabled = p5.disableFriendlyErrors;
+      // We need a custom error handling system within shader generation
+      p5.disableFriendlyErrors = true;
+
+      this.isGenerating = true;
       this.userCallback();
+      this.isGenerating = false;
+
+      this.cleanup();
+      p5.disableFriendlyErrors = prevFESDisabled;
       return this.output;
     }
 
@@ -589,6 +600,8 @@ function shadergenerator(p5, fn) {
         ...originalShader.hooks.fragment,
       }
 
+      const windowOverrides = {};
+
       Object.keys(availableHooks).forEach((hookName) => {
         const hookTypes = originalShader.hookTypes(hookName);
         console.log(hookTypes);
@@ -599,7 +612,7 @@ function shadergenerator(p5, fn) {
           const argsArray = [];
 
           hookTypes.parameters.forEach((parameter) => {
-            // For hooks with structs as input we should pass an object populated with variable nodes 
+            // For hooks with structs as input we should pass an object populated with variable nodes
             if (!isGLSLNativeType(parameter.type.typeName)) {
               const structArg = {};
               parameter.type.properties.forEach((property) => {
@@ -614,7 +627,7 @@ function shadergenerator(p5, fn) {
             const qualifiers = parameter.type.qualifiers.length > 0 ? parameter.type.qualifiers.join(' ') : '';
             argsArray.push(`${qualifiers} ${parameter.type.typeName} ${parameter.name}`.trim())
           })
-          
+
           let returnedValue = userCallback(...argNodes);
           const expectedReturnType = hookTypes.returnType;
           const toGLSLResults = {};
@@ -631,10 +644,10 @@ function shadergenerator(p5, fn) {
             }
             toGLSLResults['notAProperty'] = returnedValue.toGLSLBase(this.context);
           }
-          
+
           // Build the final GLSL string.
           // The order of this code is a bit confusing, we need to call toGLSLBase
-          let codeLines = [ 
+          let codeLines = [
             `(${argsArray.join(', ')}) {`,
             ...this.context.declarations,
             `${hookTypes.returnType.typeName} finalReturnValue;`
@@ -650,14 +663,22 @@ function shadergenerator(p5, fn) {
           this.resetGLSLContext();
         }
 
+        windowOverrides[hookTypes.name] = window[hookTypes.name];
+
         // Expose the Functions to global scope for users to use
         window[hookTypes.name] = function(userOverride) {
-          GLOBAL_SHADER[hookTypes.name](userOverride); 
+          GLOBAL_SHADER[hookTypes.name](userOverride);
         };
-      })
+      });
+
+      this.cleanup = () => {
+        for (const key in windowOverrides) {
+          window[key] = windowOverrides[key];
+        }
+      };
     }
 
-    resetGLSLContext() { 
+    resetGLSLContext() {
       this.context = {
         id: 0,
         getNextID: function() { return this.id++ },
@@ -670,7 +691,7 @@ function shadergenerator(p5, fn) {
   function conformVectorParameters(value, vectorDimensions) {
     // Allow arguments as arrays ([0,0,0,0]) or not (0,0,0,0)
     value = value.flat();
-    // Populate arguments so uniformVector3(0) becomes [0,0,0] 
+    // Populate arguments so uniformVector3(0) becomes [0,0,0]
     if (value.length === 1) {
       value = Array(vectorDimensions).fill(value[0]);
     }
@@ -681,7 +702,7 @@ function shadergenerator(p5, fn) {
   fn.instanceID = function() {
     return new VariableNode('gl_InstanceID', 'int');
   }
-  
+
   fn.uvCoords = function() {
     return new VariableNode('vTexCoord', 'vec2');
   }
@@ -689,7 +710,7 @@ function shadergenerator(p5, fn) {
   fn.discard = function() {
     return new VariableNode('discard', 'keyword');
   }
-  
+
   // Generating uniformFloat, uniformVec, createFloat, etc functions
   // Maps a GLSL type to the name suffix for method names
   const GLSLTypesToIdentifiers = {
@@ -720,7 +741,7 @@ function shadergenerator(p5, fn) {
       if(glslType.startsWith('vec')) {
         defaultValue = conformVectorParameters(defaultValue, +glslType.slice(3));
         this.output.uniforms[`${glslType} ${name}`] = defaultValue;
-      } 
+      }
       else {
         this.output.uniforms[`${glslType} ${name}`] = defaultValue[0];
       }
@@ -728,13 +749,13 @@ function shadergenerator(p5, fn) {
       return uniform;
     };
 
-    fn[uniformMethodName] = function (...args) { 
-      return GLOBAL_SHADER[uniformMethodName](...args); 
+    fn[uniformMethodName] = function (...args) {
+      return GLOBAL_SHADER[uniformMethodName](...args);
     };
 
     // We don't need a createTexture method.
     if (glslType === 'sampler2D') { continue; }
-    
+
     // Generate the create*() Methods for creating variables in shaders
     const createMethodName = `create${typeIdentifier}`;
     fn[createMethodName] = function (...value) {
@@ -746,9 +767,9 @@ function shadergenerator(p5, fn) {
       return nodeConstructors[glslType](value);
     }
   }
-  
+
   // GLSL Built in functions
-  // Add a whole lot of these functions. 
+  // Add a whole lot of these functions.
   // https://docs.gl/el3/abs
   //  In reality many of these have multiple overrides which will need to address later.
   // Also, their return types depend on the genType which will need to address urgently
@@ -824,13 +845,23 @@ function shadergenerator(p5, fn) {
   Object.entries(builtInGLSLFunctions).forEach(([functionName, properties]) => {
     if (properties.isp5Function) {
       const originalFn = fn[functionName];
-      
+
       fn[functionName] = function (...args) {
-        return new FunctionCallNode(functionName, args, properties)
+        if (GLOBAL_SHADER?.isGenerating) {
+          return new FunctionCallNode(functionName, args, properties)
+        } else {
+          return originalFn.apply(this, args);
+        }
       }
     } else {
       fn[functionName] = function (...args) {
-        return new FunctionCallNode(functionName, args, properties);
+        if (GLOBAL_SHADER?.isGenerating) {
+          return new FunctionCallNode(functionName, args, properties);
+        } else {
+          p5._friendlyError(
+            `It looks like you've called ${functionName} outside of a shader's modify() function.`
+          );
+        }
       }
     }
   })
@@ -849,4 +880,4 @@ export default shadergenerator;
 
 if (typeof p5 !== 'undefined') {
   p5.registerAddon(shadergenerator)
-}
\ No newline at end of file
+}

From d4f6cef905c87584f819b1bb8c0338dcecdf1852 Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Thu, 20 Mar 2025 14:40:30 +0000
Subject: [PATCH 52/54] add toFloat() for ease of use with instanceID()

---
 src/webgl/ShaderGenerator.js | 22 +++++++++++++++++-----
 1 file changed, 17 insertions(+), 5 deletions(-)

diff --git a/src/webgl/ShaderGenerator.js b/src/webgl/ShaderGenerator.js
index b98ae876f4..0ff4adfc54 100644
--- a/src/webgl/ShaderGenerator.js
+++ b/src/webgl/ShaderGenerator.js
@@ -274,6 +274,16 @@ function shadergenerator(p5, fn) {
       }
     }
 
+    toFloat() {
+      if (isFloatNode(this)) {
+        return this;
+      } else if (isIntNode(this)) {
+        return new FloatNode(this);
+      } else {
+        throw new TypeError(`Can't convert from type '${this.type}' to 'float'.`)
+      }
+    }
+
     toGLSL(context){
       throw new TypeError("Not supposed to call this function on BaseNode, which is an abstract class.");
     }
@@ -373,11 +383,13 @@ function shadergenerator(p5, fn) {
     }
 
     deconstructArgs(context) {
-      if (Array.isArray(this.args)) {
-        return this.args.map((argNode) => argNode.toGLSLBase(context)).join(', ');
-      } else {
-        return `${this.args.toGLSLBase(context)}`;
-      }
+      let argsString = this.args.map((argNode, i) => {
+        if (isIntNode(argNode) && this.argumentTypes[i] != 'float') {
+          argNode = argNode.toFloat();
+        }
+        return argNode.toGLSLBase(context);
+      }).join(', ');
+      return argsString;
     }
 
     toGLSL(context) {

From a5e76ddd8f023984488cdb8b3bf79c7e053184ba Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Thu, 20 Mar 2025 14:40:52 +0000
Subject: [PATCH 53/54] remove uvCoords function as vTexCoord is not always
 defined in a given shader

---
 src/webgl/ShaderGenerator.js | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/src/webgl/ShaderGenerator.js b/src/webgl/ShaderGenerator.js
index 0ff4adfc54..7dfd76be32 100644
--- a/src/webgl/ShaderGenerator.js
+++ b/src/webgl/ShaderGenerator.js
@@ -715,10 +715,6 @@ function shadergenerator(p5, fn) {
     return new VariableNode('gl_InstanceID', 'int');
   }
 
-  fn.uvCoords = function() {
-    return new VariableNode('vTexCoord', 'vec2');
-  }
-
   fn.discard = function() {
     return new VariableNode('discard', 'keyword');
   }

From 3dc08f6087a4a98aa6a13ea13aa05d883c927b4e Mon Sep 17 00:00:00 2001
From: 23036879 <l.plowden0620231@arts.ac.uk>
Date: Thu, 20 Mar 2025 14:41:07 +0000
Subject: [PATCH 54/54] working on tutorial sketch

---
 preview/global/sketch.js | 79 +++++++++++++++++++---------------------
 1 file changed, 38 insertions(+), 41 deletions(-)

diff --git a/preview/global/sketch.js b/preview/global/sketch.js
index f9cede49d5..f6d045ff6b 100644
--- a/preview/global/sketch.js
+++ b/preview/global/sketch.js
@@ -1,53 +1,50 @@
-// p5.disableFriendlyErrors = true;
+p5.disableFriendlyErrors = true;
 function windowResized() {
   resizeCanvas(windowWidth, windowHeight);
 }
-let myShader;
-let filterShader;
-let video;
 
+let myModel;
+let starShader;
+let starStrokeShader;
+let stars;
+
+function starShaderCallback() {
+  const time = uniformFloat(() => millis());
+  getWorldInputs((inputs) => {
+    inputs.position.y += instanceID() * 20 - 1000;
+    inputs.position.x += 40*sin(time * 0.001 + instanceID());
+    return inputs;
+  });
+  getObjectInputs((inputs) => {
+    inputs.position *= sin(time*0.001 + instanceID());
+    return inputs;
+  })
+}
 
 async function setup(){
   createCanvas(windowWidth, windowHeight, WEBGL);
-
-  // filterShader = baseFi
-
-  filterShader = baseFilterShader().modify(() => {
-    const time = uniformFloat(() => millis());
-
-    getColor((input, canvasContents) => {
-      let myColor = texture(canvasContents, uvCoords());
-      const d = distance(input.texCoord, [0.5, 0.5]);
-      myColor.x = smoothstep(0, 0.5, d);
-      myColor.y = sin(time*0.001)/2;
-      return myColor;
-    })
-  });
-
-  myShader = baseMaterialShader().modify(() => {
-    const time = uniformFloat(() => millis());
-
-    getFinalColor((col) => {
-      col.x = uvCoords().x;
-      col.y = uvCoords().y;
-      col.z = 1;
-      return col;
-    });
-
-    getWorldInputs((inputs) => {
-      inputs.position.y += 20 * sin(time * 0.001 + inputs.position.x * 0.05);
-      return inputs;
-    })
-  });
+  stars = buildGeometry(() => sphere(20, 7, 4))
+  starShader = baseMaterialShader().modify(starShaderCallback); 
+  starStrokeShader = baseStrokeShader().modify(starShaderCallback)
 }
 
 function draw(){
-  // orbitControl();
-  background(0);
+  background(0,200,240);
+  orbitControl();
+  // noStroke();
+  
+  push();
+  stroke(255,0,255)
+  fill(255,200,255)
+  strokeShader(starStrokeShader)
+  shader(starShader);
+  model(stars, 100);
+  pop();
+  push();
+  shader(baseMaterialShader());
   noStroke();
-  fill('blue')
-  sphere(100)
-  shader(myShader);
-  filter(filterShader);
-  // filterShader.setUniform('time', millis());
+  rotateX(HALF_PI);
+  translate(0, 0, -250);
+  plane(10000)
+  pop();
 }