Monday, August 13, 2012

Bret Victor - Inventing on Principle

Every time I watch my two-year-old son driving his iPad I marvel at the elegance, simplicity and intuitiveness of the iOS user interface, and I wonder about those mostly-unknown geniuses who designed it. This weekend I watched a presentation by Bret Victor, who is one of those geniuses. This is an excellent talk on many levels.

 

Bret Victor - Inventing on Principle from CUSEC on Vimeo.

 

And when you are done take a look at Bret’s site for other amazing stuff. Just don’t use Internet Explorer; I suspect Bret does not like IE for some reason.

Tuesday, July 24, 2012

Rule 34

A few years ago I had the opportunity to go and hear one of my literary heroes, William Gibson, give a talk. After the talk I asked him which author he thought was his literary heir apparent. He said Charles Stross without hesitation. The next day I picked up a copy of Halting State, and thus began my love affair with the writings of Mr. Stross.

I recently finished Rule 34, which was published in 2011 and is the second in the Halting State Series. I loved this book. Not only is Stross’ a brilliant storyteller, but he also has his pulse on the current bleeding edge of cultural and technological evolution, and an amazing vision of where that evolution is going to take our species in the near and far future.

I don’t plan to give away the plot, but there are a couple of themes in this book that I will hint at because I have spent a lot of time cogitating about them over the last year.

 

The Rate of Technology Adoption by Law Enforcement

In Rule 34 Stross imagines a near-future where the criminal justice system is driven by sophisticated machine learning and augmented reality software systems. Stross’ technical vision cannot be faulted; all of the applications of technology that he describes are not only entirely feasible, but are mostly already commercially available today in some form or another. However, having worked in the Justice and Public safety domain for the last year, I have to question the rate of cultural evolution in law enforcement that this vision implies.

Despite what we see on television, in shows like CSI, in reality law enforcement agencies are glacially slow at adopting even relatively new technologies. In the United States and Canada today, other than the large federal and state/provincial agencies, most state/provincial and local agencies and police departments do not have fully integrated systems, let alone systems that leverage emerging technologies. The US Federal Government has a number of initiatives to address this, including the GRA and NIEM standards, but adoption of these has been very slow indeed.

In Rule 34 Stross hints at a cataclysmic cyber-attack that motivates a significant acceleration in technology adoption by law enforcement agencies across the planet. The 9/11 attacks failed to motivate this acceleration, so I am not convinced that a cyber-attack would, even if it went so far as to take out national power, telecommunication, and transportation infrastructure.

The current Holy Grail of law enforcement is Predictive Policing (think Minority Report sans the half-naked psychics in the wading pool). Despite the fact that the data storage and real-time analysis technologies needed to implement  predictive policing solutions are available today in off-the-shell products, e.g. Hadoop, Mahout, MarkLogic Server, and even products that they are probably already using, like Microsoft SQL Server; most agencies are now only beginning to look into how they might integrate their existing systems, automate currently paper-based processes, analyse their data using 40-year-old OLAP technologies, and migrate existing 25-year-old mainframe applications. I imagine that it is going to be a while before we see predictive, machine learning-based systems going into production in these agencies, let alone systems that leverage augmented reality user interfaces.

 

Free Will and Criminal Justice

One of the characters in Rule 34 asks a very interesting question; if humans do not have free will then how relevant or useful is a system of justice based on increasingly more complex laws and statutes, and the enforcement of those laws and statutes primarily through the threat of punishment? Recent research in cognitive neuroscience, using functional brain imaging techniques, has revealed that we start acting on a decision, i.e. muscle-actuating nerve impulses are sent, before we consciously become aware that we have made that decision. This implies that most, if not all, of the decision making process happens in the unconscious mind, and that once the decision has been made, the conscious mind is informed of the decision as a courtesy.

This seems to imply that Martin Luther was right[ish] all along; man does not have free will after all. If this is the case then how can we expect that a threat of punishment aimed at the conscious mind will motivate obeyance? And more importantly, how can we justify punishing those that transgress? Are they not merely victims of their own pathology? I don’t claim to have answers to these questions, but I do know that the findings of this research brings into question many of our most deeply held assumptions about our own nature, and the structures that we have put in place to govern that nature at scale.

Update (August 26th 2012): If you are at all interested in this latter topic, I highly recommend that you read “Free Will” by Sam Harris. It represents a thorough analysis of the current state of the demise of our illusions of Free Will. 

