Fast Lines:

The HTML file


<head>

<script type="text/javascript" src="dragon.js"></script>
<script type="text/javascript" src="LineTests.js"></script>
<script type="text/javascript" src="Canvas.js"></script>

</head>

<body>
<script>
   var width = 500;
   var height = 500;
   var lastvalue = 0;

   canvas = new Canvas(width,height);

function DoDisplay(value) {

   if (value == undefined) {
      value = lastvalue;
   }

   lastvalue = value;

   canvas.Clear();
   switch (value) {
      case '0':
         LineTest(canvas); 
	 break;
      case '1':
         DrawIt(canvas);
	 break;
      case '2':
         Circle(canvas);
	 break;
      case '3':
         Dragon(canvas);
	 break;
      default:
   }

   canvas.Redisplay();
   return;
}

   canvas.Clear();
   DoDisplay('0');
   canvas.Redisplay();



</script>
<p>
<select onchange="DoDisplay(this.value)">
    <option value="0">Line Test</option>
    <option value="1">Data Test</option>
    <option value="2">Circle</option>
    <option value="3">Dragon</option>
</select>

Fast Lines:
<select onchange="canvas.SetFastLines(this.value); DoDisplay()">
    <option value="1">On</option>
    <option value="0">Off
</select>

</body>

Canvas.js

function Canvas(width, height, locID) {

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

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

    var canvas = document.createElement('canvas')
        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);

    this.width = width;
    this.height = height;
    this.clearColor = {"R":0, "G": 0, "B": 0}
    this.ctx =  canvas.getContext("2d");

    this.fastLines = true;

    this.frameBuffer = new Array(this.width);
    for(var x = 0; x < this.width; x++) {
       this.frameBuffer[x] = new Array(this.height);
       for(var y=0; y < this.height; y++) {
           this.frameBuffer[x][y] = {"R": this.clearColor.R,
	            "G": this.clearColor.G, "B": this.clearColor.B};
       }
    }

    return this;
}

