It's a Hollow Feeling

This code is based on a discussion here.

BACK FRONT FRONT_AND_BACK none
Press x,y,z to rotate.

The HTML File

<html>
<head>

<script id="vertex-shader" type="x-shader/x-vertex" >
precision mediump float;
attribute vec4 vPosition;
attribute vec3 vBC;
attribute vec4 vColor;

//uniform float vFillType;

varying vec3 f_BC;
varying vec4 f_Color;

void main() {

    gl_Position = vPosition;
    f_BC = vBC;
    f_Color = vColor;
}
</script>

<script id="fragment-shader" type="x-shader/x-fragment">
#extension GL_OES_standard_derivatives : enable
precision mediump float;

uniform float vFillType;

varying vec4 f_Color;
varying vec3 f_BC;

    float edgeFactor(){
        vec3 d = fwidth(f_BC);
        vec3 a3 = smoothstep(vec3(0.0), d*1.5, f_BC);
        return min(min(a3.x, a3.y), a3.z);
    }

    void main(){
        if (vFillType >= 1.0) {
             if(gl_FrontFacing){
                 gl_FragColor = vec4(0.0, 0.0, 0.0, (1.0-edgeFactor())*0.95);
             } else {
                 gl_FragColor = vec4(0.0, 0.0, 0.0, (1.0-edgeFactor())*0.7);
	  	// gl_FragColor = vec4(1.0, 0.0, 0.0, 0.5);
             }
	 } else {
             if(gl_FrontFacing){
	         gl_FragColor = f_Color;
             } else { 
	         gl_FragColor =  vec4(0.0, 0.0, 0.0, 0.5);
	     }
	 }
    }
</script>

<script type="text/javascript" src="../Common/webgl-utils.js"></script>
<script type="text/javascript" src="../Common/initShaders.js"></script>
<script type="text/javascript" src="../Common/MV.js"></script>
<script type="text/javascript" src="bary.js"></script>
<script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"></script>

<style>
input {
    text-align: right;
}
</style>

</head>

<body>
     <h1>It's a Hollow Feeling</h1>
     This code is based on a discussion <a href="http://codeflow.org/entries/2012/aug/02/easy-wireframe-display-with-barycentric-coordinates/">here</a>.

<script>
    var canvas = new Canvas(500, 500);
    canvas.Redisplay();
</script>

<p>

<button type="button" onclick="canvas.FlipDir(),canvas.Redisplay()">
   Triangle Dir
</button>

<button type="button" onclick="canvas.ChangeShader(),canvas.Redisplay()">
   Change Shader
</button>

 <form >
      <input type="radio" name="face" value="0" checked
             onchange="canvas.ChangeFace(this.value), canvas.Redisplay()">
             BACK 
     </input>
      <input type="radio" name="face" value="1" 
             onchange="canvas.ChangeFace(this.value), canvas.Redisplay()">
             FRONT 
     </input>
      <input type="radio" name="face" value="2" 
             onchange="canvas.ChangeFace(this.value), canvas.Redisplay()">
             FRONT_AND_BACK 
     </input>
      <input type="radio" name="face" value="3" 
             onchange="canvas.ChangeFace(this.value), canvas.Redisplay()">
             none 
     </input>
   </form>
Press x,y,z to rotate.

</body>

The Javascript File

function MakeCanvas(width, height, locID) {

    if (width == undefined || width < 0) {
       width = 300;
    }

    if (height == undefined || height < 0) {
       height = 300;
    }

    var canvas = document.createElement('canvas')
        canvas.tabIndex = 0;
        canvas.height = height;
        canvas.width = width;

    if(locID == undefined) {
        document.body.appendChild(canvas);
    } else {
        div = document.getElementById(locID);
        if (null == div) {
            document.body.appendChild(canvas);
        } else {
            div.appendChild(canvas);
        }
    }

    document.body.appendChild(canvas);
    return canvas;
}

function InitGL(canvas) {
    var gl =  WebGLUtils.setupWebGL(canvas,'OES_standard_derivatives');
    if (!gl) {
        alert ("WebGL isn't available");
    }

    // required to turn on fwidth and such.
    gl.getExtension('OES_standard_derivatives');
    return gl;
}

