
Row-Reducing in the Sun: A Summer Linear Algebra Deep-Dive
Table of contents
Timeline: | June - July 2015 | |
Languages Used: | JavaScript (Node.js) | |
School: | Colorado College | |
Course: | MA220: Linear Algebra |
Summer at Colorado College often meant one of two things: soaking up the Colorado sun, or diving headfirst into a “wild card” Block Plan course. For me, in the summer of 2015, it was both. Every student was granted a “wild card” to take one summer course tuition-free, and I wasn’t going to pass up the opportunity.

Given a function f
, where f
is spaghetti, you can use the spaghetti to connect two potatoes. Did I get that right?
While not the choice everyone would make, among all the courses offered that summer, I opted to use my wild card to take MA220: Linear Algebra, a subject that had always felt like the elegant, abstract backbone of so much in computer science, but which wasn’t one of the CS degree requirements for a reason that still eludes me. Even so, this was perhaps my only opportunity to take it, since my Spring and Fall semesters were packed with required courses for my double major in Theatre and CS.
The Block Plan, if you’re not familiar, is intense. You take one course for three and a half weeks, full-time. It’s like a mental sprint, and you emerge either exhausted but enlightened, or just plain exhausted. For this particular course, our professor, the mathematical biologist Dr. David Brown, had a knack for assigning problems that pushed us beyond the textbook.