There is a lot more to Charles Stross’ Rule 34 than these two themes; it is a feast for the [conscious] mind, so I highly recommend that you pick-up or download a copy and read it.

Wednesday, July 4, 2012

Pure Genius!

Here is a bit of pure genius that I have to share:

http://thecodelesscode.com/contents

This should be mandatory reading for all software developers and architects.

Enjoy!

Friday, January 20, 2012

The Emperor Will Never Have Clothes!

Half a decade ago I did some fairly exhaustive research into software time and cost estimation techniques for Microsoft Services. I evaluated the effectiveness of both formal and informal techniques used within Microsoft, across the software development industry, and even in other industries. I looked at everything from Estimation by Analogy to COCOMO II.

After months of research I discovered that the most commonly used estimation techniques were Estimation by Analogy and Wideband Delphi, often done in combination. However, most of the teams using these techniques were doing so informally, and had never heard of either of these terms.

I also discovered that there were no software estimation techniques that consistently produced accurate estimates, other than for small projects, even those sophisticated parametric estimation techniques like COCOMO II. 

At the time I was doing the research the failure rate for IT projects was around 70%, and many of those failures were due to catastrophic budget and schedule overruns. It quickly became evident to me that the way the industry had historically thought about estimating software projects was deeply broken; The Emperor had no clothes! 

My final recommendation to Microsoft Services was that they adopt Estimation by Analogy and Wideband Delphi, and formally train their staff on the use and limitations of these techniques. My other recommendation was that they also invest in maturing their risk management processes, given the historical inaccuracy of software estimation in general. It was on this latter area that I focused until I left Microsoft.

Our obviously apparent inability to do accurate estimation bugged me for years after I completed this research. This itch that I could not seem to scratch motivated me to do some reading about predication in general, and in particular Quantitative Analysis and its use of Stochastic models; one would imagine that if any industry had worked out how to predict the future it would be the Financial Industry given their huge monetary incentive to do so. Obviously recent events have shown that not to be the case, but this was before all that nastiness.

And then I read “The Black Swan: The Impact of the Highly Improbable” by Nassim Nicholas Taleb, and it all made sense. It is not that we have yet to find an accurate technique to do software estimation; it is that accurate estimation of large, complex software development projects is simply NOT POSSIBLE! (given our current understanding of the laws of physics anyway). Not only does the Emperor not have clothes, but he will remain naked for the foreseeable future.

I suspect that the move to  Agile software development practices is a natural response to our inability to do accurate estimation, but I am continually amazed at how many people in the industry and how many of its customers are still in denial about this, what should now be self-evident, fact. I still encounter many projects where development teams are held to early estimates, which are typically done by non-technical project managers. And this is particularly true on fixed-cost\budget projects.

Surely it is time for the entire industry and its customers to acknowledge that this whole approach is simply broken? The illusion is that fixed-cost\budget projects shift ownership of the risk from the customer to the organization who is developing the software; but given the percentage of IT projects that still fail today, this is obviously just that: an illusion; and a pernicious one at that.

We need to start from the premise that accurate software estimation is currently, and probably forever, impossible, and go from there. Agile practices have made a good start but we obviously need to do more. And most importantly we need to educate our customers!

Here is an interview with Nassim Nicholas Taleb wherein he talks specifically about our inability to estimate IT projects:

Tuesday, December 20, 2011

Why Software Development is like Ironing a Thneed

 I’m being quite useful. This thing is a Thneed.
A Thneed's a Fine-Something-That-All-People-Need!
It's a shirt. It's a sock. It's a glove. It's a hat.
But it has OTHER uses. Yes, far beyond that.
You can use it for carpets. For pillows! For sheets!
Or curtains! Or covers for bicycle seats!
"

                                         – from “The Lorax” by Dr. Seuss

During my time in the military two decades ago I become highly skilled at ironing. And not only shirts and pants, but beds, hats, sheets and other items one would not normally consider “ironable”. I continue to this day to do my own ironing. I actually find it rather therapeutic.

Since having children I have also become reacquainted with the works of Theodor Geisel, more affectionately known as Dr. Seuss. It struck me the other day while ironing a particularly pesky shirt that software development is very much like ironing a Thneed.

