My implementation still isn't perfect, as it biases points in the middle over points around the edges. (A consequence of the fact that a bunch of points evenly distributed around a small radius are closer together than a bunch of points evenly distributed around a large radius.) But it's definitely better than the one in the article.
This means there's nothing special about 3 - the more times you add uniforms together, the more normal it becomes. Also it doesn't matter if the uniform isn't uniform.
I changed it to
var angle = (Math.PI * 2 * Math.random());