Canvas.prototype = {

    SetClearColor: function(r,g,b) {
        this.clearColor.R = r; 
        this.clearColor.G = g; 
        this.clearColor.B = b; 
        return;
    },

    Width: function() {
       return this.width;
    },

    Height: function() {
       return this.height;
    },

    SetFastLines: function(value) {
       this.fastLines = (value == '1');
       console.log("fastLines is now ", this.fastLines);
    },

    Clear: function() {
        for(var x = 0; x < this.width; x++) {
            for(var y=0; y < this.height; y++) {
                this.frameBuffer[x][y].R = this.clearColor.R;
                this.frameBuffer[x][y].G = this.clearColor.G;
                this.frameBuffer[x][y].B = this.clearColor.B;
            }
	}
        return;
    },

    Redisplay: function() {
        for(var x = 0; x < this.width; x++) {
           for(var y=0; y < this.height; y++) {
	       color = "rgb(" + this.frameBuffer[x][y].R
	                      + ", " + this.frameBuffer[x][y].G
	                      + ", " + this.frameBuffer[x][y].B +")";
	       this.ctx.fillStyle = color;
	       this.ctx.fillRect(x,y,1,1);
           }
        }	
        return;
    },

    SetPointReal: function(x,y,r,g,b,p) {
           var R, G, B;

	   R = Clamp(0,255,r);
	   G = Clamp(0,255,g);
	   B = Clamp(0,255,b);

	   this.frameBuffer[x][y].R = Math.round(R*p);
	   this.frameBuffer[x][y].G = Math.round(G*p);
	   this.frameBuffer[x][y].B = Math.round(B*p);
    },

    SetPoint: function(x,y,r,g,b,p) {
       if(p == undefined) {
          p = 1;
       }

       if (x >= 0 && x < this.width && y > 0 && y <= this.height) {
	   y = this.height-y;
           this.SetPointReal(x,y,r,g,b,p);
       } else {
          console.log("Set Point Error, ", x, y, r, g, b,p);
       }
       return;
    },

    Point: function(x,y, r,g,b) {
        this.SetPoint(x,y,r,g,b);
    },

    Line: function(x1, y1, r1, g1, b1, x2, y2, r2, g2, b2) {
       if (this.fastLines) {
          this.BruteLine(x1, y1, r1, g1, b1, x2, y2, r2, g2, b2);
       } else {
          this.WuLine(x1, y1, r1, g1, b1, x2, y2, r2, g2, b2);
       }
    },

    // adopted from wikipedia  Sept 16
    WuLine: function(x1, y1, r1, g1, b1, x2, y2, r2, g2, b2) {
        // functions for WU
	function ipart(x) {
	   return Math.floor(x);
	}

        // fractional part.
	function fpart(x) {
	    if (x < 0) {
	        return 1-(x-Math.floor(x)); 
	    } else {
	        return x-Math.floor(x);
	    }
	};

        // reverse of the fractional part
        function rfpart(x) {
	    return 1-fpart(x);
	};

        // determine the axis of major change.
	var steep = (Math.abs(y2-y1) > Math.abs(x2-x1));

        // cover special cases.
	// horizontal line
	if (x1 == x2) {
	   if (y1 > y2) {
	      tmp = y1; y1= y2; y2 = tmp;
	   }
	   for(var y = y1; y <= y2; y++) {
                this.SetPoint(x1, y, r1, g1, b1);
	   }
	   return;
	}

        // and an anti horizontal line.
	if (y1 == y2) {
	   if (x1 > x2) {
	      tmp = x1; x1 = x2; x2 = tmp;
	   }
	   for(var x = x1; x <= x2; x++) {
                this.SetPoint(x, y1, r1, g1, b1);
	   }
	   return;
	}

        // x will be the axis of change, less code but more confusing.  
	// Don't like it, will change if I have time.
	// swap x and y
	if (steep) {
	    tmp = x1; x1 = y1; y1 = tmp;
	    tmp = x2; x2 = y2; y2 = tmp;
	}
        // make sure that x1 is the smaller so we can have a positive  
	// for loop.
	// swap the two points.
	if (x1 > x2) {
	    tmp = x1; x1 = x2; x2 = tmp;
	    tmp = y1; y1 = y2; y2 = tmp;
	}

	var dx = x2 - x1;
	var dy = y2 - y1;

	// note this is the major change, but it might be m or 1/m
	var gradient = dy/dx;

	// handle the endpoints
	var xEnd = Math.round(x1);
	var yEnd = y1 + gradient * (xEnd - x1);
	var xGap = rfpart(x1+0.5);
	xPixel1  = xEnd;
	yPixel1 = ipart(yEnd);

	if (steep) {
	   this.SetPoint(yPixel1, xPixel1,r1,g1,b1, rfpart(yEnd) *xGap);
	   this.SetPoint(yPixel1+1, xPixel1, r1, g1,b1, fpart(yEnd)*xGap);
	} else {
	   this.SetPoint(xPixel1, yPixel1,r1,g1,b1,rfpart(yEnd)*xGap);
	   this.SetPoint(xPixel1, yPixel1+1,r1, g1,b1,fpart(yEnd)*xGap);
	}

        // this is the first y intersection
	var y = yEnd + gradient;

        xEnd = Math.round(x2);
	yEnd = y2 + gradient * (xEnd - x2);
	xGap = fpart(x2+0.5);

	var xPixel2 = xEnd;
	var yPixel2 = ipart(yEnd);

	if (steep) {
	   this.SetPoint(yPixel2, xPixel2,r1,g1,b1,rfpart(yEnd)*xGap);
	   this.SetPoint(yPixel2+1, xPixel2,r1, g1,b1,fpart(yEnd)*xGap);
	} else {
	   this.SetPoint(xPixel2, yPixel2,r1,g1,b1,rfpart(yEnd)*xGap);
	   this.SetPoint(xPixel2, yPixel2+1,r1, g1,b1,fpart(yEnd)*xGap);
	}

	var x;

	if (steep) {
	   for (x = xPixel1 +1; x < xPixel2; x++) {
	       this.SetPoint(ipart(y), x,r1,g1,b1,rfpart(y));
	       this.SetPoint(ipart(y)+1, x,r1,g1,b1,fpart(y));
	       y = y + gradient;
	   }
	} else {
	   for (x = xPixel1 +1; x < xPixel2; x++) {
	       this.SetPoint(x, ipart(y), r1,g1,b1,rfpart(y));
	       this.SetPoint(x, ipart(y)+1, r1,g1,b1,fpart(y));
	       y = y + gradient;
	   }
	}
	return;
    },

    BruteLine: function(x1, y1, r1, g1, b1, x2, y2, r2, g2, b2) {
        var dx = x2-x1;
	var dy = y2-y1;
	var m = dy/dx; // may be NAN but will not be used.

	var x,y,dir;

	if (dx == 0 && dy == 0)  {
	    // attempt to draw a degenerate line (a point)
	    this.SetPoint(x1, y1, r1, g1,b1)
	} else if (dx == 0) {
	    // no change in x, so it is a vertical line
	    var sy = Math.min(y1,y2);
	    var ey = Math.max(y1, y2);
	    for(y=sy; y <= ey; y++) {
	       this.SetPoint(x1,y,r1,g1,b1);
	    }
	} else if (dy == 0) {
	    // no change in y so it is a horizontal line.
	    var sx = Math.min(x1,x2);
	    var ex = Math.max(x1, x2);
	    for(x=sx; x <= ex; x++) {
	       this.SetPoint(x,y1,r1,g1,b1);
	    }
	} else if (Math.abs(dx) > Math.abs(dy) ) {
	    if (dx < 0) {
	       dir  = -1;
	    } else {
	       dir = 1;
	    }
	    for(x=x1; x!= x2; x+= dir) {
	       y = Math.round(y1+m*(x-x1));
	       this.SetPoint(x,y,r1,g1,b1);
	    }
	} else {
	    if (dy < 0) {
	        dir = -1;
	    } else {
	        dir = 1;
	    }
	    for(y=y1; y != y2; y+= dir) {
	        x = Math.round(x1 + 1/m*(y-y1));
	        this.SetPoint(x,y,r1,g1,b1);
	    }
	}
        return;
    },
};