So let’s make some assumptions about a Thneed based on the description above. Some poetic licence and imagination will be required.

  1. Nobody is entirely sure what a Thneed is, not even the manufacturer.
  2. When customers buy a Thneed they have only a vague idea of what they need it for and how it is going to make their lives better.
  3. No two Thneed’s are exactly alike; they are the snowflakes of garments.
  4. Thneed producers create new and improved Thneed’s all the time.
  5. A Thneed is too big and awkwardly shaped to fit on your ironing board and Dr. Seuss makes no mention of a Thneed-press.
  6. It is hard, if not impossible, to estimate how long it will take you or a highly trained team of Thneed-ironers to iron a Thneed, with any level of accuracy or confidence in your estimate.
  7. The market for Thneed-ironing accessories is confusing in its profusion, pace and super-competitiveness.
  8. Thneed-ironing methodologies were derived from shirt and pants ironing methodologies, but they are actually poorly suited.
  9. A Thneed is like most other garments in that it occasionally requires ironing.
  10. Thneed manufactures’ own Thneed’s are usually the worst ironed.

So based on the assumptions above, how does one go about ironing this Thneed thing? Well the trick is known by every person who has ever had to iron a shirt.

Manipulate the garment so that a small piece of it is flat on the ironing board and then iron that piece. Then get another piece flat and iron that piece, and so on and so on, until you have ironed the entire garment. If you are foolish enough to try to iron large sections of the garment at the beginning, you will become very frustrated and will probably run out of steam before you are done. You have to “divide and conquer” when it comes to ironing a Thneed; that is the winning strategy.

I don’t think that I need to actually spell out why this is like software development; if you have done any software development in your life you will know that I am right (or mostly so).

Maybe someday I will post “Why Architects are like the Lorax, and Users like the Once-ler”. On second thoughts maybe I won’t.

Happy Ironing!

Wednesday, December 14, 2011

Apache Hadoop on Windows Azure

The brilliant Alexander Stojanovic talking about his latest project, Isotope, which is Microsoft’s Hadoop distribution. Yes, you read that right; Microsoft is going to be packaging up HDFS, MapReduce, Flume, Sqoop, Hive, Pig, Pegasus, Mahout, Lucene and some new technology into a Hadoop distributed for the Windows and Windows Azure platforms.

Update (June 10th, 2012): It looks like Channel9 removed the video for some reason.

Monday, December 12, 2011

Ghost in the Wires

I just finished reading “Ghost in the Wire : My Adventures as the World’s Most Wanted Hacker” by Kevin Mitnick and William L. Simon. It reads like a spy thriller and I literally had a hard time putting it down. The stories of Kevin Mitnick’s social engineering exploits are truly amazing, and regardless of one’s ethical stance on hacking one has to respect his extraordinary audacity.

Given that Mitnick himself admits to being a master of social engineering, i.e. lying and manipulating people, I still cannot say for sure whether or not I believe that all his hacking was motivated by curiosity alone. But I don't think it matters really; I agree with Kevin and his supporters that he was treated rather poorly by the US Justice System.

In hindsight though, I can also totally understand why he was treated as harshly as he was; at the time they simply did not have the necessary understanding to ascertain just how big a threat he was, and so they had to assume the worst, and defer to sources who obviously had issues with him that went well beyond the morality and legality of his hacking activities. I also don’t think it mattered that he had apparently not used any of the access or data he obtained for nefarious purposes; it was simply the fact that he could have caused significant damage and loss if he so chose. It was just pure luck on the part of the targets of Kevin’s hacking that he was not malicious;  and I would hazard a guess that contemporary jurisprudence is not underpinned in any way by luck (though I am no expert in this area, so one never knows).       

This book is well worth the read and I highly recommend it for anyone interested in computer security. It shows that the weakest link in any system is unarguably always the human components, and that without strictly adhered-to policy that no system can be made secure, regardless of the size of the technology investment.

P.S. The book refers to a film that was made by Kevin’s supporters; it is called Freedom Downtime - The Story of Kevin Mitnick. It was obviously made on a shoestring budget, and apparently before hacking became a lucrative new line of business for organized crime world-wide, but it is worth watching. It also happens to be available in its entirety online, and I have embedded it below:

Friday, December 9, 2011

Processing.js Snowflake Fail

I probably should be writing a post about Microsoft’s SOA and BPM platforms, but I need a breather from that particular topic, so instead I am going to write about my recent frustrations with Processing.js. I was hoping to be able to create some flashy new sketches, but unfortunately my recent experiments have uncovered a critical bug in Processing.js that will only be fixed in the 1.5 release. 

My 3 year-old daughter can’t stop talking about snow so I decided to create a little snow generator for her and post it on this blog. I also wanted to experiment with Processing.js’ ability to load SVG files, which can then be used in a sketch. My idea was simple; create an SVG file that contains a number of shapes that can be randomly combined to create snowflake shapes. Then generate a collection of those random shapes and animate them. Not an ambitious project in the least.