function Canvas(width, height, locID) {
    var canvas = MakeCanvas(width, height, locID);

    var gl = InitGL(canvas);
    this.gl = gl;

    gl.viewport(0,0, width, height);

    var program = initShaders(gl, "vertex-shader","fragment-shader");
    gl.useProgram(program);

    this.vPos = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, this.vPos);

    var vPos = gl.getAttribLocation(program, "vPosition");
    gl.vertexAttribPointer(vPos,2,gl.FLOAT, false,0,0);
    gl.enableVertexAttribArray(vPos);

    this.vBC = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, this.vBC);

    var vBC = gl.getAttribLocation(program, "vBC");
    gl.vertexAttribPointer(vBC,3,gl.FLOAT, false,0,0);
    gl.enableVertexAttribArray(vBC);

    // a buffer for the colors
    this.cBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, this.cBuffer);

    var colorAttribute = gl.getAttribLocation(program, "vColor");
    gl.vertexAttribPointer(colorAttribute,3,gl.FLOAT, false,0,0);
    gl.enableVertexAttribArray(colorAttribute);

    this.shaderLoc = gl.getUniformLocation(program, "vFillType");
    gl.uniform1f(this.shaderLoc, 0.0);

    this.Init();

    return this;
}

Canvas.prototype = {

    Init: function() {
        this.gl.clearColor(1.0, 1.0, 1.0, 1.0);

	this.gl.enable(this.gl.BLEND);
        this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);

	this.RestartList();

        this.shaderChoice = false;
        this.gl.uniform1f(this.shaderLoc, 0.0);
 
        this.direction = this.gl.CCW;
        this.gl.frontFace(this.direction);

	this.gl.cullFace(this.gl.BACK);
        this.gl.enable(this.gl.CULL_FACE);
    },

    FlipDir: function() {
        if (this.direction == this.gl.CCW) {
	    this.direction = this.gl.CW;
	} else {
	    this.direction = this.gl.CCW;
	}
        this.gl.frontFace(this.direction);
	this.Redisplay();
    },

    RestartList: function() {

        var p1 = vec2(-0.8, -0.8);
        var p2 = vec2(-0.1, -0.1);
        var p3 = vec2(-0.8, -0.1);
       
	this.verts= [p1, p2, p3] ;

        p1 = vec2(0.1, 0.1);
        p3 = vec2(0.8, 0.1);
	p2 = vec2(0.8, 0.8);

        this.verts.push(p1);
        this.verts.push(p2);
        this.verts.push(p3);

	var c1 = vec3(Math.random(), Math.random(),Math.random())
	var c2 = vec3(Math.random(), Math.random(),Math.random())
	var c3 = vec3(Math.random(), Math.random(),Math.random())

	this.colors = [c1, c2, c3];

	c1 = vec3(Math.random(), Math.random(),Math.random())
	c2 = vec3(Math.random(), Math.random(),Math.random())
	c3 = vec3(Math.random(), Math.random(),Math.random())

	this.colors.push(c1);
	this.colors.push(c2);
	this.colors.push(c3);

	this.bc = [[1,0,0],[0,1,0],[0,0,1],[1,0,0],[0,1,0],[0,0,1]];

        this.UpdateBuffers();
	this.Redisplay();
    },

    UpdateBuffers: function() {
        var gl = this.gl;

        // change the vertex data
        gl.bindBuffer(gl.ARRAY_BUFFER, this.vBC);
	gl.bufferData(gl.ARRAY_BUFFER,flatten(this.bc),gl.STATIC_DRAW);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.vPos);
	gl.bufferData(gl.ARRAY_BUFFER,flatten(this.verts),gl.STATIC_DRAW);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.cBuffer);
	gl.bufferData(gl.ARRAY_BUFFER,flatten(this.colors),gl.STATIC_DRAW);
    },

    ChangeShader: function() { 
        this.shaderChoice = ! this.shaderChoice;
	var shader = 0.0;

	if (this.shaderChoice) {
	    shader = 1.0
	}
        this.gl.uniform1f(this.shaderLoc, shader);
	this.Redisplay();
    },

    ChangeFace: function(value) {
       switch(value) {
          default:
          case '0':
              this.gl.enable(this.gl.CULL_FACE);
	      this.gl.cullFace(this.gl.BACK);
	      break;
	  case '1':
              this.gl.enable(this.gl.CULL_FACE);
	      this.gl.cullFace(this.gl.FRONT);
	      break;
	  case '2':
              this.gl.enable(this.gl.CULL_FACE);
	      this.gl.cullFace(this.gl.FRONT_AND_BACK);
	      break;
	  case '3':
              this.gl.disable(this.gl.CULL_FACE);
	      break;
       }
       this.Redisplay();
    },

    Redisplay: function() {
        this.gl.clear(this.gl.COLOR_BUFFER_BIT );

	this.gl.drawArrays(this.gl.TRIANGLES, 0, this.verts.length);
        return;
    }
};