function Clamp(min, max, v) {
   return Math.max(min, Math.min(v,max));
}

LineTests.js


var X=0,
    Y = 1,
    R = 2,
    G = 3, 
    B = 4;

data = [
         [ "line", [ 10,10,255,0,0], [20,20,0,0,255], [30,30,0,255,0]],
	 [ "polygon", [100,100,0,255,255], [110,100,0,255,255], 
	              [110,110,0,255,255], [100,110,0,255,255]],
       ];



function DrawPolyLine(canvas, ary) {
    for(var p =1; p < ary.length-1; p++) {
        canvas.Line(
	  ary[p][X],ary[p][Y], ary[p][R],ary[p][G],ary[p][B], 
	  ary[p+1][X],ary[p+1][Y], ary[p+1][R],ary[p+1][G],ary[p+1][B]) ;
    }
    return;
}

function DrawPolygon(canvas, ary) {
    DrawPolyLine(canvas, ary);

        var a = 1, b = ary.length-1;
        canvas.Line(
	  ary[b][X],ary[b][Y], ary[b][R],ary[b][G],ary[b][B], 
	  ary[a][X],ary[a][Y], ary[a][R],ary[a][G],ary[a][B]) ;
    return;
}

function DrawIt(canvas) {
    var shape,point;

    for(shape=0; shape < data.length; shape ++) {
       console.log("drawing a ", data[shape][0]);
       if (data[shape][0] == "line") {
           DrawPolyLine(canvas, data[shape]);
       } else if (data[shape][0] == "polygon")  {
           DrawPolygon(canvas, data[shape]);
       }
    }
    return;
}

function Dragon(canvas) {
    DrawPolyLine(canvas, dragon[0]);
    return;
}

function  Circle(canvas) {
   var t;
   var x1,y1;
   var x2,y2;
   var r = Math.min(canvas.Width()/2, canvas.Height()/2)*.9;
   var xc, yc;
   var step = 30;

   xc = Math.round(canvas.Width()/2);
   yc = Math.round(canvas.Height()/2);

   t = 0;

   for (var R = 20; R <= r ; R+= 10) {
   x1 = xc + Math.round(R * Math.cos(t*Math.PI/180));
   y1 = yc + Math.round(R * Math.sin(t*Math.PI/180));
   for(t=step;t<360; t+= step) {
       x2 = xc + Math.round(R * Math.cos(t*Math.PI/180));
       y2 = yc + Math.round(R * Math.sin(t*Math.PI/180));
       canvas.Line(x1, y1, 255,255,255, x2, y2 , 255, 0,0);
       x1 = x2;
       y1 = y2;
   }

   t = 360;
   x2 = xc + Math.round(R * Math.cos(t*Math.PI/180));
   y2 = yc + Math.round(R * Math.sin(t*Math.PI/180));
   canvas.Line(x1, y1, 255,0,0, x2, y2 , 255, 0,0);
   }

   canvas.Point(xc, yc, 255,0,0);
}

function LineTest(canvas) {

   var xm = Math.round(canvas.Width() *.1);
   var ym = Math.round(canvas.Height() *.1);
   var xx = Math.round(canvas.Width() * .9);
   var yx = Math.round(canvas.Height() * .9);

   // draw a horizontal line left to right.
   canvas.Line(xm,ym, 255,255,255, xx,ym , 0, 0, 0 );
   // draw a horizontal line right to left
   canvas.Line(xx,yx, 255,255,255, xm, yx, 0, 0, 0 );

   // draw a vertical line top to bottom
   canvas.Line (xm, yx, 255, 255, 255, xm, ym, 0,0,0);
   // draw a vertical line bottom to top.
   canvas.Line (xx, ym, 255, 255, 255, xx, yx, 0,0,0);

   // four diagionals.
   canvas.Line(xm,ym,255,255,255, xx,yx,0,0,0);
   canvas.Line(xx,yx,255,255,255, xm,ym,0,0,0);

   canvas.Line(xx,ym,255,255,255, xm,yx,0,0,0);
   canvas.Line(xm,yx,255,255,255, xx,ym,0,0,0);

   var xc = Math.round(canvas.Width()/2);
   var yc = Math.round(canvas.Height()/2);;
   var r = Math.min(xc, yc)*.7;
   var x,y;

   var t;
   for(t=0;t<360;t+=10) {
       x = xc + Math.round(r*Math.cos(t*Math.PI/180));
       y = yc + Math.round(r*Math.sin(t*Math.PI/180));
       canvas.Line(xc, yc,255,0,0, x,y,155,42,0);
   }
}


dragon.js is simply data.