My previous experiments with Processing were done with the stable 1.5.1 release of PDE. I thought I would try the latest alpha version of the Processing 2.0 PDE for this experiment, primarily because it has a JavaScript mode, and will export a web page that loads Processing.js and your sketch (and detect the necessary browser capabilities too!). It does not seem to provide an option to embed the sketch script directly in the HTML, so the sketch is always referenced as an external pde file.  It took me a couple of hours to create the SVG file in InkScape, and a sketch in PDE that did exactly what I wanted. While prototyping the sketch I was working in the Standard mode, i.e. it generates a Java applet, since that offers the best development-time performance.

When, after completing the sketch, I changed to the JavaScript mode my sketch failed to pretty much do anything other than draw the background gradient.

The original sketch looks like this when running:

snowfalkes

Processing provides a loadShape method that takes the path or URL to an SVG file, parses the SVG, and generates Processing-native PShape objects. There is currently no way to load SVG elements that are embedded directly in the HTML. Hopefully this will come in a future version of Processing.js. Processing also provides a  getChild method to get shapes nested within the root PShape. PShapes can be drawn directly to the screen or drawn off-screen to a PGraphics object which can then be used at some later time to draw to the screen by calling the image method.

To generate my snowflakes I created an array of PGraphics objects (each with a little wrapper) and then drew random snowflakes to each. I also added some noise and toy physics to make the whole thing a little more realistic. It looked great in PDE.

Note: I initially was sorting the array from smallest to largest and then drawing them in that order, but after comparing the results I could not see a difference and simply omitted the sort. I had to write my own sort function because the sort implementation that is provided in Processing will only sort arrays of int, float and String. 

There was only one small problem; off-screen drawing of PShapes is broken in the current build of Processing.js. I have filed a bug and it looks like this will be fixed for the 1.5 release of Processing.js. So this post obviously does not include the running sketch.

Another Note: I tried using the tint method to modify the apparent brightness of the snowflakes based on their scale each time the snowflake was drawn, rather than explicitly adjusting the stroke color. This KILLED the performance even when it was running in the Standard mode on my quad-core 8GB laptop with hardware-accelerated graphics. Another bug perhaps?

Rather than find a work-around in Processing.js, I will probably try to port this sketch to one of the other JavaScript graphics APIs, like Raphaël for example. And of course I will post the running result and code in some future post on this blog.

Since I cant show the final result, here is my Processing code for the sketch (usual caveats and disclaimers apply):

color bkground = #000080;
color bkground2 = #0000FF;
snowFlakeFactory factory;
snowFlake[] flakes; 

void setup()
{
  size(600, 200);
  smooth();
  frameRate(60);
  factory = new snowFlakeFactory("snowflakes.svg");
  flakes = factory.createFlakes(60);
}

class snowFlakeFactory
{
  color _background;
  PShape _template,
  _spoke, 
  _centerHex, 
  _centerCircle, 
  _star, 
  _longArms, 
  _mediumArms, 
  _shortArms, 
  _endCircle;

  snowFlakeFactory(String templateFileName)
  {  
    _template = loadShape(templateFileName);
    _template.disableStyle();
    _spoke = _template.getChild("spoke");
    _centerHex = _template.getChild("centerHex");
    _centerCircle = _template.getChild("centerCircle");
    _star  = _template.getChild("star");
    _longArms = _template.getChild("longArms");
    _mediumArms = _template.getChild("mediumArms");
    _shortArms = _template.getChild("shortArms");
    _endCircle = _template.getChild("endCircle");
  }

  snowFlake createFlake()
  {
    snowFlake flake = new snowFlake();
    PGraphics graph = createGraphics(450, 450, P2D);
    graph.beginDraw();
    graph.background(0,0);
    graph.smooth();
    float br = 4000 * flake._scale;
    graph.stroke(br, br, 255);
    graph.noFill();
    graph.strokeWeight(13);
    graph.shapeMode(CORNERS);
    radialDraw(graph,_spoke, 225, 225, 0);
    if (heads())
    {
      if (heads())
      {
        graph.shape(_centerHex, 225, 225);
      }
      else
      {
        graph.shape(_centerCircle, 225, 225);
      }
    }
    if (heads()) graph.shape(_star, 225, 225);
    if (heads()) radialDraw(graph,_endCircle, 225, 225, 190 );
    PShape[] arms = {
      _longArms, _mediumArms, _longArms
    };
    if (heads()) radialDraw(graph,arms[(int)random(0, 2)], 225, 225, 100);
    if (heads()) radialDraw(graph,arms[(int)random(0, 2)], 225, 225, 130);
    if (heads()) radialDraw(graph,arms[(int)random(0, 2)], 225, 225, 160);
    graph.endDraw();
    flake._image = graph;
    return flake;
  }

