title: How to cope with WebGL missing glBlendEquation(GL_MAX)
body: <p>Here's my current C code that does what I'd like to do, but it relies on <code>glBlendEquation(GL_MAX)</code> which is unavailable in WebGL. What I want is to render a wiggly fuzzy line. I could use a Gaussian blur but it would have to have a VERY large radius (16 pixels) and I expect it would be REALLY slow.</p>
  
  <p>Note I've removed some gl state management code and a couple other things fore clarity but the code should work as is.</p>
  
  <p>Existing C code:</p>
  
  <pre><code>static const char *pnt_vtx_shader =
      "#version 110\n"
      "varying vec2 uv;\n"
      "void main() {\n"
      "    uv = (gl_MultiTexCoord0.st - 1.0f);\n"
      "    gl_Position = gl_Vertex;\n"
      "}";
  
  static const char *pnt_shader_src =
      "#version 110\n"
      "varying vec2 uv;\n"
      "void main() {\n"
      "    gl_FragColor = vec4(exp(-4.5f*0.5f*log2(dot(uv,uv)+1.0f)));\n"
      "}";
  
  
  GLuint shader_prog ;
  int samp;
  float pw, ph;
  
  float sco_verts[128*8*4];
  int sco_ind[128*3*6];
  
  
  void init(int width, int height, int num_samp)
  {
  
      pw = 0.5f*fmaxf(1.0f/24, 8.0f/width), ph = 0.5f*fmaxf(1.0f/24, 8.0f/height);
      samp = num_samp;
  
      // helper function, compiles and links the shader, prints out any errors
      shader_prog = compile_program(pnt_vtx_shader, pnt_shader_src);
  
      for(int i=0; i&lt;samp; i++) {
          sco_verts[(i*8+0)*4+0] = 0; sco_verts[(i*8+0)*4+1] = 2;
          sco_verts[(i*8+1)*4+0] = 0; sco_verts[(i*8+1)*4+1] = 0;
          sco_verts[(i*8+2)*4+0] = 1; sco_verts[(i*8+2)*4+1] = 2;
          sco_verts[(i*8+3)*4+0] = 1; sco_verts[(i*8+3)*4+1] = 0;
          sco_verts[(i*8+4)*4+0] = 1; sco_verts[(i*8+4)*4+1] = 2;
          sco_verts[(i*8+5)*4+0] = 1; sco_verts[(i*8+5)*4+1] = 0;
          sco_verts[(i*8+6)*4+0] = 2; sco_verts[(i*8+6)*4+1] = 2;
          sco_verts[(i*8+7)*4+0] = 2; sco_verts[(i*8+7)*4+1] = 0;
      }
  
      for(int i=0; i&lt;samp; i++) {
          sco_ind[(i*6+0)*3+0] = i*8+0; sco_ind[(i*6+0)*3+1] = i*8+1; sco_ind[(i*6+0)*3+2] = i*8+3;
          sco_ind[(i*6+1)*3+0] = i*8+0; sco_ind[(i*6+1)*3+1] = i*8+3; sco_ind[(i*6+1)*3+2] = i*8+2;
  
          sco_ind[(i*6+2)*3+0] = i*8+2; sco_ind[(i*6+2)*3+1] = i*8+4; sco_ind[(i*6+2)*3+2] = i*8+5;
          sco_ind[(i*6+3)*3+0] = i*8+2; sco_ind[(i*6+3)*3+1] = i*8+5; sco_ind[(i*6+3)*3+2] = i*8+3;
  
          sco_ind[(i*6+4)*3+0] = i*8+4; sco_ind[(i*6+4)*3+1] = i*8+6; sco_ind[(i*6+4)*3+2] = i*8+7;
          sco_ind[(i*6+5)*3+0] = i*8+4; sco_ind[(i*6+5)*3+1] = i*8+7; sco_ind[(i*6+5)*3+2] = i*8+5;
      }
  }
  
  // getsamp does some averaging over samples
  static float getsamp(const float *data, int len, int i, int w) {
      float sum = 0, err = 0;
      int l = IMAX(i-w, 0);
      int u = IMIN(i+w, len);
      for(int i = l; i &lt; u; i++)
          sum+= data[i];
      return sum / (2*w);
  }
  
  // R holds a rotation matrix... it's the transpose of what you would give GL though
  // because of reasons :P (I wrote code that did all the stuff from this program in
  // software first and the GL version shares a bunch of code with that one)
  // data is audio samples, [-1, 1], the length of the array is in len
  void render_scope(float R[3][3], const float *data, int len)
  {
      // do the rotate/project ourselves because the GL matrix won't do the right
      // thing if we just send it our verticies, we want wour tris to always be 
      // parrallel to the view plane, because we're actually drawing a fuzzy line
      // not a 3D object
      // also it makes it easier to match the software implementation
      float px, py;
      {
          float s = getsamp(data, len, 0, len/96);
          s=copysignf(log2f(fabsf(s)*3+1)/2, s);
          float xt = -0.5f, yt = 0.2f*s, zt = 0.0f;
          float x = R[0][0]*xt + R[1][0]*yt + R[2][0]*zt;
          float y = R[0][1]*xt + R[1][1]*yt + R[2][1]*zt;
          float z = R[0][2]*xt + R[1][2]*yt + R[2][2]*zt;
          const float zvd = 1/(z+2);
          px=x*zvd*4/3; py=y*zvd*4/3;
      }
  
      for(int i=0; i&lt;samp; i++) {
          float s = getsamp(data, len, (i+1)*len/(samp), len/96);
          s=copysignf(log2f(fabsf(s)*3+1)/2, s);
  
          float xt = (i+1 - (samp)/2.0f)*(1.0f/(samp)), yt = 0.2f*s, zt = 0.0f;
          float x = R[0][0]*xt + R[1][0]*yt + R[2][0]*zt;
          float y = R[0][1]*xt + R[1][1]*yt + R[2][1]*zt;
          float z = R[0][2]*xt + R[1][2]*yt + R[2][2]*zt;
          const float zvd = 1/(z+2);
          x=x*zvd*4/3; y=y*zvd*4/3;
  
          const float dx=x-px, dy=y-py;
          const float d = 1/hypotf(dx, dy);
          const float tx=dx*d*pw, ty=dy*d*pw;
          const float nx=-dy*d*pw, ny=dx*d*ph;
  
          sco_verts[(i*8+0)*4+2] = px-nx-tx; sco_verts[(i*8+0)*4+3] = py-ny-ty;
          sco_verts[(i*8+1)*4+2] = px+nx-tx; sco_verts[(i*8+1)*4+3] = py+ny-ty;
          sco_verts[(i*8+2)*4+2] = px-nx   ; sco_verts[(i*8+2)*4+3] = py-ny;
          sco_verts[(i*8+3)*4+2] = px+nx   ; sco_verts[(i*8+3)*4+3] = py+ny;
          sco_verts[(i*8+4)*4+2] =  x-nx   ; sco_verts[(i*8+4)*4+3] =  y-ny;
          sco_verts[(i*8+5)*4+2] =  x+nx   ; sco_verts[(i*8+5)*4+3] =  y+ny;
          sco_verts[(i*8+6)*4+2] =  x-nx+tx; sco_verts[(i*8+6)*4+3] =  y-ny+ty;
          sco_verts[(i*8+7)*4+2] =  x+nx+tx; sco_verts[(i*8+7)*4+3] =  y+ny+ty;
          px=x,py=y;
      }
  
      glEnable(GL_BLEND);
      glBlendEquation(GL_MAX);
      glUseProgram(shader_prog);
  
      glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
      glEnableClientState(GL_VERTEX_ARRAY);
      glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  
      glTexCoordPointer(2, GL_FLOAT, sizeof(float)*4, sco_verts);
      glVertexPointer(2, GL_FLOAT, sizeof(float)*4, sco_verts + 2);
      glDrawElements(GL_TRIANGLES, samp*3*6, GL_UNSIGNED_INT, sco_ind);
  }
  </code></pre>
  
  <p>Here's a screenshot from a test app, I'm not sure the line width is right in this screen shot... but meh it gives the idea, also I'd be using way more points so the lines would be smoother.</p>
  
  <p><img src="https://i.stack.imgur.com/2ZLYW.png" alt="Screenshot"></p>