My answer to Dr. Brown’s exam question: What is the terminal velocity of a falling raindrop?
One lesson, in particular, stands out: matrix reduction, also known as Gaussian elimination. After learning about this thought-provoking topic, driven purely by curiosity and having no exposure to existing algorithms, I spent an afternoon attempting to implement a matrix reduction algorithm in code.
Now, for a math class, you might expect MATLAB or Python with NumPy. But for some reason, I decided to tackle this challenge using Node.js and plain old JavaScript. Why? Probably a desire for familiarity, and a touch of “let’s see if I can make this work” stubbornness. Building a command-line tool to interactively reduce matrices felt like a proper challenge, blending theoretical math with practical programming.
When Math Met the Command Line
The first hurdle wasn’t the math itself, but how to get the matrix into my program. This was a command-line utility, so no fancy UI. I opted for interactive input via process.stdin
. It meant users would type rows of numbers, hitting enter after each, and then signal “done” when finished.
This required a bit of Node.js magic to listen for data and parse it.
1// From reducematrix.js
2function init()
3{
4 console.log("Enter rows of matrix like so:");
5 console.log("x1 x2 x3");
6 console.log("x4 x5 x6");
7 process.stdin.resume();
8 process.stdin.setEncoding('utf8');
9
10 process.stdin.on('data', handleInput);
11}
12
13function handleInput(text) {
14 if (text === 'done\n') {
15 doneInputting();
16 } else {
17 var values = text.split(" ");
18 if(matrixWidth === 0) {
19 matrixWidth = values.length;
20 } else {
21 if(values.length !== matrixWidth) {
22 console.log('Incorrect number of rows. Data was not inputted. Try again.');
23 return;
24 }
25 }
26 var matrixRow = [];
27 for(var i=0; i<values.length; i++) {
28 var val = parseInt(values[i]);
29 matrixRow.push(val);
30 }
31 matrix.push(matrixRow);
32 }
33}
34
35function doneInputting() {
36 printMatrix();
37 if(matrix.length <= 1) {
38 setDefaultMatrix();
39 }
40 reduceMatrix();
41 console.log('Now that process.stdin is paused, there is nothing more to do.');
42 process.exit();
43}
This setup meant the program would patiently wait for input, validate that each row had the correct number of columns, and then store it in a global matrix
array. The “done” command would then trigger the main event: the matrix reduction.
The Core Logic: A Rudimentary Row Reduction
The heart of the project was reduceMatrix()
.
💡 The Main Idea
The goal of matrix reduction (or Gaussian elimination) is to transform a matrix into an equivalent one that’s easier to work with, typically row echelon form. This involves a series of elementary row operations: swapping rows, multiplying a row by a non-zero scalar, and adding a multiple of one row to another.
My implementation, looking back, was a rather rudimentary first pass, focusing primarily on making the first element of subsequent rows zero relative to the first row’s first element. It wasn’t a full-blown, robust Gaussian elimination for any matrix, but it was my attempt to grapple with the underlying arithmetic.
1// From reducematrix.js
2function oldReduceMatrix() {
3 if(matrix.length <= 0) {
4 console.log("Error.");
5 return;
6 }
7 var firstRow = matrix[0];
8 var currentRowIndex = matrix.length-1;
9 var currentRow = matrix[currentRowIndex]; // Start with last row.
10 while(currentRow[0] !== 0) {
11 // To make 3 => 4, multiply 3 * (4/3)
12 var additive;
13 for(var i=0; i<firstRow.length; i++) {
14 if(i === 0) {
15 // This logic was a struggle point; the intent was to find a scalar to zero out currentRow[0]
16 // but the implementation here is a bit... unconventional.
17 // It roughly simplifies to +/- currentRow[i], which is not standard Gaussian elimination.
18 // It reflects my early grappling with the math concepts in code!
19 var positiveOne = (currentRow[i] < 0 && firstRow[i]) < 0 || (currentRow[i] > 0 && firstRow[i] > 0);
20 additive = (positiveOne ? 1 : -1) * (firstRow[i] * (currentRow[i] / firstRow[i]));
21 }
22 currentRow[i] += additive;
23 }
24 if(currentRowIndex > 0) {
25 currentRow = matrix[currentRowIndex--];
26 } else {
27 // This is where a more robust algorithm would handle pivots and move to the next column.
28 // For this project, the focus was on getting *something* working for the first element.
29 break; // Exit if we've processed all rows relative to the first.
30 }
31 printMatrix(); // See the matrix evolve!
32 }
33}
As you can see, the additive
calculation was a particular point of struggle and exploration for me. My goal was to find a factor to apply to the firstRow
to eliminate the currentRow[0]
element, but the JavaScript implementation ended up being a nascent, somewhat unconventional attempt. It’s a great example of how translating abstract mathematical operations into concrete code can reveal where your understanding needs shoring up!
The printMatrix()
function was absolutely essential for debugging. Seeing the matrix evolve after each (sometimes incorrect) operation was crucial for understanding what was happening.
1// From reducematrix.js
2function printMatrix() {
3 console.log("\nPrinting matrix...");
4 if(matrix.length <= 0) {
5 console.log("No matrix.");
6 return;
7 }
8 for(var i=0; i<matrix.length; i++) {
9 var row = "";
10 for(var j=0; j<matrix[i].length; j++) {
11 row += matrix[i][j].toString() + " "
12 }
13 console.log(row);
14 }
15 console.log("Done printing.\n");
16}
Reflections: Learning by Doing (and Debugging)
This Linear Algebra project, coded in JavaScript, was a fantastic learning experience. It wasn’t just about understanding the theoretical steps of matrix reduction; it was about the gritty details of implementing those steps, handling input, managing state, and seeing the immediate (and sometimes frustrating) results of my code.
It solidified my understanding of how fundamental mathematical concepts underpin so much of computer science, and how even a seemingly simple operation can become complex when you’re building it from first principles. The Block Plan definitely intensified the process of learning in any subject, but this hands-on coding challenge made the abstract concepts of linear algebra feel tangible.
If you’re ever struggling with a math concept, I highly recommend trying to code it yourself. Even if your first attempt isn’t perfect, the process of translating those ideas into a working program will teach you more than any textbook ever could.
Source Code
Feel free to browse the full source code below. Be kind to my 2015-era JavaScript! It’s a snapshot of a student grappling with new concepts and a new language environment.
reducematrix.js
1// var fs = require("fs");
2// var util = require("util");
3
4/* Multidimensional Array storing all columns and rows of colors in the table. */
5var matrix = [];
6var matrixWidth = 0;
7var DEFAULT_MATRIX_WIDTH = 4;
8var DEFAULT_MATRIX_HEIGHT = 4;
9
10init();
11
12/* Initialize matrix. */
13function init() {
14 console.log("Enter rows of matrix like so:");
15 console.log("x1 x2 x3");
16 console.log("x4 x5 x6");
17 process.stdin.resume();
18 process.stdin.setEncoding("utf8");
19
20 process.stdin.on("data", handleInput);
21}
22
23function handleInput(text) {
24 if (text === "done\n") {
25 doneInputting();
26 } else {
27 var values = text.split(" ");
28 if (matrixWidth === 0) {
29 matrixWidth = values.length;
30 } else {
31 if (values.length !== matrixWidth) {
32 console.log(
33 "Incorrect number of rows. Data was not inputted. Try again."
34 );
35 return;
36 }
37 }
38 var matrixRow = [];
39 for (var i = 0; i < values.length; i++) {
40 var val = parseInt(values[i]);
41 matrixRow.push(val);
42 }
43 matrix.push(matrixRow);
44 }
45}
46
47function doneInputting() {
48 printMatrix();
49 if (matrix.length <= 1) {
50 setDefaultMatrix();
51 }
52 reduceMatrix();
53 console.log("Now that process.stdin is paused, there is nothing more to do.");
54 process.exit();
55}
56
57function reduceMatrix() {
58 if (matrix.length <= 0) {
59 console.log("Error.");
60 return;
61 }
62 var firstRow = matrix[0];
63 var currentRowIndex = matrix.length - 1;
64 var currentRow = matrix[currentRowIndex]; // Start with last row.
65 var stepCounter = 0;
66
67 while (currentRowIndex > 0) {
68 if (firstRow[0] === 0) {
69 console.log("Cannot reduce: The first element of the first row is zero.");
70 return;
71 }
72
73 // Only perform a step if the first element is not already zero
74 if (currentRow[0] !== 0) {
75 stepCounter++;
76 const factor = -currentRow[0] / firstRow[0];
77
78 for (var i = 0; i < firstRow.length; i++) {
79 currentRow[i] += factor * firstRow[i];
80 if (Math.abs(currentRow[i]) < 1e-10) {
81 currentRow[i] = 0; // Clean up floating point errors
82 }
83 }
84
85 printMatrix();
86 }
87 currentRowIndex--;
88 }
89
90 if (stepCounter === 0) {
91 console.log("No reduction needed.");
92 } else {
93 printMatrix();
94 }
95}
96
97function oldReduceMatrix() {
98 if (matrix.length <= 0) {
99 console.log("Error.");
100 return;
101 }
102 var firstRow = matrix[0];
103 var currentRowIndex = matrix.length - 1;
104 var currentRow = matrix[currentRowIndex]; // Start with last row.
105
106 while (currentRow[0] !== 0) {
107 // To make 3 => 4, multiply 3 * (4/3)
108 var additive;
109 for (var i = 0; i < firstRow.length; i++) {
110 if (i === 0) {
111 var positiveOne =
112 (currentRow[i] < 0 && firstRow[i]) < 0 ||
113 (currentRow[i] > 0 && firstRow[i] > 0);
114 additive =
115 (positiveOne ? 1 : -1) *
116 (firstRow[i] * (currentRow[i] / firstRow[i]));
117 }
118 currentRow[i] += additive;
119 }
120 if (currentRowIndex > 0) {
121 currentRow = matrix[currentRowIndex--];
122 }
123 printMatrix();
124 }
125}
126
127function printMatrix() {
128 console.log("\nPrinting matrix...");
129 if (matrix.length <= 0) {
130 console.log("No matrix.");
131 return;
132 }
133 for (var i = 0; i < matrix.length; i++) {
134 var row = "";
135 for (var j = 0; j < matrix[i].length; j++) {
136 row += matrix[i][j].toString() + " ";
137 }
138 console.log(row);
139 }
140 console.log("Done printing.\n");
141}
142
143function setDefaultMatrix() {
144 matrix = [];
145 for (var i = 0; i < DEFAULT_MATRIX_HEIGHT; i++) {
146 matrix[i] = [];
147 for (var j = 0; j < DEFAULT_MATRIX_WIDTH; j++) {
148 matrix[i][j] = 3;
149 }
150 }
151}
Interactive Matrix Reducer
You may also enjoy this simple UI built around the same algorithm as my rudimentary CLI tool.
⚠️ Disclaimer
This is a simplified, incomplete version of Gaussian elimination. It only reduces the first column of each row relative to the first row. This is based on my quick attempt to understand matrix reduction in an afternoon.
Enter each row on a new line. Separate numbers with spaces. Example:
2 1 -1 8
-3 -1 2 -11
-2 1 2 -3
Update: Full Gaussian Elimination
gaussianelim.js
1/* Multidimensional Array storing all columns and rows of colors in the table. */
2var matrix = [];
3var matrixWidth = 0;
4
5/* The core algorithm. Transforms the matrix into row echelon form. */
6function gaussianElimination() {
7 if (!matrix || matrix.length === 0) {
8 console.log("Matrix is empty.");
9 return;
10 }
11
12 var rows = matrix.length;
13 var cols = matrix[0].length;
14 var pivotRow = 0; // Tracks the current row used to eliminate others
15
16 // Loop through each column as the pivot column
17 for (var pivotCol = 0; pivotCol < cols && pivotRow < rows; pivotCol++) {
18 // Partial pivoting: find the best pivot.
19 // Find the row (at or below current pivotRow) with the largest absolute value in the pivot column.
20 var maxRowIndex = pivotRow;
21 for (var i = pivotRow + 1; i < rows; i++) {
22 if (
23 Math.abs(matrix[i][pivotCol]) > Math.abs(matrix[maxRowIndex][pivotCol])
24 ) {
25 maxRowIndex = i;
26 }
27 }
28
29 // If the best pivot in this column is 0, can't use it. Skip to next column.
30 if (matrix[maxRowIndex][pivotCol] === 0) {
31 console.log(
32 "\nColumn " + (pivotCol + 1) + " has no pivot. Moving to next column."
33 );
34 continue;
35 }
36
37 // Swap the current pivot row with the row found to have the best pivot.
38 if (maxRowIndex !== pivotRow) {
39 console.log(
40 "\nSwapping R" +
41 pivotRow +
42 " and R" +
43 maxRowIndex +
44 " to get a better pivot."
45 );
46 var temp = matrix[pivotRow];
47 matrix[pivotRow] = matrix[maxRowIndex];
48 matrix[maxRowIndex] = temp;
49 printMatrix();
50 }
51
52 // Elimination step: For every row *below* the current pivot row...
53 for (var i = pivotRow + 1; i < rows; i++) {
54 // Calculate the factor needed to create a zero in the pivot column.
55 const factor = matrix[i][pivotCol] / matrix[pivotRow][pivotCol];
56
57 // Only perform the operation if a factor exists (i.e., the element isn't already 0)
58 if (factor !== 0) {
59 console.log(
60 "\nPerforming R" +
61 i +
62 " = R" +
63 i +
64 " - (" +
65 factor +
66 ") * R" +
67 pivotRow
68 );
69 // ...apply the row operation to every element in that row.
70 for (var k = pivotCol; k < cols; k++) {
71 matrix[i][k] -= factor * matrix[pivotRow][k];
72 }
73 printMatrix();
74 }
75 }
76 pivotRow++; // Move to the next row to be our pivot
77 }
78 console.log("\nGaussian Elimination Complete");
79}
80
81function handleInput(text) {
82 if (text.trim() === "" || text.trim() === "done") {
83 doneInputting();
84 return;
85 }
86
87 var values = text.split(" ");
88 if (matrixWidth === 0) {
89 matrixWidth = values.length;
90 } else {
91 if (values.length !== matrixWidth) {
92 console.log(
93 "Incorrect number of rows. Data was not inputted. Try again."
94 );
95 return;
96 }
97 }
98 var matrixRow = [];
99 for (var i = 0; i < values.length; i++) {
100 var val = parseInt(values[i]);
101 matrixRow.push(val);
102 }
103 matrix.push(matrixRow);
104}
105
106function doneInputting() {
107 printMatrix();
108 if (matrix.length > 1) {
109 reduceMatrix();
110 } else {
111 console.log("Matrix is too small to reduce. Using the default matrix.");
112 matrix = [
113 [2, 1, -1, 8],
114 [-3, -1, 2, -11],
115 [-2, 1, 2, -3],
116 ];
117 }
118
119 console.log("Initial Matrix:");
120 printMatrix();
121 gaussianElimination();
122 console.log("Final Matrix (Row Echelon Form):");
123 printMatrix();
124
125 process.exit();
126}
127
128/* Prints the current state of the matrix. */
129function printMatrix() {
130 if (matrix.length <= 0) {
131 console.log("No matrix.");
132 return;
133 }
134 for (var i = 0; i < matrix.length; i++) {
135 var row = "";
136 for (var j = 0; j < matrix[i].length; j++) {
137 row += matrix[i][j].toFixed(2).padStart(8, " ") + " ";
138 }
139 console.log(row);
140 }
141}
142
143console.log("Enter rows of matrix like so:");
144console.log("x1 x2 x3");
145console.log("x4 x5 x6");
146process.stdin.resume();
147process.stdin.setEncoding("utf8");
148
149process.stdin.on("data", handleInput);
💡 Update (2025)
Revisiting this post years later, I couldn’t resist the urge to finish the implementation and demonstrate what full Gaussian elimination can do. See the CLI source code and the interactive version below.
Enter each row on a new line. Separate numbers with spaces.
2 1 -1 8
-3 -1 2 -11
-2 1 2 -3
Who knew linear algebra could be so… interactive? Let me know in the comments if you’ve ever tackled a math concept by coding it!