  snowFlake[] createFlakes(int flakeCount)
  {
    snowFlake[] flakes = new snowFlake[flakeCount];
    for (int i = 0; i < flakeCount; i++)
    {
      flakes[i] = createFlake();
    }
    return flakes;
  }

  void radialDraw(PGraphics graph, PShape feature, float originX, float originY, float rad)
  {   
    float xOffset = rad * cos(PI/6);
    float yOffset = rad * sin(PI/6);
    graph.shape(feature, originX, originY + rad);
    feature.rotate(PI/3);
    graph.shape(feature, originX - xOffset, originY + yOffset);
    feature.rotate(PI/3);
    graph.shape(feature, originX - xOffset, originY - yOffset);
    feature.rotate(PI/3);
    graph.shape(feature, originX, originY - rad);
    feature.rotate(PI/3);
    graph.shape(feature, originX + xOffset, originY - yOffset);
    feature.rotate(PI/3);
    graph.shape(feature, originX + xOffset, originY + yOffset);
    feature.rotate(PI/3);
  }
}

class snowFlake
{
  float _posX;
  float _posY;
  float _scale;
  float _rotation;
  PGraphics _image;

   snowFlake()
  {
    _posX = random(width);
    _posY = random(height);
    _scale = heads()? random(0.01,0.03) : random(0.04,0.1);
    _rotation = random(0, PI/6);
    _image = null;
  }

  void drawFlake()
  {
    pushMatrix();
      translate(_posX, _posY);
      scale(_scale);
      rotate(_rotation);
      image(_image, -225, -225);
    popMatrix();
  }
}

void draw()
{
  drawBackroundGradient(bkground,bkground2);
  for(int i = 0; i < flakes.length; i++)
  {
    snowFlake flake = flakes[i];
    float gravity = flake._scale * (10 + random(0,5));
    float wind = flake._scale * (5 + random(-2,2));
    flake._posY += gravity; 
    flake._posX += wind; 
    flake._rotation += 0.01;
    flakes[i].drawFlake();
    if(flake._posY &rt; height + 20) flake._posY = -20;
    if(flake._posX &rt; width + 20) flake._posX = -20;
  }
  
}

void drawBackroundGradient(color c1, color c2)
{
  noFill();
  for (int i = 0; i <= height; i++) {
    float inter = map(i, 0, height, 0, 1);
    color c = lerpColor(c1, c2, inter);
    stroke(c);
    line(0, i, width, i);
  }
}

float _prob = 0.75;
boolean heads()
{
  float rand = random(0, 1);
  return (rand < _prob);
}

And here is the SVG:

<svg id="snowflakeTemplate" xmlns="http://www.w3.org/2000/svg" height="1000" width="700" version="1.1">
<g id="mainLayer" stroke="#000" stroke-miterlimit="4" stroke-dasharray="none" fill="none">
<path id="spoke" stroke-linejoin="round" d="M0-0,0,190" stroke-linecap="round" stroke-width="10"/>
<path id="centerHex" stroke-linejoin="miter" d="m-36.48-23.229,0,44.146,36.184,20.891,36.775-21.232,0-41.606-35.987-20.777z" stroke-linecap="butt" stroke-width="10"/>
<path id="star" stroke-linejoin="round" d="M53.858-31.273,49.838-86.48-0.015-62.659l-50.032-24.126-4.3663,55.188-45.979,31.37,45.814,31.287,4.3801,55.416,49.918-23.819,50.097,24.128,4.4815-55.112,46.094-31.294z" stroke-linecap="round" stroke-width="10"/>
<path id="longArms" stroke-linejoin="round" d="M49.142,11.646,0-11.646-49.142,11.559" stroke-linecap="round" stroke-width="10"/>
<path id="mediumArms" stroke-linejoin="round" d="M28.782,6.4858,0-6.4858-28.782,6.3986" stroke-linecap="round" stroke-width="10"/>
<path id="shortArms" stroke-linejoin="round" d="M-12.226,2.7043,0-2.7915,12.226,2.7915" stroke-linecap="round" stroke-width="10"/>
<circle id="endCircle" cx="0" cy="0" r="10" stroke-width="10"/>
<circle id="centerCircle" cx="0" cy="0" r="36.5" stroke-width="10"/>
<rect id="diamond" stroke-linejoin="round" transform="rotate(45)" height="8" width="8" stroke-linecap="round" y="-4" x="-4" stroke-width="10"/>
</g>
</svg>

