As a pet-project, I wanted to render Pascal's pyramid dynamically. To do this, I have used three.js to deal with WebGL, and plain old javascript for everything non-webGL.

I have found that youtube has no video showing off a 3D visualization of Pascal's triangle so I uploaded this to youtube as well, in case you feel like checking that out!

### The pyramid

Pascal's pyramid is the 3-simplex variant of pascal's Triangle. Whilst a lot of people are familiar with the 2-simplex, the 3-simplex is a bit less common. So what is the 3 simplex?

Simply put, it's the result of aligning the coefficients of trinomial expansion and the trinomial distribution in three dimensions. Technically, doing this will result in a tetrahedron and not a pyramid, since a pyramid has five faces and the generation will only create four. (1 base and three sides).

To generate a layer of the pyramid, we take the equation (A+B+C) and raise it to the power of the layer we want to generate. For example, suppose we want to generate the fourth layer, we would raise (A+B+C)^4. The trinomial is expanded by repeatedly multiplying the trinomial with itself, resulting in (A+B+C)^1 x (A+B+C)^n) == (A+B+C)^(n+1). This is the parallel of pascal's triangle in that we would use a binomial to generate a two-dimensional representation of the coeffecients of binomial expansion. (a+b)² = a² + 2ab + b² = 1a² + 2xy + 1y².

A nice thing to notice however, is that the trinomial is not the "upper-limit" of simplex'. In fact, we can generalize Pascal's Triangle into as much dimensions as we want based on the multinomial theorem. This results in us being able to generate an n-simplex, however we hit the limits of visualization at three dimensions. Mathematically however, we can go as far as we want in this.

#### Relation of triangle to pyramid

There is a deep relationship between Pascal's Triangle and Pascal's Pyramid. It is most easily expressed with this equation: PT(i,j) * PT(n,i) = PP(n,i,j). Where PT is Pascal's Triangle, PP is Pascal's pyramid, i is a row, j is a column and n is a level. Because of this, we don't have to code the trinomial expansion, which would be computationally speaking expensive. We can abuse the relatively easy-to-generate Pascal's Triangle and reach our Pyramid using multiplication. It states that the "nth" layer of the pyramid is generated by multiplying the numbers of each line of PT down to N by the numbers of row N.

To generate a level in Javascript I wrote

function generatePascalPyramidLevel(level) { // First we need the row of pascal's triangle at this level. var trianglerow = generatePascalTriangleRow(level); console.log(trianglerow); var pyramidlevel = []; for(var i = 0; i <= level; i++) // i is a row in this case. { var levelrow = []; var multiplicationRow = generatePascalTriangleRow(i); if(i == 0) { levelrow = [1]; }else { for(var k = 0; k < multiplicationRow[0].length; k++) { levelrow[k] = multiplicationRow[0][k] * trianglerow[0][i]; } } pyramidlevel[i] = levelrow; } return pyramidlevel; }

It's a neat little bit of code that solves much of the computational complexity by using our triangle.

### The three.js part!

Three.js is a 3D Library that makes WebGL simpler. If you're like me and you're not fond of dealing with graphics programming, it's well worth checking out due to it's ease-of-use (at least, assuming you know javascript and perhaps a bit of general 3D doesn't hurt, but there's plenty of documentation).

#### Displaying the triangle

First we set up our scene and our renderer. I just render it on an HTML page which has only a little bit of extra DOM elements. Namely an input text field and a 'display' button, so a user can input the amount of levels he wants to generate. After our setup, we want to get our data from the function shown above.

var scene = new THREE.Scene(); var renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight-125); document.getElementById("renderframe").appendChild(renderer.domElement); camera.position.z = 5; // end setup var input = document.getElementById("rows").value; console.log(input); getPascalPyramid(input);

Now we have our scene setup, we need to render something. After some looking around the documentation and various resources I found that using ShapeGeometry to display characters rather than TextGeometry would be suitable for this project. Another thing we need to keep in mind is that the "position" of the elements is different for each element. But it can be taught of in the following way. For each layer, for each row we start at the same position. We change the position for each column we print. After a column is printed, we begin printing at our original position on the YZ axis, but change the X-axis. After the level (Row x Columns) is done, we adjust the Y position. (Go one lower to print the next level lower).

var xoffset = 0; var yoffset = 2; var zoffset = 0; var colour = 0xffff0000; for(var level = 0; level < pascalpyramid.length; level++) { zoffset = 0; for(var row = 0; row < pascalpyramid[level].length;row++) { xoffset = 0; for(var col = 0; col < pascalpyramid[level][row].length; col++) { var currentNumber = pascalpyramid[level][row][col]; shapes = THREE.FontUtils.generateShapes(Math.round(currentNumber), { font: "helvetiker", weight: "normal", size: 0.25 }); xoffset += 1; geom = new THREE.ShapeGeometry(shapes); mat = new THREE.MeshBasicMaterial({color:colour}); mat.side = THREE.DoubleSide; mesh = new THREE.Mesh(geom, mat); mesh.position.set(xoffset, yoffset, zoffset); scene.add(mesh); } // Adjust the Z-axis for every column (so we display columns along Z-axis) zoffset -= 0.5; } yoffset -= 1;

And that's it, I added some more code to generate each layer in repeating blue-red colouring but that's trivial going from this point. I also implemented some camera functions but those are not specific to this project, though the code is included on github!

You can play around with it yourself here.

Sean McGee quickly whipped up an interested way of visualizing the layers. He did this in Photoshop but by the time you check out the code or website, it might already be implemented in my project. Though, it might not, so here's a picture!

The full code is available on github.