Friday, November 18, 2011

Simple L-system Processing.js Code

Here as promised is the code from my previous post:

<script src="http://processingjs.org/content/download/processing-js-1.3.6/processing-1.3.6.min.js" type="text/javascript"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/modernizr/2.0.6/modernizr.min.js" type="text/javascript"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.7/jquery.min.js" type="text/javascript"></script>
<div id="blogpostcontent"><!-- Blog Text -->
</div>
<div id="nocanvas" style="display: none; color: Red;"><!-- Fail Text -->
</div>
<div id="procanvas"><canvas id="processingCanvas"></canvas></div>
<div><!-- More Blog Text -->
</div>
<script type="text/javascript">
var words = $("#blogpostcontent").text().replace(/[\.,-\/#!$%\^&\*;:{}=\-_`~()]/g, "").replace(/\s{2,}/g, "").split(' ');
if (!Modernizr.canvas) { $('#nocanvas').show(); $('#procanvas').remove(); }
</script>
<script type="text/processing" data-processing-target="processingCanvas">

Tree tree;
int wordCount = 0;

class Stack {
ArrayList aList;

Stack() {
aList = new ArrayList(1024);
}

Stack(int initialSize) {
aList = new ArrayList(initialSize);
}

boolean isEmpty() {
if (aList.size() > 0) return false;
return true;
}

void push(Object obj) {
aList.add(obj);
}

Object pop() {
int n = aList.size();
if (n > 0) return aList.remove(n - 1);
return null;
}
}

class Branch {

float x = 0;
float y = 0;
float theta = 0;
float thickness = 0;

Branch() {
};

Branch (Branch branch) {
x = branch.x;
y = branch.y;
theta = branch.theta;
thickness = branch.thickness;
}
}

class Tree {

String axiom, currentString;
String productionRule;
Branch branch;
Stack branchStack;
int pos = 0;
color col;
float angle = PI/10;
float angleChaos = 1;
float initialThickness = 18;
float thickness = initialThickness;

Tree () {
axiom = "F";
currentString = axiom;
branch = new Branch();
productionRule = "F[+FF-F-F][-FF+F-F]";
branchStack = new Stack();
}

void grow() {
grow(1);
}

void grow(int generations) {
for (int i = 0; i < generations; i++) {
String nextString = "";
for (int j = 0; j < currentString.length(); j++) {
char c = currentString.charAt(j);
if (c == "F") {
nextString += productionRule;
}
else {
nextString += c;
}
}
currentString = nextString;
}
}

void draw() {
for (int i = 0; i < currentString.length(); i++) {
this.drawBranch();
}
}

void drawBranch () {
if (pos >= currentString.length()) return;
char c = currentString.charAt(pos);
switch (c) {
case "F":
fill(100 + random(-40, 40), 42 + random(-40, 40), 42);
String word = (wordCount < words.length - 1) ? words[wordCount++] : "noword";
branch.thickness = thickness;
textFont(createFont("Helvetica", branch.thickness));
pushMatrix();
translate(branch.x, branch.y);
rotate(branch.theta);
text(word, 0, 0);
popMatrix();
float extension = textWidth(word);
branch.x += extension * cos(branch.theta);
branch.y += extension * sin(branch.theta);
break;
case "-":
branch.theta -= (angle + random(-1.0 * angle * angleChaos, angle * angleChaos));
break;
case "+":
branch.theta += (angle + random(-1.0 * angle * angleChaos, angle * angleChaos));
break;
case "[":
branchStack.push(new Branch(branch));
if (thickness > 3) {
thickness -= 3;
}
else {
thickness = 1;
}
break;
case "]":
branch = (Branch)branchStack.pop();
thickness = branch.thickness;
break;
}
pos++;
}
}

void setup () {
size(500, 500);
background(#141414);
frameRate(60);
smooth();
tree = new Tree();
tree.grow(3);
}

void draw () {
translate(width/2, height);
rotate(1.5 * PI);
tree.drawBranch();
}

</script>

The L-system code is based on this Processing code written by Daniel Jones.